laxy-verify 1.3.0 → 1.3.2
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 +486 -474
- package/dist/ai-analysis.d.ts +28 -0
- package/dist/ai-analysis.js +32 -0
- package/dist/audit/broken-links.d.ts +25 -25
- package/dist/audit/broken-links.js +97 -97
- package/dist/badge.d.ts +2 -2
- package/dist/badge.js +18 -18
- package/dist/cli.js +1242 -1250
- package/dist/compare-env.d.ts +23 -0
- package/dist/compare-env.js +55 -0
- package/dist/config.d.ts +102 -102
- package/dist/config.js +360 -360
- package/dist/entitlement.d.ts +15 -15
- package/dist/entitlement.js +98 -98
- package/dist/init-analysis.d.ts +6 -0
- package/dist/init-analysis.js +302 -0
- package/dist/init.js +132 -132
- package/dist/lighthouse.d.ts +37 -37
- package/dist/lighthouse.js +231 -231
- package/dist/report-markdown.d.ts +53 -53
- package/dist/report-markdown.js +407 -407
- package/dist/route-discovery.d.ts +7 -0
- package/dist/route-discovery.js +108 -0
- package/dist/security-audit.d.ts +17 -17
- package/dist/security-audit.js +127 -127
- package/dist/serve.d.ts +1 -0
- package/dist/serve.js +36 -0
- package/dist/verification-core/report.js +526 -526
- package/dist/verification-core/types.d.ts +164 -164
- package/dist/visual-diff.d.ts +33 -33
- package/dist/visual-diff.js +213 -213
- package/package.json +1 -1
package/dist/init.js
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
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.runInit = runInit;
|
|
37
|
-
const fs = __importStar(require("node:fs"));
|
|
38
|
-
const path = __importStar(require("node:path"));
|
|
39
|
-
const detect_js_1 = require("./detect.js");
|
|
40
|
-
const init_analysis_js_1 = require("./init-analysis.js");
|
|
41
|
-
function renderStringList(items, indent = " ") {
|
|
42
|
-
return items.map((item) => `${indent}- ${item}`).join("\n");
|
|
43
|
-
}
|
|
44
|
-
function renderScenarios() {
|
|
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.runInit = runInit;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const detect_js_1 = require("./detect.js");
|
|
40
|
+
const init_analysis_js_1 = require("./init-analysis.js");
|
|
41
|
+
function renderStringList(items, indent = " ") {
|
|
42
|
+
return items.map((item) => `${indent}- ${item}`).join("\n");
|
|
43
|
+
}
|
|
44
|
+
function renderScenarios() {
|
|
45
45
|
return `scenarios:
|
|
46
46
|
- name: "로그인 후 대시보드 접근"
|
|
47
47
|
steps:
|
|
@@ -51,59 +51,59 @@ function renderScenarios() {
|
|
|
51
51
|
- fill: input[name=password]
|
|
52
52
|
with: testpass123!
|
|
53
53
|
- click: button[type=submit]
|
|
54
|
-
- expect_visible: main`;
|
|
55
|
-
}
|
|
56
|
-
function runInit(dir) {
|
|
57
|
-
const laxyYmlPath = path.join(dir, ".laxy.yml");
|
|
58
|
-
const workflowDir = path.join(dir, ".github", "workflows");
|
|
59
|
-
const workflowPath = path.join(workflowDir, "laxy-verify.yml");
|
|
60
|
-
const gitignorePath = path.join(dir, ".gitignore");
|
|
61
|
-
if (fs.existsSync(laxyYmlPath)) {
|
|
62
|
-
console.log(".laxy.yml already exists. Skipping. Delete it to regenerate.");
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
let detectedFramework = null;
|
|
66
|
-
let detectedPort = 3000;
|
|
67
|
-
const initAnalysis = (0, init_analysis_js_1.analyzeProjectForInit)(dir);
|
|
68
|
-
try {
|
|
69
|
-
const detected = (0, detect_js_1.detect)(dir);
|
|
70
|
-
detectedFramework = detected.framework ?? "auto";
|
|
71
|
-
detectedPort = detected.port;
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// Keep defaults when auto-detection fails.
|
|
75
|
-
}
|
|
76
|
-
const extraRoutesBlock = initAnalysis.extraRoutes.length > 0
|
|
77
|
-
? `extra_routes:\n${renderStringList(initAnalysis.extraRoutes)}\n\n`
|
|
78
|
-
: "";
|
|
79
|
-
const scenarioBlocks = initAnalysis.scenarios.length > 0
|
|
80
|
-
? [
|
|
81
|
-
"scenarios:",
|
|
82
|
-
...initAnalysis.scenarios.flatMap((scenario) => [
|
|
83
|
-
` - name: "${scenario.name.replace(/"/g, '\\"')}"`,
|
|
84
|
-
" steps:",
|
|
85
|
-
...scenario.steps.flatMap((step) => {
|
|
86
|
-
if (step.goto)
|
|
87
|
-
return [` - goto: ${step.goto}`];
|
|
88
|
-
if (step.fill && step.with !== undefined) {
|
|
89
|
-
return [
|
|
90
|
-
` - fill: ${step.fill}`,
|
|
91
|
-
` with: ${step.with}`,
|
|
92
|
-
];
|
|
93
|
-
}
|
|
94
|
-
if (step.click)
|
|
95
|
-
return [` - click: ${step.click}`];
|
|
96
|
-
if (step.expect_visible)
|
|
97
|
-
return [` - expect_visible: ${step.expect_visible}`];
|
|
98
|
-
if (step.expect_text)
|
|
99
|
-
return [` - expect_text: ${step.expect_text}`];
|
|
100
|
-
if (step.wait !== undefined)
|
|
101
|
-
return [` - wait: ${step.wait}`];
|
|
102
|
-
return [];
|
|
103
|
-
}),
|
|
104
|
-
]),
|
|
105
|
-
].join("\n")
|
|
106
|
-
: renderScenarios();
|
|
54
|
+
- expect_visible: main`;
|
|
55
|
+
}
|
|
56
|
+
function runInit(dir) {
|
|
57
|
+
const laxyYmlPath = path.join(dir, ".laxy.yml");
|
|
58
|
+
const workflowDir = path.join(dir, ".github", "workflows");
|
|
59
|
+
const workflowPath = path.join(workflowDir, "laxy-verify.yml");
|
|
60
|
+
const gitignorePath = path.join(dir, ".gitignore");
|
|
61
|
+
if (fs.existsSync(laxyYmlPath)) {
|
|
62
|
+
console.log(".laxy.yml already exists. Skipping. Delete it to regenerate.");
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
let detectedFramework = null;
|
|
66
|
+
let detectedPort = 3000;
|
|
67
|
+
const initAnalysis = (0, init_analysis_js_1.analyzeProjectForInit)(dir);
|
|
68
|
+
try {
|
|
69
|
+
const detected = (0, detect_js_1.detect)(dir);
|
|
70
|
+
detectedFramework = detected.framework ?? "auto";
|
|
71
|
+
detectedPort = detected.port;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Keep defaults when auto-detection fails.
|
|
75
|
+
}
|
|
76
|
+
const extraRoutesBlock = initAnalysis.extraRoutes.length > 0
|
|
77
|
+
? `extra_routes:\n${renderStringList(initAnalysis.extraRoutes)}\n\n`
|
|
78
|
+
: "";
|
|
79
|
+
const scenarioBlocks = initAnalysis.scenarios.length > 0
|
|
80
|
+
? [
|
|
81
|
+
"scenarios:",
|
|
82
|
+
...initAnalysis.scenarios.flatMap((scenario) => [
|
|
83
|
+
` - name: "${scenario.name.replace(/"/g, '\\"')}"`,
|
|
84
|
+
" steps:",
|
|
85
|
+
...scenario.steps.flatMap((step) => {
|
|
86
|
+
if (step.goto)
|
|
87
|
+
return [` - goto: ${step.goto}`];
|
|
88
|
+
if (step.fill && step.with !== undefined) {
|
|
89
|
+
return [
|
|
90
|
+
` - fill: ${step.fill}`,
|
|
91
|
+
` with: ${step.with}`,
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
if (step.click)
|
|
95
|
+
return [` - click: ${step.click}`];
|
|
96
|
+
if (step.expect_visible)
|
|
97
|
+
return [` - expect_visible: ${step.expect_visible}`];
|
|
98
|
+
if (step.expect_text)
|
|
99
|
+
return [` - expect_text: ${step.expect_text}`];
|
|
100
|
+
if (step.wait !== undefined)
|
|
101
|
+
return [` - wait: ${step.wait}`];
|
|
102
|
+
return [];
|
|
103
|
+
}),
|
|
104
|
+
]),
|
|
105
|
+
].join("\n")
|
|
106
|
+
: renderScenarios();
|
|
107
107
|
const ymlContent = `# Generated by laxy-verify --init
|
|
108
108
|
# See https://github.com/SUNgm24/Laxy/tree/main/laxy-verify for full docs
|
|
109
109
|
framework: ${detectedFramework} # auto-detected
|
|
@@ -127,21 +127,21 @@ ${extraRoutesBlock}visual_diff:
|
|
|
127
127
|
- "[data-ignore-diff]"
|
|
128
128
|
|
|
129
129
|
${scenarioBlocks}
|
|
130
|
-
`;
|
|
131
|
-
fs.writeFileSync(laxyYmlPath, ymlContent, "utf-8");
|
|
132
|
-
console.log("Created .laxy.yml");
|
|
133
|
-
if (initAnalysis.scenarios.length > 0) {
|
|
134
|
-
console.log(`Seeded ${initAnalysis.scenarios.length} draft E2E scenario(s) from your app structure.`);
|
|
135
|
-
}
|
|
136
|
-
if (initAnalysis.extraRoutes.length > 0) {
|
|
137
|
-
console.log(`Detected ${initAnalysis.extraRoutes.length} extra route(s) for SPA / multi-page coverage.`);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (fs.existsSync(workflowPath)) {
|
|
141
|
-
console.log(".github/workflows/laxy-verify.yml already exists. Skipping.");
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
fs.mkdirSync(workflowDir, { recursive: true });
|
|
130
|
+
`;
|
|
131
|
+
fs.writeFileSync(laxyYmlPath, ymlContent, "utf-8");
|
|
132
|
+
console.log("Created .laxy.yml");
|
|
133
|
+
if (initAnalysis.scenarios.length > 0) {
|
|
134
|
+
console.log(`Seeded ${initAnalysis.scenarios.length} draft E2E scenario(s) from your app structure.`);
|
|
135
|
+
}
|
|
136
|
+
if (initAnalysis.extraRoutes.length > 0) {
|
|
137
|
+
console.log(`Detected ${initAnalysis.extraRoutes.length} extra route(s) for SPA / multi-page coverage.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (fs.existsSync(workflowPath)) {
|
|
141
|
+
console.log(".github/workflows/laxy-verify.yml already exists. Skipping.");
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
fs.mkdirSync(workflowDir, { recursive: true });
|
|
145
145
|
const workflowContent = `name: Laxy Verify
|
|
146
146
|
on:
|
|
147
147
|
pull_request:
|
|
@@ -198,23 +198,23 @@ jobs:
|
|
|
198
198
|
path: .laxy-result.json
|
|
199
199
|
retention-days: 30
|
|
200
200
|
overwrite: true
|
|
201
|
-
`;
|
|
202
|
-
fs.writeFileSync(workflowPath, workflowContent, "utf-8");
|
|
203
|
-
console.log("Created .github/workflows/laxy-verify.yml");
|
|
204
|
-
}
|
|
205
|
-
const gitignoreEntries = [".lighthouseci/", ".laxy-result.json"];
|
|
206
|
-
if (fs.existsSync(gitignorePath)) {
|
|
207
|
-
const existing = fs.readFileSync(gitignorePath, "utf-8");
|
|
208
|
-
const lines = existing.split("\n").map((line) => line.trim());
|
|
209
|
-
const missing = gitignoreEntries.filter((entry) => !lines.includes(entry));
|
|
210
|
-
if (missing.length > 0) {
|
|
211
|
-
fs.appendFileSync(gitignorePath, `${missing.join("\n")}\n`, "utf-8");
|
|
212
|
-
console.log(`Appended to .gitignore: ${missing.join(", ")}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
fs.writeFileSync(gitignorePath, `${gitignoreEntries.join("\n")}\n`, "utf-8");
|
|
217
|
-
console.log("Created .gitignore");
|
|
218
|
-
}
|
|
219
|
-
console.log("\n Done. Run 'npx laxy-verify .' to verify your project.");
|
|
220
|
-
}
|
|
201
|
+
`;
|
|
202
|
+
fs.writeFileSync(workflowPath, workflowContent, "utf-8");
|
|
203
|
+
console.log("Created .github/workflows/laxy-verify.yml");
|
|
204
|
+
}
|
|
205
|
+
const gitignoreEntries = [".lighthouseci/", ".laxy-result.json"];
|
|
206
|
+
if (fs.existsSync(gitignorePath)) {
|
|
207
|
+
const existing = fs.readFileSync(gitignorePath, "utf-8");
|
|
208
|
+
const lines = existing.split("\n").map((line) => line.trim());
|
|
209
|
+
const missing = gitignoreEntries.filter((entry) => !lines.includes(entry));
|
|
210
|
+
if (missing.length > 0) {
|
|
211
|
+
fs.appendFileSync(gitignorePath, `${missing.join("\n")}\n`, "utf-8");
|
|
212
|
+
console.log(`Appended to .gitignore: ${missing.join(", ")}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
fs.writeFileSync(gitignorePath, `${gitignoreEntries.join("\n")}\n`, "utf-8");
|
|
217
|
+
console.log("Created .gitignore");
|
|
218
|
+
}
|
|
219
|
+
console.log("\n Done. Run 'npx laxy-verify .' to verify your project.");
|
|
220
|
+
}
|
package/dist/lighthouse.d.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import type { LighthouseScores } from "./grade.js";
|
|
2
|
-
interface LhResult {
|
|
3
|
-
scores: LighthouseScores | null;
|
|
4
|
-
errors: string[];
|
|
5
|
-
}
|
|
6
|
-
export interface RouteScoreResult {
|
|
7
|
-
route: string;
|
|
8
|
-
scores: LighthouseScores | null;
|
|
9
|
-
errors: string[];
|
|
10
|
-
}
|
|
11
|
-
export interface MultiRouteLhResult {
|
|
12
|
-
/** Weighted average: root (/) = 2x weight, other routes = 1x */
|
|
13
|
-
aggregated: LighthouseScores | null;
|
|
14
|
-
perRoute: RouteScoreResult[];
|
|
15
|
-
/** True if every route that produced scores individually passed all thresholds */
|
|
16
|
-
allRoutesPass(thresholds: {
|
|
17
|
-
performance: number;
|
|
18
|
-
accessibility: number;
|
|
19
|
-
seo: number;
|
|
20
|
-
bestPractices: number;
|
|
21
|
-
}): boolean;
|
|
22
|
-
}
|
|
23
|
-
export declare function runLighthouse(port: number, runs: number, routePath?: string): Promise<LhResult>;
|
|
24
|
-
/** Run Lighthouse against an arbitrary external URL (Pro: compare-env feature). */
|
|
25
|
-
export declare function runLighthouseOnUrl(fullUrl: string, runs: number): Promise<LhResult>;
|
|
26
|
-
/**
|
|
27
|
-
* Run Lighthouse on multiple routes and aggregate results.
|
|
28
|
-
*
|
|
29
|
-
* Aggregation strategy (weighted average):
|
|
30
|
-
* - Root route "/" receives weight 2 (most visible page)
|
|
31
|
-
* - All other routes receive weight 1
|
|
32
|
-
*
|
|
33
|
-
* Gold eligibility: every route must individually pass all thresholds.
|
|
34
|
-
* Silver/Bronze eligibility: based on the weighted-average aggregate score.
|
|
35
|
-
*/
|
|
36
|
-
export declare function runLighthouseOnRoutes(port: number, runs: number, routes: string[]): Promise<MultiRouteLhResult>;
|
|
37
|
-
export {};
|
|
1
|
+
import type { LighthouseScores } from "./grade.js";
|
|
2
|
+
interface LhResult {
|
|
3
|
+
scores: LighthouseScores | null;
|
|
4
|
+
errors: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface RouteScoreResult {
|
|
7
|
+
route: string;
|
|
8
|
+
scores: LighthouseScores | null;
|
|
9
|
+
errors: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface MultiRouteLhResult {
|
|
12
|
+
/** Weighted average: root (/) = 2x weight, other routes = 1x */
|
|
13
|
+
aggregated: LighthouseScores | null;
|
|
14
|
+
perRoute: RouteScoreResult[];
|
|
15
|
+
/** True if every route that produced scores individually passed all thresholds */
|
|
16
|
+
allRoutesPass(thresholds: {
|
|
17
|
+
performance: number;
|
|
18
|
+
accessibility: number;
|
|
19
|
+
seo: number;
|
|
20
|
+
bestPractices: number;
|
|
21
|
+
}): boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function runLighthouse(port: number, runs: number, routePath?: string): Promise<LhResult>;
|
|
24
|
+
/** Run Lighthouse against an arbitrary external URL (Pro: compare-env feature). */
|
|
25
|
+
export declare function runLighthouseOnUrl(fullUrl: string, runs: number): Promise<LhResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Run Lighthouse on multiple routes and aggregate results.
|
|
28
|
+
*
|
|
29
|
+
* Aggregation strategy (weighted average):
|
|
30
|
+
* - Root route "/" receives weight 2 (most visible page)
|
|
31
|
+
* - All other routes receive weight 1
|
|
32
|
+
*
|
|
33
|
+
* Gold eligibility: every route must individually pass all thresholds.
|
|
34
|
+
* Silver/Bronze eligibility: based on the weighted-average aggregate score.
|
|
35
|
+
*/
|
|
36
|
+
export declare function runLighthouseOnRoutes(port: number, runs: number, routes: string[]): Promise<MultiRouteLhResult>;
|
|
37
|
+
export {};
|