laxy-verify 1.1.12 → 1.1.14

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
@@ -3,6 +3,11 @@
3
3
  CLI verification for frontend apps.
4
4
 
5
5
  `laxy-verify` runs production build checks, Lighthouse, tiered verify E2E, and plan-gated verification features for Free, Pro, and Pro+ accounts.
6
+ It is designed around three user questions:
7
+
8
+ - Free: "Is this likely to break right now?"
9
+ - Pro: "Is this strong enough to send to a client?"
10
+ - Pro+: "Can I call this release-ready with confidence?"
6
11
 
7
12
  ```bash
8
13
  npx laxy-verify --init --run
@@ -71,6 +76,9 @@ npx laxy-verify logout
71
76
  | Visual diff | No | No | Yes |
72
77
  | Failure analysis signals | No | No | Yes |
73
78
 
79
+ Pro is for delivery verification.
80
+ Pro+ is for release-confidence verification with extra evidence before you say "ship it."
81
+
74
82
  For CI, set `LAXY_TOKEN` instead of using interactive login.
75
83
 
76
84
  ```yaml
@@ -129,13 +137,25 @@ Each run writes `.laxy-result.json`.
129
137
 
130
138
  ```json
131
139
  {
132
- "grade": "Silver",
140
+ "grade": "Gold",
133
141
  "timestamp": "2026-04-09T09:00:00Z",
134
142
  "build": { "success": true, "durationMs": 12000, "errors": [] },
135
- "e2e": { "passed": 4, "failed": 0, "total": 4, "results": [] },
143
+ "e2e": { "passed": 5, "failed": 0, "total": 5, "results": [] },
136
144
  "lighthouse": { "performance": 82, "accessibility": 94, "seo": 90, "bestPractices": 92, "runs": 3 },
145
+ "multiViewport": {
146
+ "allPassed": true,
147
+ "summary": "Desktop, tablet, and mobile checks passed."
148
+ },
149
+ "visualDiff": {
150
+ "verdict": "pass",
151
+ "differencePercentage": 0
152
+ },
153
+ "verification": {
154
+ "tier": "pro_plus",
155
+ "report": { "verdict": "release-ready" }
156
+ },
137
157
  "exitCode": 0,
138
- "_plan": "pro"
158
+ "_plan": "pro_plus"
139
159
  }
140
160
  ```
141
161
 
@@ -144,6 +164,7 @@ Each run writes `.laxy-result.json`.
144
164
  - Monorepos require targeting the app subdirectory explicitly.
145
165
  - Dev-server-based Lighthouse can differ from production hosting.
146
166
  - Pro+ visual diff and viewport checks increase runtime.
167
+ - Local verification is most stable on current LTS Node releases.
147
168
 
148
169
  ## License
149
170
 
package/dist/build.js CHANGED
@@ -25,7 +25,7 @@ function runBuild(command, timeoutSec, cwd) {
25
25
  const errorLines = [];
26
26
  console.log(`\n Building: ${command}${cwd ? ` (cwd: ${cwd})` : ""}`);
27
27
  const proc = process.platform === "win32"
28
- ? (0, node_child_process_1.spawn)(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
28
+ ? (0, node_child_process_1.spawn)(process.env.ComSpec || "cmd.exe", ["/d", "/c", command], {
29
29
  stdio: ["ignore", "pipe", "pipe"],
30
30
  cwd,
31
31
  })
package/dist/e2e.js CHANGED
@@ -70,9 +70,9 @@ function getFillTarget(selectors) {
70
70
  }
71
71
  function getFeedbackTarget(selectors) {
72
72
  return pickSelector(selectors, [
73
- /^\[role=['"]alert['"]\]$/,
74
73
  /^\[role=['"]status['"]\]$/,
75
74
  /^\[aria-live=['"](polite|assertive)['"]\]$/,
75
+ /^\[role=['"]alert['"]\]$/,
76
76
  /^\[data-testid.*(error|success|toast|alert|notice|result)/,
77
77
  /^\.(error|alert|toast|notice|success|status)/,
78
78
  ]);
package/dist/serve.js CHANGED
@@ -75,7 +75,7 @@ async function startDevServer(command, port, timeoutSec, cwd) {
75
75
  return new Promise((resolve, reject) => {
76
76
  console.log(`Starting dev server: ${command}${cwd ? ` (cwd: ${cwd})` : ""}`);
77
77
  const proc = process.platform === "win32"
78
- ? (0, node_child_process_1.spawn)(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
78
+ ? (0, node_child_process_1.spawn)(process.env.ComSpec || "cmd.exe", ["/d", "/c", command], {
79
79
  stdio: ["ignore", "pipe", "pipe"],
80
80
  env: { ...process.env, PORT: String(port) },
81
81
  cwd,
@@ -41,7 +41,6 @@ const fs = __importStar(require("node:fs"));
41
41
  const path = __importStar(require("node:path"));
42
42
  const puppeteer_1 = __importDefault(require("puppeteer"));
43
43
  const pngjs_1 = require("pngjs");
44
- const pixelmatch_1 = __importDefault(require("pixelmatch"));
45
44
  function ensureDir(dir) {
46
45
  fs.mkdirSync(dir, { recursive: true });
47
46
  }
@@ -58,7 +57,12 @@ async function captureScreenshot(url, outputPath) {
58
57
  await browser.close();
59
58
  }
60
59
  }
61
- function compareImages(baselinePath, currentPath, diffOutputPath) {
60
+ async function loadPixelmatch() {
61
+ const dynamicImport = new Function("specifier", "return import(specifier)");
62
+ const mod = await dynamicImport("pixelmatch");
63
+ return mod.default;
64
+ }
65
+ async function compareImages(baselinePath, currentPath, diffOutputPath) {
62
66
  const baselinePng = pngjs_1.PNG.sync.read(fs.readFileSync(baselinePath));
63
67
  const currentPng = pngjs_1.PNG.sync.read(fs.readFileSync(currentPath));
64
68
  const width = Math.min(baselinePng.width, currentPng.width);
@@ -75,7 +79,8 @@ function compareImages(baselinePath, currentPath, diffOutputPath) {
75
79
  const baseData = cropData(baselinePng, width, height);
76
80
  const currData = cropData(currentPng, width, height);
77
81
  const diff = new pngjs_1.PNG({ width, height });
78
- const diffPixels = (0, pixelmatch_1.default)(baseData, currData, diff.data, width, height, { threshold: 0.1 });
82
+ const pixelmatch = await loadPixelmatch();
83
+ const diffPixels = pixelmatch(baseData, currData, diff.data, width, height, { threshold: 0.1 });
79
84
  ensureDir(path.dirname(diffOutputPath));
80
85
  fs.writeFileSync(diffOutputPath, pngjs_1.PNG.sync.write(diff));
81
86
  const totalPixels = width * height;
@@ -102,7 +107,7 @@ async function runVisualDiff(projectDir, url, label = "current") {
102
107
  diffPath: "",
103
108
  };
104
109
  }
105
- const comparison = compareImages(baselinePath, currentPath, diffPath);
110
+ const comparison = await compareImages(baselinePath, currentPath, diffPath);
106
111
  let verdict = "pass";
107
112
  if (comparison.diffPercentage >= 60) {
108
113
  verdict = "rollback";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "laxy-verify",
3
- "version": "1.1.12",
4
- "description": "Frontend quality gate: build + Lighthouse verification",
3
+ "version": "1.1.14",
4
+ "description": "Frontend quality gate: build, Lighthouse, tiered E2E, and release-confidence verification",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
7
7
  "bin": {