@vibecheckai/cli 3.3.0 → 3.5.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/bin/registry.js +389 -269
- package/bin/runners/cli-utils.js +2 -33
- package/bin/runners/context/generators/cursor.js +49 -2
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +849 -0
- package/bin/runners/lib/analyzers.js +599 -142
- package/bin/runners/lib/audit-logger.js +532 -0
- package/bin/runners/lib/authority/authorities/architecture.js +364 -0
- package/bin/runners/lib/authority/authorities/compliance.js +341 -0
- package/bin/runners/lib/authority/authorities/human.js +343 -0
- package/bin/runners/lib/authority/authorities/quality.js +420 -0
- package/bin/runners/lib/authority/authorities/security.js +228 -0
- package/bin/runners/lib/authority/index.js +293 -0
- package/bin/runners/lib/authority-badge.js +425 -425
- package/bin/runners/lib/bundle/bundle-intelligence.js +846 -0
- package/bin/runners/lib/cli-charts.js +368 -0
- package/bin/runners/lib/cli-config-display.js +405 -0
- package/bin/runners/lib/cli-demo.js +275 -0
- package/bin/runners/lib/cli-errors.js +438 -0
- package/bin/runners/lib/cli-help-formatter.js +439 -0
- package/bin/runners/lib/cli-interactive-menu.js +509 -0
- package/bin/runners/lib/cli-prompts.js +441 -0
- package/bin/runners/lib/cli-scan-cards.js +362 -0
- package/bin/runners/lib/compliance-reporter.js +710 -0
- package/bin/runners/lib/conductor/index.js +671 -0
- package/bin/runners/lib/easy/README.md +123 -0
- package/bin/runners/lib/easy/index.js +140 -0
- package/bin/runners/lib/easy/interactive-wizard.js +788 -0
- package/bin/runners/lib/easy/one-click-firewall.js +564 -0
- package/bin/runners/lib/easy/zero-config-reality.js +714 -0
- package/bin/runners/lib/engines/accessibility-engine.js +218 -18
- package/bin/runners/lib/engines/api-consistency-engine.js +335 -30
- package/bin/runners/lib/engines/async-patterns-engine.js +444 -0
- package/bin/runners/lib/engines/bundle-size-engine.js +433 -0
- package/bin/runners/lib/engines/confidence-scoring.js +276 -0
- package/bin/runners/lib/engines/context-detection.js +264 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +292 -27
- package/bin/runners/lib/engines/database-patterns-engine.js +429 -0
- package/bin/runners/lib/engines/duplicate-code-engine.js +354 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +127 -17
- package/bin/runners/lib/engines/env-variables-engine.js +458 -0
- package/bin/runners/lib/engines/error-handling-engine.js +437 -0
- package/bin/runners/lib/engines/false-positive-prevention.js +630 -0
- package/bin/runners/lib/engines/framework-adapters/index.js +607 -0
- package/bin/runners/lib/engines/framework-detection.js +508 -0
- package/bin/runners/lib/engines/import-order-engine.js +429 -0
- package/bin/runners/lib/engines/mock-data-engine.js +53 -10
- package/bin/runners/lib/engines/naming-conventions-engine.js +544 -0
- package/bin/runners/lib/engines/noise-reduction-engine.js +452 -0
- package/bin/runners/lib/engines/orchestrator.js +334 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +176 -36
- package/bin/runners/lib/engines/react-patterns-engine.js +457 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +382 -54
- package/bin/runners/lib/engines/type-aware-engine.js +263 -39
- package/bin/runners/lib/engines/vibecheck-engines/index.js +122 -13
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +806 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +373 -73
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +577 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +543 -0
- package/bin/runners/lib/engines/vibecheck-engines.js +514 -0
- package/bin/runners/lib/enhanced-features/index.js +305 -0
- package/bin/runners/lib/enhanced-output.js +631 -0
- package/bin/runners/lib/enterprise.js +300 -0
- package/bin/runners/lib/entitlements-v2.js +161 -478
- package/bin/runners/lib/firewall/command-validator.js +351 -0
- package/bin/runners/lib/firewall/config.js +341 -0
- package/bin/runners/lib/firewall/content-validator.js +519 -0
- package/bin/runners/lib/firewall/index.js +101 -0
- package/bin/runners/lib/firewall/path-validator.js +256 -0
- package/bin/runners/lib/html-proof-report.js +350 -700
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +817 -0
- package/bin/runners/lib/mcp-utils.js +425 -0
- package/bin/runners/lib/missions/plan.js +46 -6
- package/bin/runners/lib/missions/templates.js +232 -0
- package/bin/runners/lib/output/index.js +1022 -0
- package/bin/runners/lib/policy-engine.js +652 -0
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +333 -0
- package/bin/runners/lib/polish/autofix/async-handlers.js +273 -0
- package/bin/runners/lib/polish/autofix/dead-code.js +280 -0
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +344 -0
- package/bin/runners/lib/polish/autofix/index.js +200 -0
- package/bin/runners/lib/polish/autofix/remove-consoles.js +209 -0
- package/bin/runners/lib/polish/autofix/strengthen-types.js +245 -0
- package/bin/runners/lib/polish/backend-checks.js +148 -0
- package/bin/runners/lib/polish/documentation-checks.js +111 -0
- package/bin/runners/lib/polish/frontend-checks.js +168 -0
- package/bin/runners/lib/polish/index.js +71 -0
- package/bin/runners/lib/polish/infrastructure-checks.js +131 -0
- package/bin/runners/lib/polish/library-detection.js +175 -0
- package/bin/runners/lib/polish/performance-checks.js +100 -0
- package/bin/runners/lib/polish/security-checks.js +148 -0
- package/bin/runners/lib/polish/utils.js +203 -0
- package/bin/runners/lib/prompt-builder.js +540 -0
- package/bin/runners/lib/proof-certificate.js +634 -0
- package/bin/runners/lib/reality/accessibility-audit.js +946 -0
- package/bin/runners/lib/reality/api-contract-validator.js +1012 -0
- package/bin/runners/lib/reality/chaos-engineering.js +1084 -0
- package/bin/runners/lib/reality/performance-tracker.js +1077 -0
- package/bin/runners/lib/reality/scenario-generator.js +1404 -0
- package/bin/runners/lib/reality/visual-regression.js +852 -0
- package/bin/runners/lib/reality-profiler.js +717 -0
- package/bin/runners/lib/replay/flight-recorder-viewer.js +1160 -0
- package/bin/runners/lib/review/ai-code-review.js +832 -0
- package/bin/runners/lib/rules/custom-rule-engine.js +985 -0
- package/bin/runners/lib/sbom-generator.js +641 -0
- package/bin/runners/lib/scan-output-enhanced.js +512 -0
- package/bin/runners/lib/scan-output.js +65 -19
- package/bin/runners/lib/security/owasp-scanner.js +939 -0
- package/bin/runners/lib/ship-output.js +18 -25
- package/bin/runners/lib/terminal-ui.js +113 -1
- package/bin/runners/lib/unified-cli-output.js +603 -430
- package/bin/runners/lib/upsell.js +90 -338
- package/bin/runners/lib/validators/contract-validator.js +283 -0
- package/bin/runners/lib/validators/dead-export-detector.js +279 -0
- package/bin/runners/lib/validators/dep-audit.js +245 -0
- package/bin/runners/lib/validators/env-validator.js +319 -0
- package/bin/runners/lib/validators/index.js +120 -0
- package/bin/runners/lib/validators/license-checker.js +252 -0
- package/bin/runners/lib/validators/route-validator.js +290 -0
- package/bin/runners/runAIAgent.js +5 -10
- package/bin/runners/runAgent.js +3 -0
- package/bin/runners/runApprove.js +1233 -1200
- package/bin/runners/runAuth.js +22 -1
- package/bin/runners/runAuthority.js +528 -0
- package/bin/runners/runCheckpoint.js +4 -24
- package/bin/runners/runClassify.js +862 -859
- package/bin/runners/runConductor.js +772 -0
- package/bin/runners/runContainer.js +366 -0
- package/bin/runners/runContext.js +3 -0
- package/bin/runners/runDoctor.js +28 -41
- package/bin/runners/runEasy.js +410 -0
- package/bin/runners/runFirewall.js +3 -0
- package/bin/runners/runFirewallHook.js +3 -0
- package/bin/runners/runFix.js +76 -66
- package/bin/runners/runGuard.js +411 -18
- package/bin/runners/runIaC.js +372 -0
- package/bin/runners/runInit.js +10 -60
- package/bin/runners/runMcp.js +11 -12
- package/bin/runners/runPolish.js +240 -64
- package/bin/runners/runPromptFirewall.js +5 -12
- package/bin/runners/runProve.js +20 -55
- package/bin/runners/runReality.js +68 -59
- package/bin/runners/runReport.js +31 -5
- package/bin/runners/runRuntime.js +5 -8
- package/bin/runners/runScan.js +194 -1273
- package/bin/runners/runShip.js +695 -47
- package/bin/runners/runTruth.js +3 -0
- package/bin/runners/runValidate.js +7 -11
- package/bin/runners/runVibe.js +791 -0
- package/bin/runners/runWatch.js +14 -23
- package/bin/vibecheck.js +179 -65
- package/mcp-server/index.js +202 -636
- package/mcp-server/lib/api-client.cjs +7 -299
- package/mcp-server/package.json +1 -1
- package/mcp-server/tier-auth.js +175 -574
- package/mcp-server/tools-v3.js +800 -505
- package/mcp-server/tools.js +495 -0
- package/package.json +1 -1
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +0 -291
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +0 -83
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +0 -198
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +0 -275
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +0 -167
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +0 -140
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +0 -234
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +0 -78
- package/mcp-server/index-v1.js +0 -698
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reality Profiler - World-Class Performance & Coverage Analysis
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* 1. Core Web Vitals tracking (LCP, FID, CLS, TTFB)
|
|
6
|
+
* 2. Coverage Map visualization
|
|
7
|
+
* 3. User Journey Analysis
|
|
8
|
+
* 4. Evidence Pack generation
|
|
9
|
+
* 5. Performance Budget checking
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
const path = require("path");
|
|
16
|
+
const crypto = require("crypto");
|
|
17
|
+
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
19
|
+
// CORE WEB VITALS TRACKING
|
|
20
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
21
|
+
|
|
22
|
+
const PERFORMANCE_BUDGETS = {
|
|
23
|
+
lcp: { good: 2500, needsWork: 4000 }, // Largest Contentful Paint (ms)
|
|
24
|
+
fid: { good: 100, needsWork: 300 }, // First Input Delay (ms)
|
|
25
|
+
cls: { good: 0.1, needsWork: 0.25 }, // Cumulative Layout Shift
|
|
26
|
+
ttfb: { good: 800, needsWork: 1800 }, // Time to First Byte (ms)
|
|
27
|
+
fcp: { good: 1800, needsWork: 3000 }, // First Contentful Paint (ms)
|
|
28
|
+
tti: { good: 3800, needsWork: 7300 }, // Time to Interactive (ms)
|
|
29
|
+
tbt: { good: 200, needsWork: 600 }, // Total Blocking Time (ms)
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Track Core Web Vitals from a Playwright page
|
|
34
|
+
*/
|
|
35
|
+
async function trackWebVitals(page, url) {
|
|
36
|
+
const metrics = {
|
|
37
|
+
url,
|
|
38
|
+
timestamp: new Date().toISOString(),
|
|
39
|
+
navigationStart: Date.now(),
|
|
40
|
+
vitals: {},
|
|
41
|
+
resources: [],
|
|
42
|
+
errors: [],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// Inject web-vitals tracking script
|
|
47
|
+
await page.addInitScript(() => {
|
|
48
|
+
window.__webVitals = {
|
|
49
|
+
lcp: null,
|
|
50
|
+
fid: null,
|
|
51
|
+
cls: 0,
|
|
52
|
+
fcp: null,
|
|
53
|
+
ttfb: null,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Observe LCP
|
|
57
|
+
new PerformanceObserver((list) => {
|
|
58
|
+
const entries = list.getEntries();
|
|
59
|
+
window.__webVitals.lcp = entries[entries.length - 1]?.renderTime ||
|
|
60
|
+
entries[entries.length - 1]?.loadTime;
|
|
61
|
+
}).observe({ type: "largest-contentful-paint", buffered: true });
|
|
62
|
+
|
|
63
|
+
// Observe FID
|
|
64
|
+
new PerformanceObserver((list) => {
|
|
65
|
+
const entry = list.getEntries()[0];
|
|
66
|
+
window.__webVitals.fid = entry?.processingStart - entry?.startTime;
|
|
67
|
+
}).observe({ type: "first-input", buffered: true });
|
|
68
|
+
|
|
69
|
+
// Observe CLS
|
|
70
|
+
new PerformanceObserver((list) => {
|
|
71
|
+
for (const entry of list.getEntries()) {
|
|
72
|
+
if (!entry.hadRecentInput) {
|
|
73
|
+
window.__webVitals.cls += entry.value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}).observe({ type: "layout-shift", buffered: true });
|
|
77
|
+
|
|
78
|
+
// Navigation timing
|
|
79
|
+
window.addEventListener("load", () => {
|
|
80
|
+
const timing = performance.getEntriesByType("navigation")[0];
|
|
81
|
+
if (timing) {
|
|
82
|
+
window.__webVitals.ttfb = timing.responseStart - timing.requestStart;
|
|
83
|
+
window.__webVitals.fcp = timing.domContentLoadedEventEnd - timing.startTime;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Navigate and wait for load
|
|
89
|
+
const response = await page.goto(url, {
|
|
90
|
+
waitUntil: "networkidle",
|
|
91
|
+
timeout: 30000,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Wait for LCP to stabilize
|
|
95
|
+
await page.waitForTimeout(2000);
|
|
96
|
+
|
|
97
|
+
// Collect metrics
|
|
98
|
+
metrics.vitals = await page.evaluate(() => window.__webVitals);
|
|
99
|
+
metrics.statusCode = response?.status() || 0;
|
|
100
|
+
metrics.loadTime = Date.now() - metrics.navigationStart;
|
|
101
|
+
|
|
102
|
+
// Collect resource timing
|
|
103
|
+
metrics.resources = await page.evaluate(() => {
|
|
104
|
+
return performance.getEntriesByType("resource").map((r) => ({
|
|
105
|
+
name: r.name,
|
|
106
|
+
type: r.initiatorType,
|
|
107
|
+
size: r.transferSize,
|
|
108
|
+
duration: r.duration,
|
|
109
|
+
}));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Calculate derived metrics
|
|
113
|
+
metrics.totalResourceSize = metrics.resources.reduce((sum, r) => sum + (r.size || 0), 0);
|
|
114
|
+
metrics.resourceCount = metrics.resources.length;
|
|
115
|
+
|
|
116
|
+
} catch (error) {
|
|
117
|
+
metrics.errors.push(error.message);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return metrics;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Grade a metric against budget
|
|
125
|
+
*/
|
|
126
|
+
function gradeMetric(value, budget) {
|
|
127
|
+
if (value === null || value === undefined) return "unknown";
|
|
128
|
+
if (value <= budget.good) return "good";
|
|
129
|
+
if (value <= budget.needsWork) return "needs-improvement";
|
|
130
|
+
return "poor";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Analyze performance metrics
|
|
135
|
+
*/
|
|
136
|
+
function analyzePerformance(metrics) {
|
|
137
|
+
const analysis = {
|
|
138
|
+
overall: "good",
|
|
139
|
+
grades: {},
|
|
140
|
+
issues: [],
|
|
141
|
+
recommendations: [],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Grade each vital
|
|
145
|
+
for (const [key, budget] of Object.entries(PERFORMANCE_BUDGETS)) {
|
|
146
|
+
const value = metrics.vitals?.[key];
|
|
147
|
+
const grade = gradeMetric(value, budget);
|
|
148
|
+
analysis.grades[key] = { value, grade, budget };
|
|
149
|
+
|
|
150
|
+
if (grade === "poor") {
|
|
151
|
+
analysis.overall = "poor";
|
|
152
|
+
analysis.issues.push({
|
|
153
|
+
metric: key,
|
|
154
|
+
value,
|
|
155
|
+
threshold: budget.needsWork,
|
|
156
|
+
severity: "BLOCK",
|
|
157
|
+
});
|
|
158
|
+
} else if (grade === "needs-improvement" && analysis.overall !== "poor") {
|
|
159
|
+
analysis.overall = "needs-improvement";
|
|
160
|
+
analysis.issues.push({
|
|
161
|
+
metric: key,
|
|
162
|
+
value,
|
|
163
|
+
threshold: budget.good,
|
|
164
|
+
severity: "WARN",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Generate recommendations
|
|
170
|
+
if (analysis.grades.lcp?.grade !== "good") {
|
|
171
|
+
analysis.recommendations.push({
|
|
172
|
+
metric: "LCP",
|
|
173
|
+
suggestion: "Optimize largest image/text block, use lazy loading, preload critical resources",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (analysis.grades.cls?.grade !== "good") {
|
|
177
|
+
analysis.recommendations.push({
|
|
178
|
+
metric: "CLS",
|
|
179
|
+
suggestion: "Add explicit dimensions to images/videos, avoid inserting content above existing content",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (analysis.grades.ttfb?.grade !== "good") {
|
|
183
|
+
analysis.recommendations.push({
|
|
184
|
+
metric: "TTFB",
|
|
185
|
+
suggestion: "Optimize server response time, use CDN, enable caching",
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return analysis;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Render performance report
|
|
194
|
+
*/
|
|
195
|
+
function renderPerformanceReport(metrics, analysis) {
|
|
196
|
+
const lines = [];
|
|
197
|
+
const c = {
|
|
198
|
+
reset: "\x1b[0m",
|
|
199
|
+
bold: "\x1b[1m",
|
|
200
|
+
dim: "\x1b[2m",
|
|
201
|
+
green: "\x1b[32m",
|
|
202
|
+
yellow: "\x1b[33m",
|
|
203
|
+
red: "\x1b[31m",
|
|
204
|
+
cyan: "\x1b[36m",
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const gradeColor = (grade) => {
|
|
208
|
+
if (grade === "good") return c.green;
|
|
209
|
+
if (grade === "needs-improvement") return c.yellow;
|
|
210
|
+
return c.red;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const gradeIcon = (grade) => {
|
|
214
|
+
if (grade === "good") return "✓";
|
|
215
|
+
if (grade === "needs-improvement") return "◐";
|
|
216
|
+
if (grade === "poor") return "✗";
|
|
217
|
+
return "?";
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
lines.push("╭" + "─".repeat(68) + "╮");
|
|
221
|
+
lines.push("│" + " ".repeat(20) + `${c.bold}⚡ PERFORMANCE PROFILE${c.reset}` + " ".repeat(20) + "│");
|
|
222
|
+
lines.push("├" + "─".repeat(68) + "┤");
|
|
223
|
+
|
|
224
|
+
// Core Web Vitals
|
|
225
|
+
const vitals = [
|
|
226
|
+
{ key: "lcp", name: "Largest Contentful Paint", unit: "ms" },
|
|
227
|
+
{ key: "fid", name: "First Input Delay", unit: "ms" },
|
|
228
|
+
{ key: "cls", name: "Cumulative Layout Shift", unit: "" },
|
|
229
|
+
{ key: "ttfb", name: "Time to First Byte", unit: "ms" },
|
|
230
|
+
{ key: "fcp", name: "First Contentful Paint", unit: "ms" },
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
for (const vital of vitals) {
|
|
234
|
+
const data = analysis.grades[vital.key];
|
|
235
|
+
if (!data) continue;
|
|
236
|
+
|
|
237
|
+
const color = gradeColor(data.grade);
|
|
238
|
+
const icon = gradeIcon(data.grade);
|
|
239
|
+
const value = data.value !== null ? `${Math.round(data.value)}${vital.unit}` : "N/A";
|
|
240
|
+
const name = vital.name.padEnd(28);
|
|
241
|
+
|
|
242
|
+
lines.push(`│ ${color}${icon}${c.reset} ${name} ${value.padStart(10)} ${c.dim}(< ${data.budget.good}${vital.unit})${c.reset}`.padEnd(78) + "│");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
lines.push("├" + "─".repeat(68) + "┤");
|
|
246
|
+
|
|
247
|
+
// Summary
|
|
248
|
+
const overallColor = gradeColor(analysis.overall);
|
|
249
|
+
const overallIcon = analysis.overall === "good" ? "🎉" : analysis.overall === "poor" ? "🔴" : "🟡";
|
|
250
|
+
lines.push(`│ ${overallIcon} Overall: ${overallColor}${c.bold}${analysis.overall.toUpperCase()}${c.reset}`.padEnd(78) + "│");
|
|
251
|
+
|
|
252
|
+
// Resources summary
|
|
253
|
+
const sizeKB = Math.round(metrics.totalResourceSize / 1024);
|
|
254
|
+
lines.push(`│ 📦 Resources: ${metrics.resourceCount} files, ${sizeKB}KB transferred`.padEnd(69) + "│");
|
|
255
|
+
lines.push(`│ ⏱️ Load Time: ${metrics.loadTime}ms`.padEnd(69) + "│");
|
|
256
|
+
|
|
257
|
+
lines.push("╰" + "─".repeat(68) + "╯");
|
|
258
|
+
|
|
259
|
+
// Recommendations
|
|
260
|
+
if (analysis.recommendations.length > 0) {
|
|
261
|
+
lines.push("");
|
|
262
|
+
lines.push(` ${c.bold}💡 Recommendations:${c.reset}`);
|
|
263
|
+
for (const rec of analysis.recommendations) {
|
|
264
|
+
lines.push(` ${c.cyan}${rec.metric}:${c.reset} ${rec.suggestion}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return lines.join("\n");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
272
|
+
// COVERAGE MAP
|
|
273
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Generate coverage map from crawled routes
|
|
277
|
+
*/
|
|
278
|
+
function generateCoverageMap(visitedRoutes, allRoutes, truthpack = {}) {
|
|
279
|
+
const coverage = {
|
|
280
|
+
visited: new Set(visitedRoutes),
|
|
281
|
+
expected: new Set(allRoutes),
|
|
282
|
+
fromTruthpack: new Set(truthpack.routes?.map((r) => r.path) || []),
|
|
283
|
+
covered: [],
|
|
284
|
+
missing: [],
|
|
285
|
+
unexpected: [],
|
|
286
|
+
stats: {},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Calculate coverage
|
|
290
|
+
for (const route of coverage.expected) {
|
|
291
|
+
if (coverage.visited.has(route)) {
|
|
292
|
+
coverage.covered.push(route);
|
|
293
|
+
} else {
|
|
294
|
+
coverage.missing.push(route);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Find unexpected routes (visited but not in expected)
|
|
299
|
+
for (const route of coverage.visited) {
|
|
300
|
+
if (!coverage.expected.has(route) && !coverage.fromTruthpack.has(route)) {
|
|
301
|
+
coverage.unexpected.push(route);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Calculate stats
|
|
306
|
+
coverage.stats = {
|
|
307
|
+
total: coverage.expected.size,
|
|
308
|
+
covered: coverage.covered.length,
|
|
309
|
+
missing: coverage.missing.length,
|
|
310
|
+
unexpected: coverage.unexpected.length,
|
|
311
|
+
percentage: coverage.expected.size > 0
|
|
312
|
+
? Math.round((coverage.covered.length / coverage.expected.size) * 100)
|
|
313
|
+
: 100,
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
return coverage;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Render ASCII coverage map
|
|
321
|
+
*/
|
|
322
|
+
function renderCoverageMap(coverage) {
|
|
323
|
+
const lines = [];
|
|
324
|
+
const c = {
|
|
325
|
+
reset: "\x1b[0m",
|
|
326
|
+
bold: "\x1b[1m",
|
|
327
|
+
dim: "\x1b[2m",
|
|
328
|
+
green: "\x1b[32m",
|
|
329
|
+
yellow: "\x1b[33m",
|
|
330
|
+
red: "\x1b[31m",
|
|
331
|
+
cyan: "\x1b[36m",
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
lines.push("╭" + "─".repeat(68) + "╮");
|
|
335
|
+
lines.push("│" + " ".repeat(22) + `${c.bold}🗺️ COVERAGE MAP${c.reset}` + " ".repeat(22) + "│");
|
|
336
|
+
lines.push("├" + "─".repeat(68) + "┤");
|
|
337
|
+
|
|
338
|
+
// Coverage bar
|
|
339
|
+
const barWidth = 40;
|
|
340
|
+
const filledWidth = Math.round((coverage.stats.percentage / 100) * barWidth);
|
|
341
|
+
const bar = "█".repeat(filledWidth) + "░".repeat(barWidth - filledWidth);
|
|
342
|
+
const pctColor = coverage.stats.percentage >= 80 ? c.green : coverage.stats.percentage >= 50 ? c.yellow : c.red;
|
|
343
|
+
|
|
344
|
+
lines.push(`│ Coverage: ${pctColor}${bar}${c.reset} ${coverage.stats.percentage}%`.padEnd(78) + "│");
|
|
345
|
+
lines.push("├" + "─".repeat(68) + "┤");
|
|
346
|
+
|
|
347
|
+
// Stats
|
|
348
|
+
lines.push(`│ ${c.green}✓${c.reset} Covered: ${coverage.stats.covered} routes`.padEnd(69) + "│");
|
|
349
|
+
lines.push(`│ ${c.red}✗${c.reset} Missing: ${coverage.stats.missing} routes`.padEnd(69) + "│");
|
|
350
|
+
if (coverage.stats.unexpected > 0) {
|
|
351
|
+
lines.push(`│ ${c.yellow}?${c.reset} Unexpected: ${coverage.stats.unexpected} routes`.padEnd(69) + "│");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Show some missing routes
|
|
355
|
+
if (coverage.missing.length > 0) {
|
|
356
|
+
lines.push("├" + "─".repeat(68) + "┤");
|
|
357
|
+
lines.push(`│ ${c.dim}Missing routes:${c.reset}`.padEnd(69) + "│");
|
|
358
|
+
for (const route of coverage.missing.slice(0, 5)) {
|
|
359
|
+
lines.push(`│ ${c.red}✗${c.reset} ${route}`.padEnd(69) + "│");
|
|
360
|
+
}
|
|
361
|
+
if (coverage.missing.length > 5) {
|
|
362
|
+
lines.push(`│ ${c.dim}... and ${coverage.missing.length - 5} more${c.reset}`.padEnd(69) + "│");
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
lines.push("╰" + "─".repeat(68) + "╯");
|
|
367
|
+
|
|
368
|
+
return lines.join("\n");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
372
|
+
// USER JOURNEY ANALYSIS
|
|
373
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
374
|
+
|
|
375
|
+
const COMMON_JOURNEYS = {
|
|
376
|
+
signup: {
|
|
377
|
+
name: "User Signup",
|
|
378
|
+
icon: "👤",
|
|
379
|
+
steps: [
|
|
380
|
+
{ action: "visit", target: "/signup", description: "Visit signup page" },
|
|
381
|
+
{ action: "fill", target: "email", description: "Enter email" },
|
|
382
|
+
{ action: "fill", target: "password", description: "Enter password" },
|
|
383
|
+
{ action: "click", target: "submit", description: "Submit form" },
|
|
384
|
+
{ action: "verify", target: "/dashboard", description: "Redirected to dashboard" },
|
|
385
|
+
],
|
|
386
|
+
},
|
|
387
|
+
login: {
|
|
388
|
+
name: "User Login",
|
|
389
|
+
icon: "🔐",
|
|
390
|
+
steps: [
|
|
391
|
+
{ action: "visit", target: "/login", description: "Visit login page" },
|
|
392
|
+
{ action: "fill", target: "email", description: "Enter email" },
|
|
393
|
+
{ action: "fill", target: "password", description: "Enter password" },
|
|
394
|
+
{ action: "click", target: "submit", description: "Submit form" },
|
|
395
|
+
{ action: "verify", target: "/dashboard", description: "Redirected to dashboard" },
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
checkout: {
|
|
399
|
+
name: "Checkout Flow",
|
|
400
|
+
icon: "💳",
|
|
401
|
+
steps: [
|
|
402
|
+
{ action: "visit", target: "/pricing", description: "Visit pricing page" },
|
|
403
|
+
{ action: "click", target: "plan", description: "Select plan" },
|
|
404
|
+
{ action: "verify", target: "/checkout", description: "Redirected to checkout" },
|
|
405
|
+
{ action: "fill", target: "card", description: "Enter payment details" },
|
|
406
|
+
{ action: "click", target: "pay", description: "Submit payment" },
|
|
407
|
+
{ action: "verify", target: "/success", description: "Payment confirmed" },
|
|
408
|
+
],
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Analyze user journey results
|
|
414
|
+
*/
|
|
415
|
+
function analyzeJourney(journey, results) {
|
|
416
|
+
const analysis = {
|
|
417
|
+
journey: journey.name,
|
|
418
|
+
icon: journey.icon,
|
|
419
|
+
steps: [],
|
|
420
|
+
success: true,
|
|
421
|
+
failedAt: null,
|
|
422
|
+
duration: 0,
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
for (let i = 0; i < results.length; i++) {
|
|
426
|
+
const step = journey.steps[i];
|
|
427
|
+
const result = results[i];
|
|
428
|
+
|
|
429
|
+
analysis.steps.push({
|
|
430
|
+
...step,
|
|
431
|
+
success: result.success,
|
|
432
|
+
duration: result.duration,
|
|
433
|
+
error: result.error,
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
analysis.duration += result.duration || 0;
|
|
437
|
+
|
|
438
|
+
if (!result.success && analysis.success) {
|
|
439
|
+
analysis.success = false;
|
|
440
|
+
analysis.failedAt = i;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return analysis;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
448
|
+
// EVIDENCE PACK GENERATION
|
|
449
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Generate a comprehensive evidence pack
|
|
453
|
+
*/
|
|
454
|
+
async function generateEvidencePack(options = {}) {
|
|
455
|
+
const {
|
|
456
|
+
projectPath = ".",
|
|
457
|
+
outputDir = ".vibecheck",
|
|
458
|
+
findings = [],
|
|
459
|
+
coverage = {},
|
|
460
|
+
performance = {},
|
|
461
|
+
journeys = [],
|
|
462
|
+
screenshots = [],
|
|
463
|
+
videos = [],
|
|
464
|
+
traces = [],
|
|
465
|
+
} = options;
|
|
466
|
+
|
|
467
|
+
const packId = `evidence_${Date.now()}_${crypto.randomBytes(4).toString("hex")}`;
|
|
468
|
+
const packDir = path.join(outputDir, "evidence-packs", packId);
|
|
469
|
+
|
|
470
|
+
// Create pack directory
|
|
471
|
+
fs.mkdirSync(packDir, { recursive: true });
|
|
472
|
+
|
|
473
|
+
// Generate manifest
|
|
474
|
+
const manifest = {
|
|
475
|
+
id: packId,
|
|
476
|
+
timestamp: new Date().toISOString(),
|
|
477
|
+
project: path.basename(projectPath),
|
|
478
|
+
contents: {
|
|
479
|
+
findings: findings.length,
|
|
480
|
+
screenshots: screenshots.length,
|
|
481
|
+
videos: videos.length,
|
|
482
|
+
traces: traces.length,
|
|
483
|
+
},
|
|
484
|
+
coverage: coverage.stats,
|
|
485
|
+
performance: {
|
|
486
|
+
overall: performance.overall,
|
|
487
|
+
lcp: performance.grades?.lcp?.value,
|
|
488
|
+
cls: performance.grades?.cls?.value,
|
|
489
|
+
},
|
|
490
|
+
journeys: journeys.map((j) => ({
|
|
491
|
+
name: j.journey,
|
|
492
|
+
success: j.success,
|
|
493
|
+
})),
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
// Save manifest
|
|
497
|
+
fs.writeFileSync(
|
|
498
|
+
path.join(packDir, "manifest.json"),
|
|
499
|
+
JSON.stringify(manifest, null, 2)
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
// Save findings
|
|
503
|
+
fs.writeFileSync(
|
|
504
|
+
path.join(packDir, "findings.json"),
|
|
505
|
+
JSON.stringify(findings, null, 2)
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
// Save coverage
|
|
509
|
+
fs.writeFileSync(
|
|
510
|
+
path.join(packDir, "coverage.json"),
|
|
511
|
+
JSON.stringify(coverage, null, 2)
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
// Save performance
|
|
515
|
+
fs.writeFileSync(
|
|
516
|
+
path.join(packDir, "performance.json"),
|
|
517
|
+
JSON.stringify(performance, null, 2)
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
// Copy screenshots
|
|
521
|
+
const screenshotsDir = path.join(packDir, "screenshots");
|
|
522
|
+
if (screenshots.length > 0) {
|
|
523
|
+
fs.mkdirSync(screenshotsDir, { recursive: true });
|
|
524
|
+
for (const screenshot of screenshots) {
|
|
525
|
+
if (fs.existsSync(screenshot)) {
|
|
526
|
+
fs.copyFileSync(
|
|
527
|
+
screenshot,
|
|
528
|
+
path.join(screenshotsDir, path.basename(screenshot))
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Copy videos
|
|
535
|
+
const videosDir = path.join(packDir, "videos");
|
|
536
|
+
if (videos.length > 0) {
|
|
537
|
+
fs.mkdirSync(videosDir, { recursive: true });
|
|
538
|
+
for (const video of videos) {
|
|
539
|
+
if (fs.existsSync(video)) {
|
|
540
|
+
fs.copyFileSync(video, path.join(videosDir, path.basename(video)));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Generate HTML report
|
|
546
|
+
const htmlReport = generateHtmlEvidenceReport(manifest, findings, coverage, performance);
|
|
547
|
+
fs.writeFileSync(path.join(packDir, "report.html"), htmlReport);
|
|
548
|
+
|
|
549
|
+
return {
|
|
550
|
+
packId,
|
|
551
|
+
packDir,
|
|
552
|
+
manifest,
|
|
553
|
+
reportPath: path.join(packDir, "report.html"),
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Generate HTML evidence report
|
|
559
|
+
*/
|
|
560
|
+
function generateHtmlEvidenceReport(manifest, findings, coverage, performance) {
|
|
561
|
+
return `<!DOCTYPE html>
|
|
562
|
+
<html lang="en">
|
|
563
|
+
<head>
|
|
564
|
+
<meta charset="UTF-8">
|
|
565
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
566
|
+
<title>Vibecheck Evidence Pack - ${manifest.project}</title>
|
|
567
|
+
<style>
|
|
568
|
+
:root {
|
|
569
|
+
--bg: #0a0a0f;
|
|
570
|
+
--card-bg: #12121a;
|
|
571
|
+
--border: #2a2a3a;
|
|
572
|
+
--text: #e0e0e0;
|
|
573
|
+
--text-dim: #808090;
|
|
574
|
+
--accent: #6366f1;
|
|
575
|
+
--success: #22c55e;
|
|
576
|
+
--warning: #f59e0b;
|
|
577
|
+
--error: #ef4444;
|
|
578
|
+
}
|
|
579
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
580
|
+
body {
|
|
581
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
582
|
+
background: var(--bg);
|
|
583
|
+
color: var(--text);
|
|
584
|
+
line-height: 1.6;
|
|
585
|
+
padding: 2rem;
|
|
586
|
+
}
|
|
587
|
+
.container { max-width: 1200px; margin: 0 auto; }
|
|
588
|
+
h1 { font-size: 2rem; margin-bottom: 0.5rem; }
|
|
589
|
+
.meta { color: var(--text-dim); margin-bottom: 2rem; }
|
|
590
|
+
.card {
|
|
591
|
+
background: var(--card-bg);
|
|
592
|
+
border: 1px solid var(--border);
|
|
593
|
+
border-radius: 12px;
|
|
594
|
+
padding: 1.5rem;
|
|
595
|
+
margin-bottom: 1.5rem;
|
|
596
|
+
}
|
|
597
|
+
.card h2 { font-size: 1.25rem; margin-bottom: 1rem; color: var(--accent); }
|
|
598
|
+
.stat-grid {
|
|
599
|
+
display: grid;
|
|
600
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
601
|
+
gap: 1rem;
|
|
602
|
+
}
|
|
603
|
+
.stat {
|
|
604
|
+
text-align: center;
|
|
605
|
+
padding: 1rem;
|
|
606
|
+
background: rgba(99, 102, 241, 0.1);
|
|
607
|
+
border-radius: 8px;
|
|
608
|
+
}
|
|
609
|
+
.stat-value { font-size: 2rem; font-weight: bold; color: var(--accent); }
|
|
610
|
+
.stat-label { font-size: 0.875rem; color: var(--text-dim); }
|
|
611
|
+
.progress-bar {
|
|
612
|
+
height: 8px;
|
|
613
|
+
background: var(--border);
|
|
614
|
+
border-radius: 4px;
|
|
615
|
+
overflow: hidden;
|
|
616
|
+
margin: 0.5rem 0;
|
|
617
|
+
}
|
|
618
|
+
.progress-fill { height: 100%; background: var(--success); transition: width 0.3s; }
|
|
619
|
+
.finding {
|
|
620
|
+
padding: 1rem;
|
|
621
|
+
border-left: 3px solid var(--border);
|
|
622
|
+
margin-bottom: 0.5rem;
|
|
623
|
+
background: rgba(0,0,0,0.2);
|
|
624
|
+
}
|
|
625
|
+
.finding.BLOCK { border-color: var(--error); }
|
|
626
|
+
.finding.WARN { border-color: var(--warning); }
|
|
627
|
+
.badge {
|
|
628
|
+
display: inline-block;
|
|
629
|
+
padding: 0.25rem 0.5rem;
|
|
630
|
+
border-radius: 4px;
|
|
631
|
+
font-size: 0.75rem;
|
|
632
|
+
font-weight: bold;
|
|
633
|
+
}
|
|
634
|
+
.badge.BLOCK { background: var(--error); }
|
|
635
|
+
.badge.WARN { background: var(--warning); color: #000; }
|
|
636
|
+
.badge.good { background: var(--success); }
|
|
637
|
+
.badge.poor { background: var(--error); }
|
|
638
|
+
</style>
|
|
639
|
+
</head>
|
|
640
|
+
<body>
|
|
641
|
+
<div class="container">
|
|
642
|
+
<h1>📋 Evidence Pack</h1>
|
|
643
|
+
<p class="meta">${manifest.project} • ${manifest.timestamp}</p>
|
|
644
|
+
|
|
645
|
+
<div class="card">
|
|
646
|
+
<h2>📊 Summary</h2>
|
|
647
|
+
<div class="stat-grid">
|
|
648
|
+
<div class="stat">
|
|
649
|
+
<div class="stat-value">${findings.length}</div>
|
|
650
|
+
<div class="stat-label">Findings</div>
|
|
651
|
+
</div>
|
|
652
|
+
<div class="stat">
|
|
653
|
+
<div class="stat-value">${coverage.stats?.percentage || 0}%</div>
|
|
654
|
+
<div class="stat-label">Coverage</div>
|
|
655
|
+
</div>
|
|
656
|
+
<div class="stat">
|
|
657
|
+
<div class="stat-value">${performance.grades?.lcp?.value ? Math.round(performance.grades.lcp.value) + 'ms' : 'N/A'}</div>
|
|
658
|
+
<div class="stat-label">LCP</div>
|
|
659
|
+
</div>
|
|
660
|
+
<div class="stat">
|
|
661
|
+
<div class="stat-value">${manifest.contents.screenshots}</div>
|
|
662
|
+
<div class="stat-label">Screenshots</div>
|
|
663
|
+
</div>
|
|
664
|
+
</div>
|
|
665
|
+
</div>
|
|
666
|
+
|
|
667
|
+
<div class="card">
|
|
668
|
+
<h2>🎯 Coverage</h2>
|
|
669
|
+
<div class="progress-bar">
|
|
670
|
+
<div class="progress-fill" style="width: ${coverage.stats?.percentage || 0}%"></div>
|
|
671
|
+
</div>
|
|
672
|
+
<p>${coverage.stats?.covered || 0} of ${coverage.stats?.total || 0} routes verified</p>
|
|
673
|
+
</div>
|
|
674
|
+
|
|
675
|
+
<div class="card">
|
|
676
|
+
<h2>⚡ Performance</h2>
|
|
677
|
+
<p>Overall: <span class="badge ${performance.overall || 'unknown'}">${(performance.overall || 'unknown').toUpperCase()}</span></p>
|
|
678
|
+
</div>
|
|
679
|
+
|
|
680
|
+
<div class="card">
|
|
681
|
+
<h2>🔍 Top Findings</h2>
|
|
682
|
+
${findings.slice(0, 10).map(f => `
|
|
683
|
+
<div class="finding ${f.severity}">
|
|
684
|
+
<span class="badge ${f.severity}">${f.severity}</span>
|
|
685
|
+
<strong>${f.title || f.message}</strong>
|
|
686
|
+
<br><small>${f.file}:${f.line}</small>
|
|
687
|
+
</div>
|
|
688
|
+
`).join('')}
|
|
689
|
+
</div>
|
|
690
|
+
</div>
|
|
691
|
+
</body>
|
|
692
|
+
</html>`;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
696
|
+
// EXPORTS
|
|
697
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
698
|
+
|
|
699
|
+
module.exports = {
|
|
700
|
+
// Performance
|
|
701
|
+
trackWebVitals,
|
|
702
|
+
analyzePerformance,
|
|
703
|
+
renderPerformanceReport,
|
|
704
|
+
PERFORMANCE_BUDGETS,
|
|
705
|
+
|
|
706
|
+
// Coverage
|
|
707
|
+
generateCoverageMap,
|
|
708
|
+
renderCoverageMap,
|
|
709
|
+
|
|
710
|
+
// User Journeys
|
|
711
|
+
analyzeJourney,
|
|
712
|
+
COMMON_JOURNEYS,
|
|
713
|
+
|
|
714
|
+
// Evidence Pack
|
|
715
|
+
generateEvidencePack,
|
|
716
|
+
generateHtmlEvidenceReport,
|
|
717
|
+
};
|