laxy-verify 1.2.0 → 1.2.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.
Files changed (40) hide show
  1. package/README.md +12 -17
  2. package/dist/audit/broken-links.d.ts +21 -21
  3. package/dist/audit/broken-links.js +86 -86
  4. package/dist/auth.d.ts +11 -11
  5. package/dist/auth.js +222 -222
  6. package/dist/cli.js +868 -806
  7. package/dist/comment.d.ts +21 -21
  8. package/dist/comment.js +125 -125
  9. package/dist/config.d.ts +13 -0
  10. package/dist/config.js +43 -3
  11. package/dist/crawler.d.ts +36 -36
  12. package/dist/crawler.js +357 -357
  13. package/dist/e2e.d.ts +49 -49
  14. package/dist/e2e.js +565 -565
  15. package/dist/entitlement.d.ts +11 -11
  16. package/dist/entitlement.js +90 -90
  17. package/dist/init.js +87 -87
  18. package/dist/multi-viewport.d.ts +31 -31
  19. package/dist/multi-viewport.js +298 -298
  20. package/dist/playwright-runner.d.ts +16 -16
  21. package/dist/playwright-runner.js +208 -208
  22. package/dist/report-markdown.d.ts +39 -39
  23. package/dist/report-markdown.js +386 -386
  24. package/dist/security-audit.d.ts +9 -9
  25. package/dist/security-audit.js +64 -64
  26. package/dist/serve.d.ts +13 -13
  27. package/dist/serve.js +196 -196
  28. package/dist/trend.d.ts +50 -50
  29. package/dist/trend.js +148 -148
  30. package/dist/verification-core/index.d.ts +3 -3
  31. package/dist/verification-core/index.js +19 -19
  32. package/dist/verification-core/report.d.ts +14 -14
  33. package/dist/verification-core/report.js +409 -409
  34. package/dist/verification-core/tier-policy.d.ts +13 -13
  35. package/dist/verification-core/tier-policy.js +60 -60
  36. package/dist/verification-core/types.d.ts +108 -108
  37. package/dist/verification-core/types.js +2 -2
  38. package/dist/visual-diff.d.ts +26 -26
  39. package/dist/visual-diff.js +178 -178
  40. package/package.json +1 -1
package/dist/comment.d.ts CHANGED
@@ -1,21 +1,21 @@
1
- import type { TrendDelta } from "./trend.js";
2
- interface LaxyResult {
3
- grade: string;
4
- lighthouse: {
5
- performance: number;
6
- accessibility: number;
7
- seo: number;
8
- bestPractices: number;
9
- runs: number;
10
- } | null;
11
- thresholds: {
12
- performance: number;
13
- accessibility: number;
14
- seo: number;
15
- bestPractices: number;
16
- };
17
- exitCode: number;
18
- config_fail_on: string;
19
- }
20
- export declare function postPRComment(result: LaxyResult, trend?: TrendDelta | null): Promise<void>;
21
- export {};
1
+ import type { TrendDelta } from "./trend.js";
2
+ interface LaxyResult {
3
+ grade: string;
4
+ lighthouse: {
5
+ performance: number;
6
+ accessibility: number;
7
+ seo: number;
8
+ bestPractices: number;
9
+ runs: number;
10
+ } | null;
11
+ thresholds: {
12
+ performance: number;
13
+ accessibility: number;
14
+ seo: number;
15
+ bestPractices: number;
16
+ };
17
+ exitCode: number;
18
+ config_fail_on: string;
19
+ }
20
+ export declare function postPRComment(result: LaxyResult, trend?: TrendDelta | null): Promise<void>;
21
+ export {};
package/dist/comment.js CHANGED
@@ -1,106 +1,106 @@
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.postPRComment = postPRComment;
37
- const fs = __importStar(require("node:fs"));
38
- const github_js_1 = require("./github.js");
39
- const trend_js_1 = require("./trend.js");
40
- function renderTrendTable(delta) {
41
- const lines = [
42
- "| Check | This PR | vs Base |",
43
- "|---|---|---|",
44
- ];
45
- const gradeUp = (0, trend_js_1.gradeIndex)(delta.grade.current) > (0, trend_js_1.gradeIndex)(delta.grade.base);
46
- const gradeDown = (0, trend_js_1.gradeIndex)(delta.grade.current) < (0, trend_js_1.gradeIndex)(delta.grade.base);
47
- const gradeTrendStr = gradeUp ? ` ↑ (was ${delta.grade.base})` :
48
- gradeDown ? ` ↓ (was ${delta.grade.base})` :
49
- "";
50
- lines.push(`| Grade | **${delta.grade.current}**${gradeTrendStr} | ${delta.grade.base} |`);
51
- function fmtDelta(d) {
52
- if (d === null)
53
- return "—";
54
- if (d > 0)
55
- return `+${d}`;
56
- if (d < 0)
57
- return `${d}`;
58
- return "—";
59
- }
60
- if (delta.performance.current !== null) {
61
- lines.push(`| Performance | ${delta.performance.current} | ${fmtDelta(delta.performance.delta)} |`);
62
- }
63
- if (delta.accessibility.current !== null) {
64
- lines.push(`| Accessibility | ${delta.accessibility.current} | ${fmtDelta(delta.accessibility.delta)} |`);
65
- }
66
- if (delta.seo.current !== null) {
67
- lines.push(`| SEO | ${delta.seo.current} | ${fmtDelta(delta.seo.delta)} |`);
68
- }
69
- if (delta.bestPractices.current !== null) {
70
- lines.push(`| Best Practices | ${delta.bestPractices.current} | ${fmtDelta(delta.bestPractices.delta)} |`);
71
- }
72
- if (delta.e2e.current !== null) {
73
- lines.push(`| E2E | ${delta.e2e.current} | ${delta.e2e.base ?? "—"} (base) |`);
74
- }
75
- return `${lines.join("\n")}\n`;
76
- }
77
- async function postPRComment(result, trend) {
78
- const ctx = (0, github_js_1.getGitHubContext)();
79
- if (!ctx || ctx.eventName !== "pull_request")
80
- return;
81
- let prNumber = 0;
82
- if (ctx.eventPath && fs.existsSync(ctx.eventPath)) {
83
- const event = JSON.parse(fs.readFileSync(ctx.eventPath, "utf-8"));
84
- prNumber = event.pull_request?.number ?? 0;
85
- }
86
- if (!prNumber)
87
- return;
88
- const grade = result.grade ?? "Unverified";
89
- const lh = result.lighthouse;
90
- const t = result.thresholds;
91
- let lhTable = "";
92
- if (lh) {
93
- lhTable = `| Performance | Accessibility | SEO | Best Practices |\n|---|---|---|---|\n| ${lh.performance} / ${t.performance} | ${lh.accessibility} / ${t.accessibility} | ${lh.seo} / ${t.seo} | ${lh.bestPractices} / ${t.bestPractices} |\n\n`;
94
- }
95
- const trendTable = trend ? `${renderTrendTable(trend)}\n` : "";
96
- const passed = result.exitCode === 0;
97
- const statusLabel = passed ? "[PASS]" : "[HOLD]";
98
- const headline = passed ? "No deployment blockers found" : "Deployment blockers found";
99
- const summary = passed
100
- ? `This PR passed the current verification gate. Grade summary: **${grade}**.`
101
- : grade === "Unverified"
102
- ? "The production build failed or verification could not complete, so this PR should be held."
103
- : `This PR did not clear the current verification gate. Grade summary: **${grade}**.`;
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.postPRComment = postPRComment;
37
+ const fs = __importStar(require("node:fs"));
38
+ const github_js_1 = require("./github.js");
39
+ const trend_js_1 = require("./trend.js");
40
+ function renderTrendTable(delta) {
41
+ const lines = [
42
+ "| Check | This PR | vs Base |",
43
+ "|---|---|---|",
44
+ ];
45
+ const gradeUp = (0, trend_js_1.gradeIndex)(delta.grade.current) > (0, trend_js_1.gradeIndex)(delta.grade.base);
46
+ const gradeDown = (0, trend_js_1.gradeIndex)(delta.grade.current) < (0, trend_js_1.gradeIndex)(delta.grade.base);
47
+ const gradeTrendStr = gradeUp ? ` ↑ (was ${delta.grade.base})` :
48
+ gradeDown ? ` ↓ (was ${delta.grade.base})` :
49
+ "";
50
+ lines.push(`| Grade | **${delta.grade.current}**${gradeTrendStr} | ${delta.grade.base} |`);
51
+ function fmtDelta(d) {
52
+ if (d === null)
53
+ return "—";
54
+ if (d > 0)
55
+ return `+${d}`;
56
+ if (d < 0)
57
+ return `${d}`;
58
+ return "—";
59
+ }
60
+ if (delta.performance.current !== null) {
61
+ lines.push(`| Performance | ${delta.performance.current} | ${fmtDelta(delta.performance.delta)} |`);
62
+ }
63
+ if (delta.accessibility.current !== null) {
64
+ lines.push(`| Accessibility | ${delta.accessibility.current} | ${fmtDelta(delta.accessibility.delta)} |`);
65
+ }
66
+ if (delta.seo.current !== null) {
67
+ lines.push(`| SEO | ${delta.seo.current} | ${fmtDelta(delta.seo.delta)} |`);
68
+ }
69
+ if (delta.bestPractices.current !== null) {
70
+ lines.push(`| Best Practices | ${delta.bestPractices.current} | ${fmtDelta(delta.bestPractices.delta)} |`);
71
+ }
72
+ if (delta.e2e.current !== null) {
73
+ lines.push(`| E2E | ${delta.e2e.current} | ${delta.e2e.base ?? "—"} (base) |`);
74
+ }
75
+ return `${lines.join("\n")}\n`;
76
+ }
77
+ async function postPRComment(result, trend) {
78
+ const ctx = (0, github_js_1.getGitHubContext)();
79
+ if (!ctx || ctx.eventName !== "pull_request")
80
+ return;
81
+ let prNumber = 0;
82
+ if (ctx.eventPath && fs.existsSync(ctx.eventPath)) {
83
+ const event = JSON.parse(fs.readFileSync(ctx.eventPath, "utf-8"));
84
+ prNumber = event.pull_request?.number ?? 0;
85
+ }
86
+ if (!prNumber)
87
+ return;
88
+ const grade = result.grade ?? "Unverified";
89
+ const lh = result.lighthouse;
90
+ const t = result.thresholds;
91
+ let lhTable = "";
92
+ if (lh) {
93
+ lhTable = `| Performance | Accessibility | SEO | Best Practices |\n|---|---|---|---|\n| ${lh.performance} / ${t.performance} | ${lh.accessibility} / ${t.accessibility} | ${lh.seo} / ${t.seo} | ${lh.bestPractices} / ${t.bestPractices} |\n\n`;
94
+ }
95
+ const trendTable = trend ? `${renderTrendTable(trend)}\n` : "";
96
+ const passed = result.exitCode === 0;
97
+ const statusLabel = passed ? "[PASS]" : "[HOLD]";
98
+ const headline = passed ? "No deployment blockers found" : "Deployment blockers found";
99
+ const summary = passed
100
+ ? `This PR passed the current verification gate. Grade summary: **${grade}**.`
101
+ : grade === "Unverified"
102
+ ? "The production build failed or verification could not complete, so this PR should be held."
103
+ : `This PR did not clear the current verification gate. Grade summary: **${grade}**.`;
104
104
  const body = `## ${statusLabel} Laxy Verify: ${headline}
105
105
 
106
106
  ${summary}
@@ -110,25 +110,25 @@ ${trendTable}${lhTable}**Fail-on threshold**: ${result.config_fail_on ?? "bronze
110
110
  Grade remains available for automation, but this check should be read first as a merge and release blocker gate.
111
111
 
112
112
  ---
113
- [Open laxy-verify docs](https://github.com/SUNgm24/Laxy/tree/main/laxy-verify)`;
114
- const [owner, repo] = ctx.repository.split("/");
115
- const url = `https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/comments`;
116
- try {
117
- const res = await fetch(url, {
118
- method: "POST",
119
- headers: {
120
- Authorization: `Bearer ${ctx.token}`,
121
- Accept: "application/vnd.github.v3+json",
122
- "Content-Type": "application/json",
123
- },
124
- body: JSON.stringify({ body }),
125
- });
126
- if (!res.ok) {
127
- console.warn(`GitHub PR comment API returned ${res.status}; skipping comment.`);
128
- return;
129
- }
130
- }
131
- catch (err) {
132
- console.warn(`GitHub PR comment request failed: ${err instanceof Error ? err.message : String(err)}`);
133
- }
134
- }
113
+ [Open laxy-verify docs](https://github.com/SUNgm24/Laxy/tree/main/laxy-verify)`;
114
+ const [owner, repo] = ctx.repository.split("/");
115
+ const url = `https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/comments`;
116
+ try {
117
+ const res = await fetch(url, {
118
+ method: "POST",
119
+ headers: {
120
+ Authorization: `Bearer ${ctx.token}`,
121
+ Accept: "application/vnd.github.v3+json",
122
+ "Content-Type": "application/json",
123
+ },
124
+ body: JSON.stringify({ body }),
125
+ });
126
+ if (!res.ok) {
127
+ console.warn(`GitHub PR comment API returned ${res.status}; skipping comment.`);
128
+ return;
129
+ }
130
+ }
131
+ catch (err) {
132
+ console.warn(`GitHub PR comment request failed: ${err instanceof Error ? err.message : String(err)}`);
133
+ }
134
+ }
package/dist/config.d.ts CHANGED
@@ -38,6 +38,18 @@ export interface LaxyConfig {
38
38
  export declare class ConfigParseError extends Error {
39
39
  constructor(msg: string);
40
40
  }
41
+ export interface TeamThresholdsConfig {
42
+ performance: number;
43
+ accessibility: number;
44
+ seo: number;
45
+ best_practices: number;
46
+ fail_on: FailOn;
47
+ }
48
+ /**
49
+ * 로그인된 CLI 토큰으로 팀 공통 임계값을 서버에서 가져온다.
50
+ * 토큰 없음 / 팀 없음 / 네트워크 오류 시 null 반환 (graceful degradation).
51
+ */
52
+ export declare function fetchTeamThresholds(): Promise<TeamThresholdsConfig | null>;
41
53
  export interface LoadConfigOptions {
42
54
  dir: string;
43
55
  configPath?: string;
@@ -46,6 +58,7 @@ export interface LoadConfigOptions {
46
58
  skipLighthouse?: boolean;
47
59
  };
48
60
  ciMode: boolean;
61
+ teamThresholds?: TeamThresholdsConfig | null;
49
62
  }
50
63
  export declare function loadConfig(options: LoadConfigOptions): LaxyConfig & {
51
64
  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: "",
@@ -164,12 +166,46 @@ function parseYaml(filePath) {
164
166
  }
165
167
  return result;
166
168
  }
169
+ /**
170
+ * 로그인된 CLI 토큰으로 팀 공통 임계값을 서버에서 가져온다.
171
+ * 토큰 없음 / 팀 없음 / 네트워크 오류 시 null 반환 (graceful degradation).
172
+ */
173
+ async function fetchTeamThresholds() {
174
+ const token = (0, auth_js_1.loadToken)();
175
+ if (!token)
176
+ return null;
177
+ try {
178
+ const res = await fetch(`${auth_js_1.LAXY_API_URL}/api/v1/team-thresholds`, {
179
+ headers: { Authorization: `Bearer ${token}` },
180
+ signal: AbortSignal.timeout(5000),
181
+ });
182
+ if (!res.ok)
183
+ return null;
184
+ const data = (await res.json());
185
+ return data.thresholds ?? null;
186
+ }
187
+ catch {
188
+ return null;
189
+ }
190
+ }
167
191
  function loadConfig(options) {
168
192
  const configPath = options.configPath ?? path.join(options.dir, ".laxy.yml");
169
193
  let base = {};
170
- if (fs.existsSync(configPath)) {
194
+ const hasLocalConfig = fs.existsSync(configPath);
195
+ if (hasLocalConfig) {
171
196
  base = parseYaml(configPath);
172
197
  }
198
+ // 팀 임계값: 로컬 .laxy.yml에 thresholds가 없을 때만 적용
199
+ const team = options.teamThresholds ?? null;
200
+ const teamThresholdFallback = (!hasLocalConfig || !base.thresholds) && team
201
+ ? {
202
+ performance: team.performance,
203
+ accessibility: team.accessibility,
204
+ seo: team.seo,
205
+ bestPractices: team.best_practices,
206
+ }
207
+ : {};
208
+ const teamFailOnFallback = (!hasLocalConfig || !base.fail_on) && team ? team.fail_on : undefined;
173
209
  const config = {
174
210
  ...DEFAULT_CONFIG,
175
211
  framework: base.framework ?? DEFAULT_CONFIG.framework,
@@ -180,14 +216,18 @@ function loadConfig(options) {
180
216
  build_timeout: base.build_timeout ?? DEFAULT_CONFIG.build_timeout,
181
217
  dev_timeout: base.dev_timeout ?? DEFAULT_CONFIG.dev_timeout,
182
218
  lighthouse_runs: base.lighthouse_runs ?? DEFAULT_CONFIG.lighthouse_runs,
183
- fail_on: base.fail_on ?? DEFAULT_CONFIG.fail_on,
219
+ fail_on: base.fail_on ?? teamFailOnFallback ?? DEFAULT_CONFIG.fail_on,
184
220
  scenarios: base.scenarios,
185
221
  crawl: base.crawl ?? DEFAULT_CONFIG.crawl,
186
222
  max_crawl_depth: base.max_crawl_depth ?? DEFAULT_CONFIG.max_crawl_depth,
187
223
  max_crawl_pages: base.max_crawl_pages ?? DEFAULT_CONFIG.max_crawl_pages,
188
224
  browsers: base.browsers ?? DEFAULT_CONFIG.browsers,
189
225
  };
190
- config.thresholds = { ...DEFAULT_CONFIG.thresholds, ...(base.thresholds ?? {}) };
226
+ config.thresholds = {
227
+ ...DEFAULT_CONFIG.thresholds,
228
+ ...teamThresholdFallback,
229
+ ...(base.thresholds ?? {}),
230
+ };
191
231
  // CLI flag overrides
192
232
  if (options.cliFlags?.failOn !== undefined) {
193
233
  if (!VALID_FAIL_ON.includes(options.cliFlags.failOn)) {
package/dist/crawler.d.ts CHANGED
@@ -1,36 +1,36 @@
1
- import type { E2EScenario } from "./e2e.js";
2
- import type { VerificationTier } from "./verification-core/types.js";
3
- export interface CrawlPage {
4
- url: string;
5
- path: string;
6
- title: string;
7
- forms: CrawlForm[];
8
- buttons: string[];
9
- internalLinks: string[];
10
- hasConsoleErrors: boolean;
11
- consoleErrors: string[];
12
- }
13
- export interface CrawlForm {
14
- selector: string;
15
- inputs: {
16
- selector: string;
17
- type: string;
18
- placeholder?: string;
19
- }[];
20
- submitSelector?: string;
21
- }
22
- export interface CrawlResult {
23
- pages: CrawlPage[];
24
- totalLinks: number;
25
- crawledCount: number;
26
- }
27
- export interface CrawlOptions {
28
- maxDepth?: number;
29
- maxPages?: number;
30
- timeout?: number;
31
- }
32
- export declare function crawlApp(baseUrl: string, options?: CrawlOptions): Promise<CrawlResult>;
33
- /**
34
- * Generate E2E scenarios from crawl results.
35
- */
36
- export declare function buildScenariosFromCrawl(crawlResult: CrawlResult, tier: VerificationTier): E2EScenario[];
1
+ import type { E2EScenario } from "./e2e.js";
2
+ import type { VerificationTier } from "./verification-core/types.js";
3
+ export interface CrawlPage {
4
+ url: string;
5
+ path: string;
6
+ title: string;
7
+ forms: CrawlForm[];
8
+ buttons: string[];
9
+ internalLinks: string[];
10
+ hasConsoleErrors: boolean;
11
+ consoleErrors: string[];
12
+ }
13
+ export interface CrawlForm {
14
+ selector: string;
15
+ inputs: {
16
+ selector: string;
17
+ type: string;
18
+ placeholder?: string;
19
+ }[];
20
+ submitSelector?: string;
21
+ }
22
+ export interface CrawlResult {
23
+ pages: CrawlPage[];
24
+ totalLinks: number;
25
+ crawledCount: number;
26
+ }
27
+ export interface CrawlOptions {
28
+ maxDepth?: number;
29
+ maxPages?: number;
30
+ timeout?: number;
31
+ }
32
+ export declare function crawlApp(baseUrl: string, options?: CrawlOptions): Promise<CrawlResult>;
33
+ /**
34
+ * Generate E2E scenarios from crawl results.
35
+ */
36
+ export declare function buildScenariosFromCrawl(crawlResult: CrawlResult, tier: VerificationTier): E2EScenario[];