laxy-verify 0.1.2 → 1.0.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 +150 -163
- package/dist/badge.d.ts +1 -0
- package/dist/badge.js +14 -0
- package/dist/build.d.ts +11 -0
- package/dist/build.js +67 -0
- package/dist/cli.js +283 -93
- package/dist/comment.d.ts +20 -0
- package/dist/comment.js +89 -0
- package/dist/config.d.ts +30 -12
- package/dist/config.js +148 -47
- package/dist/detect.d.ts +10 -0
- package/dist/detect.js +133 -0
- package/dist/github.d.ts +8 -0
- package/dist/github.js +14 -0
- package/dist/grade.d.ts +37 -0
- package/dist/grade.js +64 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +121 -0
- package/dist/lighthouse.d.ts +7 -0
- package/dist/lighthouse.js +130 -0
- package/dist/serve.d.ts +12 -0
- package/dist/serve.js +126 -0
- package/dist/status.d.ts +6 -0
- package/dist/status.js +36 -0
- package/package.json +29 -44
- package/action.yml +0 -97
- package/dist/build-runner.d.ts +0 -22
- package/dist/build-runner.js +0 -156
- package/dist/build-runner.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/dev-server.d.ts +0 -8
- package/dist/dev-server.js +0 -81
- package/dist/dev-server.js.map +0 -1
- package/dist/lighthouse-runner.d.ts +0 -10
- package/dist/lighthouse-runner.js +0 -119
- package/dist/lighthouse-runner.js.map +0 -1
- package/dist/project-runtime.d.ts +0 -32
- package/dist/project-runtime.js +0 -99
- package/dist/project-runtime.js.map +0 -1
- package/dist/reporter.d.ts +0 -21
- package/dist/reporter.js +0 -100
- package/dist/reporter.js.map +0 -1
- package/dist/verification.d.ts +0 -42
- package/dist/verification.js +0 -105
- package/dist/verification.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,163 +1,150 @@
|
|
|
1
|
-
# laxy-verify
|
|
2
|
-
|
|
3
|
-
Frontend quality gate for
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
npx laxy-verify
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
Automatically detects and runs the right build command:
|
|
153
|
-
|
|
154
|
-
- **Next.js** — `next build`
|
|
155
|
-
- **Vite** — `vite build`
|
|
156
|
-
- **Create React App** — `react-scripts build`
|
|
157
|
-
- **Custom** — reads `scripts.build` from `package.json`
|
|
158
|
-
|
|
159
|
-
---
|
|
160
|
-
|
|
161
|
-
## License
|
|
162
|
-
|
|
163
|
-
MIT
|
|
1
|
+
# laxy-verify
|
|
2
|
+
|
|
3
|
+
Frontend quality gate for AI-generated code. Build + Lighthouse verification with grade output (Gold/Silver/Bronze/Unverified).
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx laxy-verify --init # Auto-detect framework, generate config
|
|
7
|
+
npx laxy-verify . # Run verification
|
|
8
|
+
npx laxy-verify --badge # Show shields.io badge
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### 1. Initialize
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd your-project
|
|
17
|
+
npx laxy-verify --init
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This generates `.laxy.yml` and `.github/workflows/laxy-verify.yml` automatically by detecting your framework and package manager.
|
|
21
|
+
|
|
22
|
+
### 2. Run Locally
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx laxy-verify .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 3. Add to CI
|
|
29
|
+
|
|
30
|
+
Push the generated workflow file. Every PR gets a quality gate with grade comment and status check.
|
|
31
|
+
|
|
32
|
+
## Grades
|
|
33
|
+
|
|
34
|
+
| Grade | Requirement |
|
|
35
|
+
|-------|------------|
|
|
36
|
+
| **Silver** | Build passes + Lighthouse exceeds all thresholds |
|
|
37
|
+
| **Bronze** | Build passes (Lighthouse not run or below threshold) |
|
|
38
|
+
| **Unverified** | Build failed |
|
|
39
|
+
|
|
40
|
+
`fail_on` controls the minimum acceptable grade. Default: `bronze`.
|
|
41
|
+
|
|
42
|
+
## Configuration
|
|
43
|
+
|
|
44
|
+
All fields optional in `.laxy.yml`:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
framework: "auto" # auto | nextjs | vite | cra | sveltekit
|
|
48
|
+
build_command: "" # default: auto-detected from package.json
|
|
49
|
+
dev_command: "" # default: auto-detected
|
|
50
|
+
package_manager: "auto" # auto | npm | pnpm | yarn | bun
|
|
51
|
+
port: 3000 # dev server port
|
|
52
|
+
build_timeout: 300 # seconds (default 5m)
|
|
53
|
+
dev_timeout: 60 # seconds for dev server start (90 in CI mode)
|
|
54
|
+
lighthouse_runs: 1 # @lhci/cli runs (CI mode auto-sets to 3)
|
|
55
|
+
|
|
56
|
+
thresholds:
|
|
57
|
+
performance: 70 # CI mode applies -10 offset (effective: 60)
|
|
58
|
+
accessibility: 85
|
|
59
|
+
seo: 80
|
|
60
|
+
best_practices: 80
|
|
61
|
+
|
|
62
|
+
fail_on: "bronze" # unverified | bronze | silver | gold
|
|
63
|
+
# unverified = never fail (informational only)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**fail_on vs build failure:** Build failure always produces grade `Unverified` and exit code 1, regardless of `fail_on`. `fail_on: unverified` means informational only (always exit 0).
|
|
67
|
+
|
|
68
|
+
**--ci flag:** Lowers Performance threshold by 10, sets `lighthouse_runs=3` (when not explicitly set), and increases `dev_timeout` to 90s. Auto-set when `CI=true` env var exists.
|
|
69
|
+
|
|
70
|
+
## CLI Options
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
npx laxy-verify [project-dir] Default: current directory
|
|
74
|
+
|
|
75
|
+
Options:
|
|
76
|
+
--format console | json Output format (default: console)
|
|
77
|
+
--ci CI mode: -10 Performance, runs=3
|
|
78
|
+
--config <path> Path to .laxy.yml
|
|
79
|
+
--fail-on unverified|bronze|silver|gold Override fail_on
|
|
80
|
+
--skip-lighthouse Build-only verification (max Bronze)
|
|
81
|
+
--badge Show shields.io badge (reads .laxy-result.json)
|
|
82
|
+
--init Generate .laxy.yml + GitHub workflow
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## exit codes
|
|
86
|
+
|
|
87
|
+
| Code | Meaning |
|
|
88
|
+
|------|--------|
|
|
89
|
+
| 0 | Grade meets or exceeds `fail_on` threshold |
|
|
90
|
+
| 1 | Grade worse than `fail_on`, or build failed |
|
|
91
|
+
| 2 | Configuration error (no package.json, invalid YAML, etc.) |
|
|
92
|
+
|
|
93
|
+
## GitHub Action
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
- uses: psungmin24/laxy-verify@v1
|
|
97
|
+
with:
|
|
98
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
99
|
+
fail-on: silver # optional, default: bronze
|
|
100
|
+
config: .laxy.yml # optional
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Posts a PR comment with grade and Lighthouse scores, and sets a commit status check.
|
|
104
|
+
|
|
105
|
+
## Limitations (v1)
|
|
106
|
+
|
|
107
|
+
- **Monorepos:** Not supported. Run `npx laxy-verify apps/web` for the app subdirectory.
|
|
108
|
+
- **Lighthouse accuracy:** Scores are measured in dev mode (`npm run dev`). Production scores are typically higher.
|
|
109
|
+
- **Fork PRs:** Comments and status checks are not posted on PRs from forks (GitHub security restriction).
|
|
110
|
+
- **Yarn Berry:** May require manual `package_manager` configuration if using Corepack.
|
|
111
|
+
|
|
112
|
+
## Expected CI Timing
|
|
113
|
+
|
|
114
|
+
- Build: 15-30s
|
|
115
|
+
- Dev server start: 5-20s
|
|
116
|
+
- Lighthouse (1 run): ~15s
|
|
117
|
+
- Lighthouse (3 runs CI): ~45s
|
|
118
|
+
- **Total: 35-95s per PR**
|
|
119
|
+
|
|
120
|
+
## .laxy-result.json
|
|
121
|
+
|
|
122
|
+
Written after every run:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"grade": "Silver",
|
|
127
|
+
"timestamp": "2026-04-07T10:30:00Z",
|
|
128
|
+
"build": { "success": true, "durationMs": 12300, "errors": [] },
|
|
129
|
+
"lighthouse": { "performance": 87, "accessibility": 92, "seo": 88, "bestPractices": 90, "runs": 1 },
|
|
130
|
+
"thresholds": { "performance": 70, "accessibility": 85, "seo": 80, "bestPractices": 80 },
|
|
131
|
+
"ciMode": false,
|
|
132
|
+
"framework": "nextjs",
|
|
133
|
+
"exitCode": 0,
|
|
134
|
+
"config_fail_on": "bronze"
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Use `npx laxy-verify --badge` to output a shields.io badge markdown from this file.
|
|
139
|
+
|
|
140
|
+
## Badge
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npx laxy-verify --badge
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Output: ``
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
package/dist/badge.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function generateBadge(grade: string): string;
|
package/dist/badge.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateBadge = generateBadge;
|
|
4
|
+
function generateBadge(grade) {
|
|
5
|
+
const gradeLower = grade.toLowerCase();
|
|
6
|
+
const colors = {
|
|
7
|
+
gold: "yellow",
|
|
8
|
+
silver: "brightgreen",
|
|
9
|
+
bronze: "blue",
|
|
10
|
+
unverified: "lightgrey",
|
|
11
|
+
};
|
|
12
|
+
const color = colors[gradeLower] ?? "lightgrey";
|
|
13
|
+
return ``;
|
|
14
|
+
}
|
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface BuildResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
durationMs: number;
|
|
4
|
+
errors: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare class BuildError extends Error {
|
|
7
|
+
errors: string[];
|
|
8
|
+
timedOut: boolean;
|
|
9
|
+
constructor(message: string, errors: string[], timedOut?: boolean);
|
|
10
|
+
}
|
|
11
|
+
export declare function runBuild(command: string, timeoutSec: number): Promise<BuildResult>;
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BuildError = void 0;
|
|
7
|
+
exports.runBuild = runBuild;
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
10
|
+
class BuildError extends Error {
|
|
11
|
+
errors;
|
|
12
|
+
timedOut;
|
|
13
|
+
constructor(message, errors, timedOut = false) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.errors = errors;
|
|
16
|
+
this.timedOut = timedOut;
|
|
17
|
+
this.name = "BuildError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.BuildError = BuildError;
|
|
21
|
+
function runBuild(command, timeoutSec) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const startTime = Date.now();
|
|
24
|
+
const stderrLines = [];
|
|
25
|
+
const errorLines = [];
|
|
26
|
+
console.log(`\n Building: ${command}`);
|
|
27
|
+
const proc = (0, node_child_process_1.spawn)(command, { shell: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
28
|
+
let timedOut = false;
|
|
29
|
+
const timer = setTimeout(() => {
|
|
30
|
+
timedOut = true;
|
|
31
|
+
if (proc.pid) {
|
|
32
|
+
(0, tree_kill_1.default)(proc.pid, "SIGKILL");
|
|
33
|
+
}
|
|
34
|
+
reject(new BuildError(`Build timed out after ${timeoutSec}s`, errorLines, true));
|
|
35
|
+
}, timeoutSec * 1000);
|
|
36
|
+
proc.stdout?.on("data", (chunk) => {
|
|
37
|
+
// Print build output to console
|
|
38
|
+
const lines = chunk.toString().split("\n").filter(Boolean);
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
console.log(` ${line}`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
proc.stderr?.on("data", (chunk) => {
|
|
44
|
+
const text = chunk.toString();
|
|
45
|
+
const lines = text.split("\n").filter(Boolean);
|
|
46
|
+
stderrLines.push(...lines);
|
|
47
|
+
errorLines.push(...lines);
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
console.error(` ${line}`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
proc.on("exit", (code) => {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
const durationMs = Date.now() - startTime;
|
|
55
|
+
const success = code === 0;
|
|
56
|
+
resolve({
|
|
57
|
+
success,
|
|
58
|
+
durationMs,
|
|
59
|
+
errors: success ? [] : errorLines,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
proc.on("error", (err) => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
reject(new BuildError(`Build process failed: ${err.message}`, errorLines));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|