laxy-verify 1.2.1 → 1.2.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 +193 -64
- package/dist/a11y-deep.d.ts +20 -0
- package/dist/a11y-deep.js +161 -0
- package/dist/ai-analysis.d.ts +28 -0
- package/dist/ai-analysis.js +32 -0
- package/dist/audit/broken-links.d.ts +5 -1
- package/dist/audit/broken-links.js +23 -12
- package/dist/bundle-size.d.ts +14 -0
- package/dist/bundle-size.js +209 -0
- package/dist/cli.js +432 -16
- package/dist/compare-env.d.ts +23 -0
- package/dist/compare-env.js +55 -0
- package/dist/config.d.ts +50 -0
- package/dist/config.js +149 -4
- package/dist/entitlement.d.ts +2 -0
- package/dist/entitlement.js +5 -1
- package/dist/init-analysis.d.ts +6 -0
- package/dist/init-analysis.js +302 -0
- package/dist/init.js +66 -0
- package/dist/lighthouse.d.ts +31 -1
- package/dist/lighthouse.js +76 -3
- package/dist/outdated-check.d.ts +17 -0
- package/dist/outdated-check.js +123 -0
- package/dist/report-markdown.d.ts +14 -0
- package/dist/report-markdown.js +21 -0
- package/dist/route-discovery.d.ts +7 -0
- package/dist/route-discovery.js +108 -0
- package/dist/secret-scan.d.ts +15 -0
- package/dist/secret-scan.js +218 -0
- package/dist/security-audit.d.ts +9 -1
- package/dist/security-audit.js +87 -24
- package/dist/seo-deep.d.ts +24 -0
- package/dist/seo-deep.js +147 -0
- package/dist/typecheck.d.ts +8 -0
- package/dist/typecheck.js +99 -0
- package/dist/verification-core/report.js +117 -0
- package/dist/verification-core/types.d.ts +58 -2
- package/dist/visual-diff.d.ts +8 -1
- package/dist/visual-diff.js +53 -8
- package/dist/vitals-budget.d.ts +23 -0
- package/dist/vitals-budget.js +168 -0
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-environment Lighthouse comparison (Pro feature).
|
|
3
|
+
*
|
|
4
|
+
* Compares Lighthouse scores between the local build and a reference
|
|
5
|
+
* environment URL (e.g. staging, production) to detect regressions
|
|
6
|
+
* introduced by the current PR.
|
|
7
|
+
*/
|
|
8
|
+
import type { LighthouseScores } from "./grade.js";
|
|
9
|
+
export interface EnvComparisonResult {
|
|
10
|
+
localUrl: string;
|
|
11
|
+
compareUrl: string;
|
|
12
|
+
local: LighthouseScores | null;
|
|
13
|
+
compare: LighthouseScores | null;
|
|
14
|
+
/** Positive = local is better, negative = compare env is better */
|
|
15
|
+
delta: {
|
|
16
|
+
performance: number | null;
|
|
17
|
+
accessibility: number | null;
|
|
18
|
+
seo: number | null;
|
|
19
|
+
bestPractices: number | null;
|
|
20
|
+
} | null;
|
|
21
|
+
}
|
|
22
|
+
export declare function runEnvComparison(localPort: number, compareUrl: string, runs?: number): Promise<EnvComparisonResult>;
|
|
23
|
+
export declare function printEnvComparison(result: EnvComparisonResult): void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runEnvComparison = runEnvComparison;
|
|
4
|
+
exports.printEnvComparison = printEnvComparison;
|
|
5
|
+
const lighthouse_js_1 = require("./lighthouse.js");
|
|
6
|
+
function scoreDelta(local, compare) {
|
|
7
|
+
if (!local || !compare)
|
|
8
|
+
return null;
|
|
9
|
+
return {
|
|
10
|
+
performance: local.performance - compare.performance,
|
|
11
|
+
accessibility: local.accessibility - compare.accessibility,
|
|
12
|
+
seo: local.seo - compare.seo,
|
|
13
|
+
bestPractices: local.bestPractices - compare.bestPractices,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
async function runEnvComparison(localPort, compareUrl, runs = 1) {
|
|
17
|
+
const localUrl = `http://127.0.0.1:${localPort}/`;
|
|
18
|
+
console.log(`\n [compare] Running env comparison: local vs ${compareUrl}`);
|
|
19
|
+
const [localResult, compareResult] = await Promise.all([
|
|
20
|
+
(0, lighthouse_js_1.runLighthouseOnUrl)(localUrl, runs),
|
|
21
|
+
(0, lighthouse_js_1.runLighthouseOnUrl)(compareUrl, runs),
|
|
22
|
+
]);
|
|
23
|
+
return {
|
|
24
|
+
localUrl,
|
|
25
|
+
compareUrl,
|
|
26
|
+
local: localResult.scores,
|
|
27
|
+
compare: compareResult.scores,
|
|
28
|
+
delta: scoreDelta(localResult.scores, compareResult.scores),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function printEnvComparison(result) {
|
|
32
|
+
console.log("\n Environment comparison:");
|
|
33
|
+
console.log(` Local: ${result.localUrl}`);
|
|
34
|
+
console.log(` Compare: ${result.compareUrl}`);
|
|
35
|
+
if (!result.local && !result.compare) {
|
|
36
|
+
console.log(" Both environments returned no Lighthouse data.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const fmt = (v) => (v === null ? "—" : String(v));
|
|
40
|
+
const fmtDelta = (d) => {
|
|
41
|
+
if (d === null)
|
|
42
|
+
return "";
|
|
43
|
+
if (d > 0)
|
|
44
|
+
return ` (+${d})`;
|
|
45
|
+
if (d < 0)
|
|
46
|
+
return ` (${d})`;
|
|
47
|
+
return " (=)";
|
|
48
|
+
};
|
|
49
|
+
const d = result.delta;
|
|
50
|
+
console.log(" Metric Local Compare Delta");
|
|
51
|
+
console.log(` Performance ${fmt(result.local?.performance ?? null).padEnd(6)} ${fmt(result.compare?.performance ?? null).padEnd(8)}${fmtDelta(d?.performance ?? null)}`);
|
|
52
|
+
console.log(` Accessibility ${fmt(result.local?.accessibility ?? null).padEnd(6)} ${fmt(result.compare?.accessibility ?? null).padEnd(8)}${fmtDelta(d?.accessibility ?? null)}`);
|
|
53
|
+
console.log(` SEO ${fmt(result.local?.seo ?? null).padEnd(6)} ${fmt(result.compare?.seo ?? null).padEnd(8)}${fmtDelta(d?.seo ?? null)}`);
|
|
54
|
+
console.log(` Best Practices ${fmt(result.local?.bestPractices ?? null).padEnd(6)} ${fmt(result.compare?.bestPractices ?? null).padEnd(8)}${fmtDelta(d?.bestPractices ?? null)}`);
|
|
55
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -18,6 +18,13 @@ export interface UserScenario {
|
|
|
18
18
|
name: string;
|
|
19
19
|
steps: UserScenarioStep[];
|
|
20
20
|
}
|
|
21
|
+
export interface VisualDiffConfig {
|
|
22
|
+
pixelmatchThreshold: number;
|
|
23
|
+
warnThreshold: number;
|
|
24
|
+
rollbackThreshold: number;
|
|
25
|
+
ignoreSelectors: string[];
|
|
26
|
+
disableAnimations: boolean;
|
|
27
|
+
}
|
|
21
28
|
export interface LaxyConfig {
|
|
22
29
|
framework: string;
|
|
23
30
|
build_command: string;
|
|
@@ -34,18 +41,61 @@ export interface LaxyConfig {
|
|
|
34
41
|
max_crawl_depth: number;
|
|
35
42
|
max_crawl_pages: number;
|
|
36
43
|
browsers: string[];
|
|
44
|
+
/** Explicit list of routes to audit with Lighthouse (e.g. ["/", "/about", "/pricing"]). */
|
|
45
|
+
lighthouse_routes?: string[];
|
|
46
|
+
/** Extra routes to audit even if the crawler cannot discover them (e.g. SPA router.push routes). */
|
|
47
|
+
extra_routes?: string[];
|
|
48
|
+
/** Maximum number of crawl-discovered routes to run Lighthouse on. Default: 5. */
|
|
49
|
+
max_lighthouse_routes: number;
|
|
50
|
+
visual_diff: VisualDiffConfig;
|
|
51
|
+
/** Run TypeScript type check (tsc --noEmit). Default: false. */
|
|
52
|
+
typecheck: boolean;
|
|
53
|
+
/** Run secret/credential leak scan. Default: false. */
|
|
54
|
+
secret_scan: boolean;
|
|
55
|
+
/** Paths to ignore during secret scan. */
|
|
56
|
+
secret_scan_ignore_paths: string[];
|
|
57
|
+
/** Run bundle size analysis. Default: false. */
|
|
58
|
+
bundle_size: boolean;
|
|
59
|
+
/** Run outdated dependency check. Default: false. */
|
|
60
|
+
outdated_check: boolean;
|
|
61
|
+
/** Run deep accessibility audit (axe-core). Default: false. */
|
|
62
|
+
a11y_deep: boolean;
|
|
63
|
+
/** Run deep SEO audit. Default: false. */
|
|
64
|
+
seo_deep: boolean;
|
|
65
|
+
/** Run Core Web Vitals budget check. Default: false. */
|
|
66
|
+
vitals_budget: boolean;
|
|
37
67
|
}
|
|
38
68
|
export declare class ConfigParseError extends Error {
|
|
39
69
|
constructor(msg: string);
|
|
40
70
|
}
|
|
71
|
+
export interface TeamThresholdsConfig {
|
|
72
|
+
performance: number;
|
|
73
|
+
accessibility: number;
|
|
74
|
+
seo: number;
|
|
75
|
+
best_practices: number;
|
|
76
|
+
fail_on: FailOn;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 로그인된 CLI 토큰으로 팀 공통 임계값을 서버에서 가져온다.
|
|
80
|
+
* 토큰 없음 / 팀 없음 / 네트워크 오류 시 null 반환 (graceful degradation).
|
|
81
|
+
*/
|
|
82
|
+
export declare function fetchTeamThresholds(): Promise<TeamThresholdsConfig | null>;
|
|
41
83
|
export interface LoadConfigOptions {
|
|
42
84
|
dir: string;
|
|
43
85
|
configPath?: string;
|
|
44
86
|
cliFlags?: {
|
|
45
87
|
failOn?: FailOn;
|
|
46
88
|
skipLighthouse?: boolean;
|
|
89
|
+
typecheck?: boolean;
|
|
90
|
+
secretScan?: boolean;
|
|
91
|
+
bundleSize?: boolean;
|
|
92
|
+
outdatedCheck?: boolean;
|
|
93
|
+
a11yDeep?: boolean;
|
|
94
|
+
seoDeep?: boolean;
|
|
95
|
+
vitalsBudget?: boolean;
|
|
47
96
|
};
|
|
48
97
|
ciMode: boolean;
|
|
98
|
+
teamThresholds?: TeamThresholdsConfig | null;
|
|
49
99
|
}
|
|
50
100
|
export declare function loadConfig(options: LoadConfigOptions): LaxyConfig & {
|
|
51
101
|
ciMode: boolean;
|
package/dist/config.js
CHANGED
|
@@ -34,10 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ConfigParseError = void 0;
|
|
37
|
+
exports.fetchTeamThresholds = fetchTeamThresholds;
|
|
37
38
|
exports.loadConfig = loadConfig;
|
|
38
39
|
const fs = __importStar(require("node:fs"));
|
|
39
40
|
const path = __importStar(require("node:path"));
|
|
40
41
|
const yaml = __importStar(require("js-yaml"));
|
|
42
|
+
const auth_js_1 = require("./auth.js");
|
|
41
43
|
const DEFAULT_CONFIG = {
|
|
42
44
|
framework: "auto",
|
|
43
45
|
build_command: "",
|
|
@@ -46,7 +48,7 @@ const DEFAULT_CONFIG = {
|
|
|
46
48
|
port: 3000,
|
|
47
49
|
build_timeout: 300,
|
|
48
50
|
dev_timeout: 60,
|
|
49
|
-
lighthouse_runs:
|
|
51
|
+
lighthouse_runs: 3,
|
|
50
52
|
thresholds: {
|
|
51
53
|
performance: 70,
|
|
52
54
|
accessibility: 85,
|
|
@@ -58,6 +60,22 @@ const DEFAULT_CONFIG = {
|
|
|
58
60
|
max_crawl_depth: 3,
|
|
59
61
|
max_crawl_pages: 10,
|
|
60
62
|
browsers: ["chromium"],
|
|
63
|
+
max_lighthouse_routes: 5,
|
|
64
|
+
visual_diff: {
|
|
65
|
+
pixelmatchThreshold: 0.1,
|
|
66
|
+
warnThreshold: 30,
|
|
67
|
+
rollbackThreshold: 60,
|
|
68
|
+
ignoreSelectors: [],
|
|
69
|
+
disableAnimations: true,
|
|
70
|
+
},
|
|
71
|
+
typecheck: false,
|
|
72
|
+
secret_scan: false,
|
|
73
|
+
secret_scan_ignore_paths: [],
|
|
74
|
+
bundle_size: false,
|
|
75
|
+
outdated_check: false,
|
|
76
|
+
a11y_deep: false,
|
|
77
|
+
seo_deep: false,
|
|
78
|
+
vitals_budget: false,
|
|
61
79
|
};
|
|
62
80
|
const VALID_FAIL_ON = ["unverified", "bronze", "silver", "gold"];
|
|
63
81
|
class ConfigParseError extends Error {
|
|
@@ -103,6 +121,47 @@ function parseYaml(filePath) {
|
|
|
103
121
|
if (browsers.length > 0)
|
|
104
122
|
result.browsers = browsers;
|
|
105
123
|
}
|
|
124
|
+
if (Array.isArray(raw.lighthouse_routes)) {
|
|
125
|
+
const routes = raw.lighthouse_routes
|
|
126
|
+
.filter((r) => typeof r === "string" && r.startsWith("/"))
|
|
127
|
+
.slice(0, 20);
|
|
128
|
+
if (routes.length > 0)
|
|
129
|
+
result.lighthouse_routes = routes;
|
|
130
|
+
}
|
|
131
|
+
if (Array.isArray(raw.extra_routes)) {
|
|
132
|
+
const routes = raw.extra_routes
|
|
133
|
+
.filter((r) => typeof r === "string" && r.startsWith("/"))
|
|
134
|
+
.slice(0, 20);
|
|
135
|
+
if (routes.length > 0)
|
|
136
|
+
result.extra_routes = routes;
|
|
137
|
+
}
|
|
138
|
+
if (typeof raw.max_lighthouse_routes === "number") {
|
|
139
|
+
result.max_lighthouse_routes = Math.max(1, Math.min(20, raw.max_lighthouse_routes));
|
|
140
|
+
}
|
|
141
|
+
if (typeof raw.visual_diff === "object" &&
|
|
142
|
+
raw.visual_diff !== null &&
|
|
143
|
+
!Array.isArray(raw.visual_diff)) {
|
|
144
|
+
const vd = raw.visual_diff;
|
|
145
|
+
const visualDiff = {};
|
|
146
|
+
if (typeof vd.pixelmatch_threshold === "number") {
|
|
147
|
+
visualDiff.pixelmatchThreshold = Math.max(0, Math.min(1, vd.pixelmatch_threshold));
|
|
148
|
+
}
|
|
149
|
+
if (typeof vd.warn_threshold === "number") {
|
|
150
|
+
visualDiff.warnThreshold = Math.max(0, Math.min(100, vd.warn_threshold));
|
|
151
|
+
}
|
|
152
|
+
if (typeof vd.rollback_threshold === "number") {
|
|
153
|
+
visualDiff.rollbackThreshold = Math.max(0, Math.min(100, vd.rollback_threshold));
|
|
154
|
+
}
|
|
155
|
+
if (Array.isArray(vd.ignore_selectors)) {
|
|
156
|
+
visualDiff.ignoreSelectors = vd.ignore_selectors
|
|
157
|
+
.filter((selector) => typeof selector === "string" && selector.trim().length > 0)
|
|
158
|
+
.slice(0, 30);
|
|
159
|
+
}
|
|
160
|
+
if (typeof vd.disable_animations === "boolean") {
|
|
161
|
+
visualDiff.disableAnimations = vd.disable_animations;
|
|
162
|
+
}
|
|
163
|
+
result.visual_diff = visualDiff;
|
|
164
|
+
}
|
|
106
165
|
if (typeof raw.fail_on === "string") {
|
|
107
166
|
const f = raw.fail_on;
|
|
108
167
|
if (!VALID_FAIL_ON.includes(f)) {
|
|
@@ -162,14 +221,66 @@ function parseYaml(filePath) {
|
|
|
162
221
|
result.scenarios = scenarios;
|
|
163
222
|
}
|
|
164
223
|
}
|
|
224
|
+
if (typeof raw.typecheck === "boolean")
|
|
225
|
+
result.typecheck = raw.typecheck;
|
|
226
|
+
if (typeof raw.secret_scan === "boolean")
|
|
227
|
+
result.secret_scan = raw.secret_scan;
|
|
228
|
+
if (Array.isArray(raw.secret_scan_ignore_paths)) {
|
|
229
|
+
result.secret_scan_ignore_paths = raw.secret_scan_ignore_paths
|
|
230
|
+
.filter((p) => typeof p === "string");
|
|
231
|
+
}
|
|
232
|
+
if (typeof raw.bundle_size === "boolean")
|
|
233
|
+
result.bundle_size = raw.bundle_size;
|
|
234
|
+
if (typeof raw.outdated_check === "boolean")
|
|
235
|
+
result.outdated_check = raw.outdated_check;
|
|
236
|
+
if (typeof raw.a11y_deep === "boolean")
|
|
237
|
+
result.a11y_deep = raw.a11y_deep;
|
|
238
|
+
if (typeof raw.seo_deep === "boolean")
|
|
239
|
+
result.seo_deep = raw.seo_deep;
|
|
240
|
+
if (typeof raw.vitals_budget === "boolean")
|
|
241
|
+
result.vitals_budget = raw.vitals_budget;
|
|
165
242
|
return result;
|
|
166
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* 로그인된 CLI 토큰으로 팀 공통 임계값을 서버에서 가져온다.
|
|
246
|
+
* 토큰 없음 / 팀 없음 / 네트워크 오류 시 null 반환 (graceful degradation).
|
|
247
|
+
*/
|
|
248
|
+
async function fetchTeamThresholds() {
|
|
249
|
+
const token = (0, auth_js_1.loadToken)();
|
|
250
|
+
if (!token)
|
|
251
|
+
return null;
|
|
252
|
+
try {
|
|
253
|
+
const res = await fetch(`${auth_js_1.LAXY_API_URL}/api/v1/team-thresholds`, {
|
|
254
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
255
|
+
signal: AbortSignal.timeout(5000),
|
|
256
|
+
});
|
|
257
|
+
if (!res.ok)
|
|
258
|
+
return null;
|
|
259
|
+
const data = (await res.json());
|
|
260
|
+
return data.thresholds ?? null;
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
167
266
|
function loadConfig(options) {
|
|
168
267
|
const configPath = options.configPath ?? path.join(options.dir, ".laxy.yml");
|
|
169
268
|
let base = {};
|
|
170
|
-
|
|
269
|
+
const hasLocalConfig = fs.existsSync(configPath);
|
|
270
|
+
if (hasLocalConfig) {
|
|
171
271
|
base = parseYaml(configPath);
|
|
172
272
|
}
|
|
273
|
+
// 팀 임계값: 로컬 .laxy.yml에 thresholds가 없을 때만 적용
|
|
274
|
+
const team = options.teamThresholds ?? null;
|
|
275
|
+
const teamThresholdFallback = (!hasLocalConfig || !base.thresholds) && team
|
|
276
|
+
? {
|
|
277
|
+
performance: team.performance,
|
|
278
|
+
accessibility: team.accessibility,
|
|
279
|
+
seo: team.seo,
|
|
280
|
+
bestPractices: team.best_practices,
|
|
281
|
+
}
|
|
282
|
+
: {};
|
|
283
|
+
const teamFailOnFallback = (!hasLocalConfig || !base.fail_on) && team ? team.fail_on : undefined;
|
|
173
284
|
const config = {
|
|
174
285
|
...DEFAULT_CONFIG,
|
|
175
286
|
framework: base.framework ?? DEFAULT_CONFIG.framework,
|
|
@@ -180,14 +291,33 @@ function loadConfig(options) {
|
|
|
180
291
|
build_timeout: base.build_timeout ?? DEFAULT_CONFIG.build_timeout,
|
|
181
292
|
dev_timeout: base.dev_timeout ?? DEFAULT_CONFIG.dev_timeout,
|
|
182
293
|
lighthouse_runs: base.lighthouse_runs ?? DEFAULT_CONFIG.lighthouse_runs,
|
|
183
|
-
fail_on: base.fail_on ?? DEFAULT_CONFIG.fail_on,
|
|
294
|
+
fail_on: base.fail_on ?? teamFailOnFallback ?? DEFAULT_CONFIG.fail_on,
|
|
184
295
|
scenarios: base.scenarios,
|
|
185
296
|
crawl: base.crawl ?? DEFAULT_CONFIG.crawl,
|
|
186
297
|
max_crawl_depth: base.max_crawl_depth ?? DEFAULT_CONFIG.max_crawl_depth,
|
|
187
298
|
max_crawl_pages: base.max_crawl_pages ?? DEFAULT_CONFIG.max_crawl_pages,
|
|
188
299
|
browsers: base.browsers ?? DEFAULT_CONFIG.browsers,
|
|
300
|
+
lighthouse_routes: base.lighthouse_routes,
|
|
301
|
+
extra_routes: base.extra_routes,
|
|
302
|
+
max_lighthouse_routes: base.max_lighthouse_routes ?? DEFAULT_CONFIG.max_lighthouse_routes,
|
|
303
|
+
visual_diff: {
|
|
304
|
+
...DEFAULT_CONFIG.visual_diff,
|
|
305
|
+
...(base.visual_diff ?? {}),
|
|
306
|
+
},
|
|
307
|
+
typecheck: base.typecheck ?? DEFAULT_CONFIG.typecheck,
|
|
308
|
+
secret_scan: base.secret_scan ?? DEFAULT_CONFIG.secret_scan,
|
|
309
|
+
secret_scan_ignore_paths: base.secret_scan_ignore_paths ?? DEFAULT_CONFIG.secret_scan_ignore_paths,
|
|
310
|
+
bundle_size: base.bundle_size ?? DEFAULT_CONFIG.bundle_size,
|
|
311
|
+
outdated_check: base.outdated_check ?? DEFAULT_CONFIG.outdated_check,
|
|
312
|
+
a11y_deep: base.a11y_deep ?? DEFAULT_CONFIG.a11y_deep,
|
|
313
|
+
seo_deep: base.seo_deep ?? DEFAULT_CONFIG.seo_deep,
|
|
314
|
+
vitals_budget: base.vitals_budget ?? DEFAULT_CONFIG.vitals_budget,
|
|
315
|
+
};
|
|
316
|
+
config.thresholds = {
|
|
317
|
+
...DEFAULT_CONFIG.thresholds,
|
|
318
|
+
...teamThresholdFallback,
|
|
319
|
+
...(base.thresholds ?? {}),
|
|
189
320
|
};
|
|
190
|
-
config.thresholds = { ...DEFAULT_CONFIG.thresholds, ...(base.thresholds ?? {}) };
|
|
191
321
|
// CLI flag overrides
|
|
192
322
|
if (options.cliFlags?.failOn !== undefined) {
|
|
193
323
|
if (!VALID_FAIL_ON.includes(options.cliFlags.failOn)) {
|
|
@@ -211,5 +341,20 @@ function loadConfig(options) {
|
|
|
211
341
|
if (options.cliFlags?.skipLighthouse) {
|
|
212
342
|
// Effectively disables Lighthouse grading
|
|
213
343
|
}
|
|
344
|
+
// CLI flag overrides for new checks
|
|
345
|
+
if (options.cliFlags?.typecheck !== undefined)
|
|
346
|
+
config.typecheck = options.cliFlags.typecheck;
|
|
347
|
+
if (options.cliFlags?.secretScan !== undefined)
|
|
348
|
+
config.secret_scan = options.cliFlags.secretScan;
|
|
349
|
+
if (options.cliFlags?.bundleSize !== undefined)
|
|
350
|
+
config.bundle_size = options.cliFlags.bundleSize;
|
|
351
|
+
if (options.cliFlags?.outdatedCheck !== undefined)
|
|
352
|
+
config.outdated_check = options.cliFlags.outdatedCheck;
|
|
353
|
+
if (options.cliFlags?.a11yDeep !== undefined)
|
|
354
|
+
config.a11y_deep = options.cliFlags.a11yDeep;
|
|
355
|
+
if (options.cliFlags?.seoDeep !== undefined)
|
|
356
|
+
config.seo_deep = options.cliFlags.seoDeep;
|
|
357
|
+
if (options.cliFlags?.vitalsBudget !== undefined)
|
|
358
|
+
config.vitals_budget = options.cliFlags.vitalsBudget;
|
|
214
359
|
return { ...config, ciMode };
|
|
215
360
|
}
|
package/dist/entitlement.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export interface EntitlementFeatures {
|
|
|
3
3
|
github_actions: boolean;
|
|
4
4
|
queue_priority: boolean;
|
|
5
5
|
parallel_execution: boolean;
|
|
6
|
+
ai_failure_analysis: boolean;
|
|
7
|
+
compare_env: boolean;
|
|
6
8
|
}
|
|
7
9
|
export type TestablePlan = "free" | "pro" | "team";
|
|
8
10
|
export declare function normalizePlan(plan?: string | null): TestablePlan;
|
package/dist/entitlement.js
CHANGED
|
@@ -18,6 +18,8 @@ const FREE_FEATURES = {
|
|
|
18
18
|
github_actions: false,
|
|
19
19
|
queue_priority: false,
|
|
20
20
|
parallel_execution: false,
|
|
21
|
+
ai_failure_analysis: false,
|
|
22
|
+
compare_env: false,
|
|
21
23
|
};
|
|
22
24
|
let cache = null;
|
|
23
25
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -70,8 +72,10 @@ function applyPlanOverride(features, overridePlan) {
|
|
|
70
72
|
...features,
|
|
71
73
|
plan: overridePlan,
|
|
72
74
|
// All verification features run on every plan
|
|
73
|
-
// github_actions — Pro and Team
|
|
75
|
+
// github_actions, ai_failure_analysis, compare_env — Pro and Team
|
|
74
76
|
github_actions: isPro,
|
|
77
|
+
ai_failure_analysis: isPro,
|
|
78
|
+
compare_env: isPro,
|
|
75
79
|
// queue_priority, parallel_execution — Team only
|
|
76
80
|
queue_priority: isTeam,
|
|
77
81
|
parallel_execution: isTeam,
|