laxy-verify 1.0.0 → 1.1.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/dist/auth.d.ts +6 -0
- package/dist/auth.js +186 -0
- package/dist/badge.d.ts +1 -1
- package/dist/badge.js +14 -11
- package/dist/build.d.ts +11 -11
- package/dist/build.js +67 -59
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +370 -257
- package/dist/comment.d.ts +20 -20
- package/dist/comment.js +82 -46
- package/dist/config.d.ts +34 -34
- package/dist/config.js +156 -118
- package/dist/detect.d.ts +10 -10
- package/dist/detect.js +133 -97
- package/dist/entitlement.d.ts +11 -0
- package/dist/entitlement.js +63 -0
- package/dist/github.d.ts +8 -8
- package/dist/github.js +14 -11
- package/dist/grade.d.ts +39 -37
- package/dist/grade.js +68 -57
- package/dist/init.d.ts +1 -1
- package/dist/init.js +91 -55
- package/dist/lighthouse.d.ts +7 -7
- package/dist/lighthouse.js +130 -94
- package/dist/multi-viewport.d.ts +20 -0
- package/dist/multi-viewport.js +155 -0
- package/dist/serve.d.ts +12 -12
- package/dist/serve.js +126 -83
- package/dist/status.d.ts +6 -6
- package/dist/status.js +36 -33
- package/package.json +3 -3
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const LAXY_API_URL: string;
|
|
2
|
+
export declare function loadToken(): string | null;
|
|
3
|
+
export declare function saveToken(token: string, email: string, expiresInSec: number): void;
|
|
4
|
+
export declare function clearToken(): void;
|
|
5
|
+
export declare function whoami(): void;
|
|
6
|
+
export declare function login(emailArg?: string): Promise<void>;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LAXY_API_URL = void 0;
|
|
37
|
+
exports.loadToken = loadToken;
|
|
38
|
+
exports.saveToken = saveToken;
|
|
39
|
+
exports.clearToken = clearToken;
|
|
40
|
+
exports.whoami = whoami;
|
|
41
|
+
exports.login = login;
|
|
42
|
+
/**
|
|
43
|
+
* auth.ts — laxy-verify CLI 인증 토큰 저장/로드/삭제
|
|
44
|
+
*
|
|
45
|
+
* 토큰은 ~/.laxy/credentials.json 에 저장됩니다.
|
|
46
|
+
* 로그인: POST /api/cli-auth (이메일 + 비밀번호)
|
|
47
|
+
* 로그아웃: 로컬 파일 삭제
|
|
48
|
+
*/
|
|
49
|
+
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
51
|
+
const os = __importStar(require("node:os"));
|
|
52
|
+
const readline = __importStar(require("node:readline"));
|
|
53
|
+
const CREDENTIALS_DIR = path.join(os.homedir(), ".laxy");
|
|
54
|
+
const CREDENTIALS_PATH = path.join(CREDENTIALS_DIR, "credentials.json");
|
|
55
|
+
exports.LAXY_API_URL = process.env.LAXY_API_URL ?? "https://laxy.io";
|
|
56
|
+
function loadToken() {
|
|
57
|
+
// 환경변수 우선
|
|
58
|
+
const envToken = process.env.LAXY_TOKEN;
|
|
59
|
+
if (envToken)
|
|
60
|
+
return envToken;
|
|
61
|
+
try {
|
|
62
|
+
if (!fs.existsSync(CREDENTIALS_PATH))
|
|
63
|
+
return null;
|
|
64
|
+
const raw = fs.readFileSync(CREDENTIALS_PATH, "utf-8");
|
|
65
|
+
const creds = JSON.parse(raw);
|
|
66
|
+
if (!creds.token)
|
|
67
|
+
return null;
|
|
68
|
+
// 만료 여부 로컬 체크 (서버도 재확인하지만 UX 개선)
|
|
69
|
+
if (creds.expires_at) {
|
|
70
|
+
const exp = new Date(creds.expires_at).getTime();
|
|
71
|
+
if (exp < Date.now()) {
|
|
72
|
+
console.error(" ⚠️ 저장된 CLI 토큰이 만료되었습니다. 다시 로그인하세요: laxy-verify login");
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return creds.token;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function saveToken(token, email, expiresInSec) {
|
|
83
|
+
if (!fs.existsSync(CREDENTIALS_DIR)) {
|
|
84
|
+
fs.mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
const creds = {
|
|
87
|
+
token,
|
|
88
|
+
email,
|
|
89
|
+
saved_at: new Date().toISOString(),
|
|
90
|
+
expires_at: new Date(Date.now() + expiresInSec * 1000).toISOString(),
|
|
91
|
+
};
|
|
92
|
+
fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), { encoding: "utf-8", mode: 0o600 });
|
|
93
|
+
}
|
|
94
|
+
function clearToken() {
|
|
95
|
+
if (fs.existsSync(CREDENTIALS_PATH)) {
|
|
96
|
+
fs.rmSync(CREDENTIALS_PATH);
|
|
97
|
+
console.log(" 로그아웃 완료 — 인증 정보를 삭제했습니다.");
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
console.log(" 저장된 인증 정보가 없습니다.");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function whoami() {
|
|
104
|
+
const envToken = process.env.LAXY_TOKEN;
|
|
105
|
+
if (envToken) {
|
|
106
|
+
console.log(" 인증: 환경변수 LAXY_TOKEN 사용 중");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
if (!fs.existsSync(CREDENTIALS_PATH)) {
|
|
111
|
+
console.log(" 로그인되지 않았습니다. laxy-verify login 명령으로 로그인하세요.");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const raw = fs.readFileSync(CREDENTIALS_PATH, "utf-8");
|
|
115
|
+
const creds = JSON.parse(raw);
|
|
116
|
+
const expDate = creds.expires_at ? new Date(creds.expires_at).toLocaleDateString("ko-KR") : "알 수 없음";
|
|
117
|
+
console.log(` 이메일: ${creds.email}`);
|
|
118
|
+
console.log(` 토큰 만료: ${expDate}`);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
console.log(" 인증 정보를 읽을 수 없습니다.");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** readline으로 비밀번호 입력 (터미널 에코 숨김) */
|
|
125
|
+
function promptPassword(prompt) {
|
|
126
|
+
return new Promise((resolve) => {
|
|
127
|
+
const rl = readline.createInterface({
|
|
128
|
+
input: process.stdin,
|
|
129
|
+
output: process.stdout,
|
|
130
|
+
terminal: true,
|
|
131
|
+
});
|
|
132
|
+
process.stdout.write(prompt);
|
|
133
|
+
// 에코 끄기 (Unix 계열만, Windows는 readline이 처리)
|
|
134
|
+
if (process.stdin.isTTY) {
|
|
135
|
+
// @ts-ignore — private API
|
|
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
|
+
}
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async function login(emailArg) {
|
|
156
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
157
|
+
const askEmail = () => {
|
|
158
|
+
if (emailArg) {
|
|
159
|
+
rl.close();
|
|
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(" 비밀번호: ");
|
|
166
|
+
console.log("\n 로그인 중...");
|
|
167
|
+
let res;
|
|
168
|
+
try {
|
|
169
|
+
res = await fetch(`${exports.LAXY_API_URL}/api/cli-auth`, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: { "Content-Type": "application/json" },
|
|
172
|
+
body: JSON.stringify({ email, password }),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
console.error(` ❌ 서버에 연결할 수 없습니다. (${exports.LAXY_API_URL})`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const data = (await res.json());
|
|
180
|
+
if (!res.ok || !data.token) {
|
|
181
|
+
console.error(` ❌ ${data.error ?? "로그인에 실패했습니다."}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
saveToken(data.token, email, data.expires_in ?? 30 * 24 * 60 * 60);
|
|
185
|
+
console.log(" ✅ 로그인 성공! laxy-verify . 을 실행해 검증을 시작하세요.");
|
|
186
|
+
}
|
package/dist/badge.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function generateBadge(grade: string): string;
|
|
1
|
+
export declare function generateBadge(grade: string): string;
|
package/dist/badge.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
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
CHANGED
|
@@ -1,11 +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>;
|
|
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
CHANGED
|
@@ -1,59 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
export {};
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export {};
|