laxy-verify 1.1.1 → 1.1.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 +212 -0
- package/dist/auth.js +19 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,9 +5,221 @@ Frontend quality gate for AI-generated code. Build + Lighthouse verification wit
|
|
|
5
5
|
```bash
|
|
6
6
|
npx laxy-verify --init # Auto-detect framework, generate config
|
|
7
7
|
npx laxy-verify . # Run verification
|
|
8
|
+
npx laxy-verify login # Log in for Pro/Pro+ features
|
|
8
9
|
npx laxy-verify --badge # Show shields.io badge
|
|
9
10
|
```
|
|
10
11
|
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### 1. Initialize
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cd your-project
|
|
18
|
+
npx laxy-verify --init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This generates `.laxy.yml` and `.github/workflows/laxy-verify.yml` automatically by detecting your framework and package manager.
|
|
22
|
+
|
|
23
|
+
### 2. Run Locally
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx laxy-verify .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 3. Add to CI
|
|
30
|
+
|
|
31
|
+
Push the generated workflow file. Every PR gets a quality gate with grade comment and status check.
|
|
32
|
+
|
|
33
|
+
## Grades
|
|
34
|
+
|
|
35
|
+
| Grade | Requirement |
|
|
36
|
+
|-------|------------|
|
|
37
|
+
| **Gold** | Build passes + all Lighthouse thresholds pass + all viewports pass (Pro+ only) |
|
|
38
|
+
| **Silver** | Build passes + Lighthouse exceeds all thresholds |
|
|
39
|
+
| **Bronze** | Build passes (Lighthouse not run or below threshold) |
|
|
40
|
+
| **Unverified** | Build failed |
|
|
41
|
+
|
|
42
|
+
`fail_on` controls the minimum acceptable grade. Default: `bronze`.
|
|
43
|
+
|
|
44
|
+
> **Gold grade** is only achievable with a Pro+ account. It requires multi-viewport Lighthouse (desktop/tablet/mobile) to all pass in addition to the standard thresholds.
|
|
45
|
+
|
|
46
|
+
## Pro / Pro+ Features
|
|
47
|
+
|
|
48
|
+
Log in to unlock paid plan features:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx laxy-verify login # Log in with your laxy-blue.vercel.app account
|
|
52
|
+
npx laxy-verify whoami # Check current login status
|
|
53
|
+
npx laxy-verify logout # Remove saved credentials
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Feature | Free | Pro | Pro+ |
|
|
57
|
+
|---------|------|-----|------|
|
|
58
|
+
| Build verification | ✅ | ✅ | ✅ |
|
|
59
|
+
| Lighthouse (1 run) | ✅ | ✅ | ✅ |
|
|
60
|
+
| Silver grade | ✅ | ✅ | ✅ |
|
|
61
|
+
| Bronze grade | ✅ | ✅ | ✅ |
|
|
62
|
+
| Lighthouse (3 runs, averaged) | ❌ | ✅ | ✅ |
|
|
63
|
+
| Gold grade eligibility | ❌ | ✅ | ✅ |
|
|
64
|
+
| Verbose failure output | ❌ | ✅ | ✅ |
|
|
65
|
+
| Multi-viewport Lighthouse (desktop/tablet/mobile) | ❌ | ❌ | ✅ |
|
|
66
|
+
| Failure analysis report | ❌ | ❌ | ✅ |
|
|
67
|
+
| Fast Lane (priority queue) | ❌ | ❌ | ✅ |
|
|
68
|
+
|
|
69
|
+
### Login
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx laxy-verify login
|
|
73
|
+
# Enter your email and password for laxy-blue.vercel.app
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Credentials are stored in `~/.laxy/credentials.json` (chmod 600). Token expires after 30 days.
|
|
77
|
+
|
|
78
|
+
For CI environments, set the `LAXY_TOKEN` environment variable instead of logging in interactively:
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
env:
|
|
82
|
+
LAXY_TOKEN: ${{ secrets.LAXY_TOKEN }}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Multi-viewport (Pro+)
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx laxy-verify --multi-viewport .
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Runs Lighthouse on three viewports: desktop, tablet (1024×768), and mobile. Gold grade requires all three to pass the configured thresholds.
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
All fields optional in `.laxy.yml`:
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
framework: "auto" # auto | nextjs | vite | cra | sveltekit
|
|
99
|
+
build_command: "" # default: auto-detected from package.json
|
|
100
|
+
dev_command: "" # default: auto-detected
|
|
101
|
+
package_manager: "auto" # auto | npm | pnpm | yarn | bun
|
|
102
|
+
port: 3000 # dev server port
|
|
103
|
+
build_timeout: 300 # seconds (default 5m)
|
|
104
|
+
dev_timeout: 60 # seconds for dev server start (90 in CI mode)
|
|
105
|
+
lighthouse_runs: 1 # @lhci/cli runs (CI mode auto-sets to 3)
|
|
106
|
+
|
|
107
|
+
thresholds:
|
|
108
|
+
performance: 70 # CI mode applies -10 offset (effective: 60)
|
|
109
|
+
accessibility: 85
|
|
110
|
+
seo: 80
|
|
111
|
+
best_practices: 80
|
|
112
|
+
|
|
113
|
+
fail_on: "bronze" # unverified | bronze | silver | gold
|
|
114
|
+
# unverified = never fail (informational only)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**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).
|
|
118
|
+
|
|
119
|
+
**--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.
|
|
120
|
+
|
|
121
|
+
## CLI Options
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
npx laxy-verify [project-dir] Default: current directory
|
|
125
|
+
|
|
126
|
+
Options:
|
|
127
|
+
--format console | json Output format (default: console)
|
|
128
|
+
--ci CI mode: -10 Performance, runs=3
|
|
129
|
+
--config <path> Path to .laxy.yml
|
|
130
|
+
--fail-on unverified|bronze|silver|gold Override fail_on
|
|
131
|
+
--skip-lighthouse Build-only verification (max Bronze)
|
|
132
|
+
--badge Show shields.io badge (reads .laxy-result.json)
|
|
133
|
+
--init Generate .laxy.yml + GitHub workflow
|
|
134
|
+
--multi-viewport Pro+: run Lighthouse on desktop/tablet/mobile
|
|
135
|
+
|
|
136
|
+
Subcommands:
|
|
137
|
+
login [email] Log in to unlock Pro/Pro+ features
|
|
138
|
+
logout Remove saved credentials
|
|
139
|
+
whoami Show current login status
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## exit codes
|
|
143
|
+
|
|
144
|
+
| Code | Meaning |
|
|
145
|
+
|------|--------|
|
|
146
|
+
| 0 | Grade meets or exceeds `fail_on` threshold |
|
|
147
|
+
| 1 | Grade worse than `fail_on`, or build failed |
|
|
148
|
+
| 2 | Configuration error (no package.json, invalid YAML, etc.) |
|
|
149
|
+
|
|
150
|
+
## GitHub Action
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
- uses: psungmin24/laxy-verify@v1
|
|
154
|
+
with:
|
|
155
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
156
|
+
fail-on: silver # optional, default: bronze
|
|
157
|
+
config: .laxy.yml # optional
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Posts a PR comment with grade and Lighthouse scores, and sets a commit status check.
|
|
161
|
+
|
|
162
|
+
For Pro/Pro+ features in CI, add `LAXY_TOKEN` to your repository secrets:
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
- uses: psungmin24/laxy-verify@v1
|
|
166
|
+
with:
|
|
167
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
168
|
+
fail-on: gold # Gold requires Pro+ account
|
|
169
|
+
env:
|
|
170
|
+
LAXY_TOKEN: ${{ secrets.LAXY_TOKEN }}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Limitations (v1)
|
|
174
|
+
|
|
175
|
+
- **Monorepos:** Not supported. Run `npx laxy-verify apps/web` for the app subdirectory.
|
|
176
|
+
- **Lighthouse accuracy:** Scores are measured in dev mode (`npm run dev`). Production scores are typically higher.
|
|
177
|
+
- **Fork PRs:** Comments and status checks are not posted on PRs from forks (GitHub security restriction).
|
|
178
|
+
- **Yarn Berry:** May require manual `package_manager` configuration if using Corepack.
|
|
179
|
+
- **Gold grade:** Requires Pro+ plan and `--multi-viewport` flag. All three viewports must pass Lighthouse thresholds.
|
|
180
|
+
|
|
181
|
+
## Expected CI Timing
|
|
182
|
+
|
|
183
|
+
- Build: 15-30s
|
|
184
|
+
- Dev server start: 5-20s
|
|
185
|
+
- Lighthouse (1 run): ~15s
|
|
186
|
+
- Lighthouse (3 runs CI): ~45s
|
|
187
|
+
- Multi-viewport (Pro+, 3 viewports): ~60s
|
|
188
|
+
- **Total: 35-95s per PR**
|
|
189
|
+
|
|
190
|
+
## .laxy-result.json
|
|
191
|
+
|
|
192
|
+
Written after every run:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"grade": "Silver",
|
|
197
|
+
"timestamp": "2026-04-07T10:30:00Z",
|
|
198
|
+
"build": { "success": true, "durationMs": 12300, "errors": [] },
|
|
199
|
+
"lighthouse": { "performance": 87, "accessibility": 92, "seo": 88, "bestPractices": 90, "runs": 1 },
|
|
200
|
+
"thresholds": { "performance": 70, "accessibility": 85, "seo": 80, "bestPractices": 80 },
|
|
201
|
+
"ciMode": false,
|
|
202
|
+
"framework": "nextjs",
|
|
203
|
+
"exitCode": 0,
|
|
204
|
+
"config_fail_on": "bronze"
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Use `npx laxy-verify --badge` to output a shields.io badge markdown from this file.
|
|
209
|
+
|
|
210
|
+
## Badge
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npx laxy-verify --badge
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Output: ``
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT
|
|
221
|
+
|
|
222
|
+
|
|
11
223
|
## Quick Start
|
|
12
224
|
|
|
13
225
|
### 1. Initialize
|
package/dist/auth.js
CHANGED
|
@@ -121,48 +121,28 @@ function whoami() {
|
|
|
121
121
|
console.log(" 인증 정보를 읽을 수 없습니다.");
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
async function login(emailArg) {
|
|
125
|
+
// 단일 readline 인터페이스로 이메일+비밀번호 수집
|
|
126
|
+
// (복수 rl 인스턴스 생성 시 Windows UV_HANDLE_CLOSING Assertion 발생)
|
|
127
|
+
const rl = readline.createInterface({
|
|
128
|
+
input: process.stdin,
|
|
129
|
+
output: process.stdout,
|
|
130
|
+
terminal: false,
|
|
131
|
+
});
|
|
132
|
+
const ask = (prompt) => new Promise((resolve) => rl.question(prompt, (ans) => resolve(ans.trim())));
|
|
133
|
+
const askMuted = (prompt) => new Promise((resolve) => {
|
|
132
134
|
process.stdout.write(prompt);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
process.stdin._handle?.setRawMode?.(true);
|
|
137
|
-
}
|
|
138
|
-
let password = "";
|
|
139
|
-
process.stdin.on("data", (chunk) => {
|
|
140
|
-
const char = chunk.toString("utf-8");
|
|
141
|
-
if (char === "\n" || char === "\r" || char === "\u0004") {
|
|
142
|
-
process.stdout.write("\n");
|
|
143
|
-
rl.close();
|
|
144
|
-
resolve(password);
|
|
145
|
-
}
|
|
146
|
-
else if (char === "\u0008" || char === "\u007f") {
|
|
147
|
-
password = password.slice(0, -1);
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
password += char;
|
|
151
|
-
}
|
|
135
|
+
rl.once("line", (line) => {
|
|
136
|
+
process.stdout.write("\n");
|
|
137
|
+
resolve(line.trim());
|
|
152
138
|
});
|
|
153
139
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return Promise.resolve(emailArg);
|
|
161
|
-
}
|
|
162
|
-
return new Promise((res) => rl.question(" 이메일: ", (ans) => { rl.close(); res(ans.trim()); }));
|
|
163
|
-
};
|
|
164
|
-
const email = await askEmail();
|
|
165
|
-
const password = await promptPassword(" 비밀번호: ");
|
|
140
|
+
const email = emailArg?.trim() ?? (await ask(" 이메일: "));
|
|
141
|
+
const password = await askMuted(" 비밀번호: ");
|
|
142
|
+
// 입력 완료 후 stdin 핸들 즉시 해제 (Windows Assertion 방지)
|
|
143
|
+
rl.removeAllListeners();
|
|
144
|
+
rl.close();
|
|
145
|
+
process.stdin.destroy();
|
|
166
146
|
console.log("\n 로그인 중...");
|
|
167
147
|
let res;
|
|
168
148
|
try {
|