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.
Files changed (42) hide show
  1. package/README.md +193 -64
  2. package/dist/a11y-deep.d.ts +20 -0
  3. package/dist/a11y-deep.js +161 -0
  4. package/dist/ai-analysis.d.ts +28 -0
  5. package/dist/ai-analysis.js +32 -0
  6. package/dist/audit/broken-links.d.ts +5 -1
  7. package/dist/audit/broken-links.js +23 -12
  8. package/dist/bundle-size.d.ts +14 -0
  9. package/dist/bundle-size.js +209 -0
  10. package/dist/cli.js +432 -16
  11. package/dist/compare-env.d.ts +23 -0
  12. package/dist/compare-env.js +55 -0
  13. package/dist/config.d.ts +50 -0
  14. package/dist/config.js +149 -4
  15. package/dist/entitlement.d.ts +2 -0
  16. package/dist/entitlement.js +5 -1
  17. package/dist/init-analysis.d.ts +6 -0
  18. package/dist/init-analysis.js +302 -0
  19. package/dist/init.js +66 -0
  20. package/dist/lighthouse.d.ts +31 -1
  21. package/dist/lighthouse.js +76 -3
  22. package/dist/outdated-check.d.ts +17 -0
  23. package/dist/outdated-check.js +123 -0
  24. package/dist/report-markdown.d.ts +14 -0
  25. package/dist/report-markdown.js +21 -0
  26. package/dist/route-discovery.d.ts +7 -0
  27. package/dist/route-discovery.js +108 -0
  28. package/dist/secret-scan.d.ts +15 -0
  29. package/dist/secret-scan.js +218 -0
  30. package/dist/security-audit.d.ts +9 -1
  31. package/dist/security-audit.js +87 -24
  32. package/dist/seo-deep.d.ts +24 -0
  33. package/dist/seo-deep.js +147 -0
  34. package/dist/typecheck.d.ts +8 -0
  35. package/dist/typecheck.js +99 -0
  36. package/dist/verification-core/report.js +117 -0
  37. package/dist/verification-core/types.d.ts +58 -2
  38. package/dist/visual-diff.d.ts +8 -1
  39. package/dist/visual-diff.js +53 -8
  40. package/dist/vitals-budget.d.ts +23 -0
  41. package/dist/vitals-budget.js +168 -0
  42. package/package.json +1 -1
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runVitalsBudget = runVitalsBudget;
4
+ /**
5
+ * Core Web Vitals budget check.
6
+ *
7
+ * Uses Puppeteer to measure LCP, CLS, and INP against configurable
8
+ * budgets. Does NOT claim FID — INP is the modern replacement.
9
+ * Blocker-capable: budget violations surface as warnings.
10
+ */
11
+ let puppeteerModule = null;
12
+ try {
13
+ puppeteerModule = require("puppeteer");
14
+ }
15
+ catch {
16
+ // Puppeteer not available — vitals budget will be skipped
17
+ }
18
+ const DEFAULT_BUDGET = {
19
+ lcp: 2500,
20
+ cls: 0.1,
21
+ inp: 200,
22
+ };
23
+ async function runVitalsBudget(url, budget) {
24
+ if (!puppeteerModule) {
25
+ return {
26
+ passed: true,
27
+ metrics: [],
28
+ lcp: null,
29
+ cls: null,
30
+ inp: null,
31
+ url,
32
+ skipped: true,
33
+ summary: "Skipped (puppeteer not available)",
34
+ };
35
+ }
36
+ const effectiveBudget = { ...DEFAULT_BUDGET, ...budget };
37
+ console.log(" Running Core Web Vitals budget check...");
38
+ const puppeteer = puppeteerModule.default || puppeteerModule;
39
+ let browser;
40
+ try {
41
+ browser = await puppeteer.launch({
42
+ headless: true,
43
+ args: [
44
+ "--no-sandbox",
45
+ "--disable-setuid-sandbox",
46
+ "--disable-extensions",
47
+ "--disable-component-update",
48
+ ],
49
+ });
50
+ const page = await browser.newPage();
51
+ await page.setViewport({ width: 1350, height: 940 });
52
+ // Inject web-vitals measurement script before navigation
53
+ await page.evaluateOnNewDocument(() => {
54
+ window.__laxyVitals = {};
55
+ // LCP
56
+ try {
57
+ new PerformanceObserver((list) => {
58
+ const entries = list.getEntries();
59
+ const last = entries[entries.length - 1];
60
+ if (last) {
61
+ window.__laxyVitals.lcp = last.startTime;
62
+ }
63
+ }).observe({ type: "largest-contentful-paint", buffered: true });
64
+ }
65
+ catch { }
66
+ // CLS
67
+ try {
68
+ let clsValue = 0;
69
+ new PerformanceObserver((list) => {
70
+ for (const entry of list.getEntries()) {
71
+ if (!entry.hadRecentInput) {
72
+ clsValue += entry.value;
73
+ }
74
+ }
75
+ window.__laxyVitals.cls = clsValue;
76
+ }).observe({ type: "layout-shift", buffered: true });
77
+ }
78
+ catch { }
79
+ // INP (Event Timing)
80
+ try {
81
+ let worstInp = 0;
82
+ new PerformanceObserver((list) => {
83
+ for (const entry of list.getEntries()) {
84
+ const duration = entry.duration;
85
+ if (duration > worstInp) {
86
+ worstInp = duration;
87
+ }
88
+ }
89
+ window.__laxyVitals.inp = worstInp;
90
+ }).observe({ type: "event", buffered: true });
91
+ }
92
+ catch { }
93
+ });
94
+ await page.goto(url, { waitUntil: "networkidle2", timeout: 30000 });
95
+ // Wait for LCP to stabilize
96
+ await new Promise((resolve) => setTimeout(resolve, 3000));
97
+ // Simulate some interactions to get INP
98
+ try {
99
+ await page.mouse.click(100, 100);
100
+ await new Promise((resolve) => setTimeout(resolve, 500));
101
+ await page.keyboard.press("Tab");
102
+ await new Promise((resolve) => setTimeout(resolve, 500));
103
+ }
104
+ catch {
105
+ // interaction may fail on minimal pages
106
+ }
107
+ // Wait a bit more for metrics to settle
108
+ await new Promise((resolve) => setTimeout(resolve, 2000));
109
+ const vitals = await page.evaluate(() => {
110
+ const v = window.__laxyVitals;
111
+ return {
112
+ lcp: v.lcp ?? 0,
113
+ cls: v.cls ?? 0,
114
+ inp: v.inp ?? 0,
115
+ };
116
+ });
117
+ const metrics = [
118
+ {
119
+ name: "LCP",
120
+ value: Math.round(vitals.lcp),
121
+ unit: "ms",
122
+ budget: effectiveBudget.lcp,
123
+ passed: vitals.lcp <= effectiveBudget.lcp,
124
+ },
125
+ {
126
+ name: "CLS",
127
+ value: Math.round(vitals.cls * 1000) / 1000,
128
+ unit: "score",
129
+ budget: effectiveBudget.cls,
130
+ passed: vitals.cls <= effectiveBudget.cls,
131
+ },
132
+ {
133
+ name: "INP",
134
+ value: Math.round(vitals.inp),
135
+ unit: "ms",
136
+ budget: effectiveBudget.inp,
137
+ passed: vitals.inp <= effectiveBudget.inp,
138
+ },
139
+ ];
140
+ const passed = metrics.every((m) => m.passed);
141
+ const failedMetrics = metrics.filter((m) => !m.passed);
142
+ const summary = passed
143
+ ? `LCP ${Math.round(vitals.lcp)}ms, CLS ${(vitals.cls).toFixed(3)}, INP ${Math.round(vitals.inp)}ms — within budget`
144
+ : failedMetrics.map((m) => `${m.name} ${m.value}${m.unit} > ${m.budget}${m.unit}`).join("; ");
145
+ console.log(` Vitals budget: ${summary}`);
146
+ for (const m of metrics) {
147
+ const status = m.passed ? "OK" : "OVER";
148
+ console.log(` ${m.name}: ${m.value}${m.unit} / ${m.budget}${m.unit} ${status}`);
149
+ }
150
+ return { passed, metrics, lcp: Math.round(vitals.lcp), cls: Math.round(vitals.cls * 1000) / 1000, inp: Math.round(vitals.inp), url, skipped: false, summary };
151
+ }
152
+ catch (err) {
153
+ console.error(` Vitals budget error: ${err instanceof Error ? err.message : String(err)}`);
154
+ return {
155
+ passed: true,
156
+ metrics: [],
157
+ lcp: null,
158
+ cls: null,
159
+ inp: null,
160
+ url,
161
+ skipped: true,
162
+ summary: `Error: ${err instanceof Error ? err.message : String(err)}`,
163
+ };
164
+ }
165
+ finally {
166
+ await browser?.close();
167
+ }
168
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "laxy-verify",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Frontend verification CLI for build checks, Lighthouse, E2E, and release readiness",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",