react-doctor-cli-dev 1.0.9 → 1.0.12

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 (38) hide show
  1. package/dashboard/index.html +253 -0
  2. package/dashboard/package-lock.json +913 -0
  3. package/dashboard/package.json +19 -0
  4. package/dashboard/public/favicon.svg +1 -0
  5. package/dashboard/public/icons.svg +24 -0
  6. package/dashboard/src/api.js +107 -0
  7. package/dashboard/src/assets/hero.png +0 -0
  8. package/dashboard/src/assets/javascript.svg +1 -0
  9. package/dashboard/src/assets/vite.svg +1 -0
  10. package/dashboard/src/css/main.css +441 -0
  11. package/dashboard/src/data.js +202 -0
  12. package/dashboard/src/main.js +53 -0
  13. package/dashboard/src/pages.js +797 -0
  14. package/dashboard/src/router.js +77 -0
  15. package/dashboard/src/utils.js +163 -0
  16. package/dashboard/vite.config.js +19 -0
  17. package/data/screenshots/5-docs-fullLoad.png +0 -0
  18. package/data/screenshots/6--fcp.png +0 -0
  19. package/data/screenshots/6--fullLoad.png +0 -0
  20. package/data/screenshots/6-white-fullLoad.png +0 -0
  21. package/package.json +4 -22
  22. package/shared/dist/index.d.ts +2 -0
  23. package/shared/dist/index.js +19 -0
  24. package/shared/dist/schemas.d.ts +91 -0
  25. package/shared/dist/schemas.js +82 -0
  26. package/shared/dist/types.d.ts +44 -0
  27. package/shared/dist/types.js +2 -0
  28. package/shared/package-lock.json +47 -0
  29. package/shared/package.json +21 -0
  30. package/shared/src/index.ts +4 -0
  31. package/shared/src/schemas.ts +136 -0
  32. package/shared/src/types.ts +137 -0
  33. package/shared/tsconfig.json +15 -0
  34. package/tsconfig.json +25 -0
  35. /package/{backend/data/screenshots/4--fcp.png → data/screenshots/5--fcp.png} +0 -0
  36. /package/{backend/data/screenshots/4--fullLoad.png → data/screenshots/5--fullLoad.png} +0 -0
  37. /package/{backend/data/screenshots/4-white-fullLoad.png → data/screenshots/5-white-fullLoad.png} +0 -0
  38. /package/{backend/data/screenshots/4-docs-fullLoad.png → data/screenshots/6-docs-fullLoad.png} +0 -0
@@ -0,0 +1,77 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // js/router.js — hash-based SPA router
3
+ // Maps #hash → page element + init function
4
+ // ─────────────────────────────────────────────────────────────
5
+
6
+ import { initOverview, initVitals, initIssues, initSuggestions, initHistory } from './pages.js';
7
+ import { closeLightbox } from './utils.js';
8
+
9
+ const ROUTES = {
10
+ "overview": { id: "page-overview", init: initOverview },
11
+ "vitals": { id: "page-vitals", init: initVitals },
12
+ "issues": { id: "page-issues", init: initIssues },
13
+ "suggestions": { id: "page-suggestions", init: initSuggestions },
14
+ "history": { id: "page-history", init: initHistory },
15
+ };
16
+
17
+ let currentRoute = null;
18
+
19
+ async function navigate(hash) {
20
+ const route = ROUTES[hash] || ROUTES["overview"];
21
+ if (currentRoute === hash) return;
22
+ currentRoute = hash;
23
+
24
+ // Hide all pages
25
+ document.querySelectorAll(".page-content").forEach(el => el.classList.remove("active"));
26
+ // Show target
27
+ const page = document.getElementById(route.id);
28
+ if (page) page.classList.add("active");
29
+
30
+ // Update nav
31
+ document.querySelectorAll(".nav-item").forEach(el => {
32
+ el.classList.toggle("active", el.dataset.route === hash);
33
+ });
34
+
35
+ // Update topbar
36
+ const titles = {
37
+ overview: ["Overview", "Performance summary"],
38
+ vitals: ["Web Vitals", "Core Web Vitals per route & device"],
39
+ issues: ["Code Issues", "Static analysis results"],
40
+ suggestions: ["Suggestions", "Rule engine recommendations"],
41
+ history: ["History", "Previous analysis runs"],
42
+ };
43
+ const [title, sub] = titles[hash] || titles.overview;
44
+ document.getElementById("topbar-title").textContent = title;
45
+ document.getElementById("topbar-sub").textContent = sub;
46
+
47
+ // Run page init (await it!)
48
+ try {
49
+ await route.init();
50
+ } catch (err) {
51
+ console.error('Error initializing page:', err);
52
+ }
53
+ }
54
+
55
+ export function initRouter() {
56
+ // Nav clicks
57
+ document.querySelectorAll(".nav-item[data-route]").forEach(btn => {
58
+ btn.addEventListener("click", () => {
59
+ window.location.hash = btn.dataset.route;
60
+ });
61
+ });
62
+
63
+ // Hash change
64
+ window.addEventListener("hashchange", () => {
65
+ navigate(location.hash.replace("#", "") || "overview");
66
+ });
67
+
68
+ // Lightbox close
69
+ document.getElementById("lightbox").addEventListener("click", e => {
70
+ if (e.target === e.currentTarget) closeLightbox();
71
+ });
72
+ document.getElementById("lb-close").addEventListener("click", closeLightbox);
73
+ document.addEventListener("keydown", e => { if (e.key === "Escape") closeLightbox(); });
74
+
75
+ // Initial route
76
+ navigate(location.hash.replace("#", "") || "overview");
77
+ }
@@ -0,0 +1,163 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // js/utils.js — pure helper functions used across all pages
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ export function scoreColor(score) {
6
+ if (score >= 90) return "var(--green)";
7
+ if (score >= 75) return "var(--blue)";
8
+ if (score >= 50) return "var(--orange)";
9
+ return "var(--red)";
10
+ }
11
+
12
+ export function gradeBadgeClass(grade) {
13
+ const g = grade ? grade[0] : 'N';
14
+ if (g === "A") return "grade-a";
15
+ if (g === "B") return "grade-b";
16
+ if (g === "C") return "grade-c";
17
+ if (g === "D") return "grade-d";
18
+ return "grade-d";
19
+ }
20
+
21
+ export function severityIcon(severity) {
22
+ return { critical: "🔴", warning: "🟠", info: "🔵" }[severity] || "⚪";
23
+ }
24
+
25
+ export function severityLabel(sev) {
26
+ return `<span class="badge ${sev}">${severityIcon(sev)} ${sev}</span>`;
27
+ }
28
+
29
+ // Vital thresholds: [good_max, warn_max] (above warn_max = bad)
30
+ const VITAL_THRESHOLDS = {
31
+ lcp: [2500, 4000],
32
+ fcp: [1800, 3000],
33
+ ttfb: [800, 1800],
34
+ inp: [200, 500],
35
+ cls: [0.1, 0.25],
36
+ render: [2000, 4000], // ← Added Render Time threshold
37
+ };
38
+
39
+ export function vitalClass(metric, value) {
40
+ const [good, warn] = VITAL_THRESHOLDS[metric] || [Infinity, Infinity];
41
+ if (value <= good) return "good";
42
+ if (value <= warn) return "warn";
43
+ return "bad";
44
+ }
45
+
46
+ export function vitalColor(metric, value) {
47
+ const cls = vitalClass(metric, value);
48
+ return cls === "good" ? "var(--green)" : cls === "warn" ? "var(--orange)" : "var(--red)";
49
+ }
50
+
51
+ // Percentage fill for the bar — capped to 100%
52
+ export function vitalBarPct(metric, value) {
53
+ const limits = {
54
+ lcp: 5000,
55
+ fcp: 4000,
56
+ ttfb: 2500,
57
+ inp: 600,
58
+ cls: 0.35,
59
+ render: 6000 // ← Added Render Time limit
60
+ };
61
+ const max = limits[metric] || 100;
62
+ return Math.min((value / max) * 100, 100).toFixed(1);
63
+ }
64
+
65
+ export function formatMs(ms) {
66
+ if (ms === undefined || ms === null) return "–";
67
+ if (ms >= 1000) return (ms / 1000).toFixed(2) + "s";
68
+ return ms.toFixed(0) + "ms";
69
+ }
70
+
71
+ export function formatCls(v) {
72
+ return typeof v === "number" ? v.toFixed(3) : "–";
73
+ }
74
+
75
+ export function formatDate(iso) {
76
+ if (!iso) return "–";
77
+ const d = new Date(iso);
78
+ return d.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }) +
79
+ " " + d.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
80
+ }
81
+
82
+ export function relativeTime(iso) {
83
+ if (!iso) return "";
84
+ const diff = Date.now() - new Date(iso).getTime();
85
+ const m = Math.floor(diff / 60000);
86
+ if (m < 1) return "just now";
87
+ if (m < 60) return `${m}m ago`;
88
+ const h = Math.floor(m / 60);
89
+ if (h < 24) return `${h}h ago`;
90
+ return `${Math.floor(h / 24)}d ago`;
91
+ }
92
+
93
+ // ── Build the SVG score ring with VISIBLE text ──────────────
94
+ export function buildScoreRing(score, size = 140, strokeWidth = 10) {
95
+ const r = (size - strokeWidth) / 2;
96
+ const c = size / 2;
97
+ const circ = 2 * Math.PI * r;
98
+ const pct = Math.min(Math.max(score / 100, 0), 1);
99
+ const color = scoreColor(score);
100
+
101
+ // Map score to a color for the text
102
+ let textColor;
103
+ if (score >= 90) textColor = '#3FB950'; // green
104
+ else if (score >= 75) textColor = '#58A6FF'; // blue
105
+ else if (score >= 50) textColor = '#F0883E'; // orange
106
+ else textColor = '#FF7B72'; // red
107
+
108
+ return `
109
+ <div class="score-ring" style="width:${size}px;height:${size}px;position:relative;flex-shrink:0;">
110
+ <svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" style="transform:rotate(-90deg);display:block;">
111
+ <circle
112
+ cx="${c}" cy="${c}" r="${r}"
113
+ fill="none"
114
+ stroke="#1C2333"
115
+ stroke-width="${strokeWidth}" />
116
+ <circle
117
+ cx="${c}" cy="${c}" r="${r}"
118
+ fill="none"
119
+ stroke="${textColor}"
120
+ stroke-width="${strokeWidth}"
121
+ stroke-linecap="round"
122
+ stroke-dasharray="${circ}"
123
+ stroke-dashoffset="${circ * (1 - pct)}"
124
+ style="transition: stroke-dashoffset 1.4s cubic-bezier(.4,0,.2,1);" />
125
+ </svg>
126
+ <div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;width:100%;z-index:10;">
127
+ <div style="font-size:2.4rem;font-weight:700;font-family:'JetBrains Mono',monospace;line-height:1;color:${textColor};text-shadow:0 0 20px rgba(0,0,0,0.5);">${Math.round(score)}</div>
128
+ <div style="font-size:0.7rem;font-weight:600;color:#8B949E;margin-top:2px;">/ 100</div>
129
+ </div>
130
+ </div>
131
+ `;
132
+ }
133
+
134
+ // Vital bar row HTML
135
+ export function buildVitalRow(label, metric, value) {
136
+ const display = metric === "cls" ? formatCls(value) : formatMs(value);
137
+ const color = vitalColor(metric, value);
138
+ const pct = vitalBarPct(metric, value);
139
+ return `
140
+ <div class="vital-row">
141
+ <div class="vital-row-head">
142
+ <span class="vname">${label}</span>
143
+ <span class="vval" style="color:${color}">${display}</span>
144
+ </div>
145
+ <div class="vital-track">
146
+ <div class="vital-fill" style="width:${pct}%;background:${color}"></div>
147
+ </div>
148
+ </div>`;
149
+ }
150
+
151
+ // Lightbox
152
+ export function openLightbox(src, caption) {
153
+ const lb = document.getElementById("lightbox");
154
+ const img = lb.querySelector("img");
155
+ if (img) img.src = src;
156
+ const cap = document.getElementById("lb-caption");
157
+ if (cap) cap.textContent = caption || "";
158
+ lb.classList.add("open");
159
+ }
160
+
161
+ export function closeLightbox() {
162
+ document.getElementById("lightbox").classList.remove("open");
163
+ }
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'vite';
2
+ import path from 'path';
3
+
4
+ export default defineConfig({
5
+ root: '.',
6
+ base: '/',
7
+ build: {
8
+ outDir: '../backend/public', // Express serves this as static
9
+ emptyOutDir: true,
10
+ },
11
+ server: {
12
+ port: 5173,
13
+ proxy: {
14
+ // Forward /api/* and /screenshots/* to the backend during dev
15
+ '/api': 'http://localhost:3000',
16
+ '/screenshots': 'http://localhost:3000',
17
+ },
18
+ },
19
+ });
Binary file
Binary file
package/package.json CHANGED
@@ -1,25 +1,19 @@
1
1
  {
2
2
  "name": "react-doctor-cli-dev",
3
- "version": "1.0.9",
4
- "description": "React performance analyzer with static analysis, runtime profiling, rule engine, and dashboard",
3
+ "version": "1.0.12",
4
+ "description": "React performance analyzer with static analysis, runtime profiling, rule engine, and dashboard upload",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
7
7
  "bin": {
8
8
  "react-doctor": "./cli/bin/react-doctor.js"
9
9
  },
10
- "scripts": {
11
- "postinstall": "node scripts/postinstall.js",
12
- "build": "node scripts/build.js",
13
- "dev": "node scripts/dev.js"
14
- },
10
+ "scripts": {},
15
11
  "keywords": [
16
12
  "react",
17
13
  "performance",
18
14
  "profiler",
19
15
  "cli",
20
- "analyzer",
21
- "dashboard",
22
- "web-vitals"
16
+ "analyzer"
23
17
  ],
24
18
  "author": "Ozma",
25
19
  "license": "ISC",
@@ -54,17 +48,5 @@
54
48
  "@types/fs-extra": "^11.0.4",
55
49
  "@types/node": "^25.9.1",
56
50
  "nodemon": "^3.1.14"
57
- },
58
- "files": [
59
- "cli/**/*",
60
- "backend/**/*",
61
- "frontend/dist/**/*",
62
- "core/**/*",
63
- "scripts/**/*",
64
- "README.md",
65
- "LICENSE"
66
- ],
67
- "engines": {
68
- "node": ">=18.0.0"
69
51
  }
70
52
  }
@@ -0,0 +1,2 @@
1
+ export * from './types';
2
+ export * from './schemas';
@@ -0,0 +1,19 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // Exporting all types in one common used file
18
+ __exportStar(require("./types"), exports);
19
+ __exportStar(require("./schemas"), exports);
@@ -0,0 +1,91 @@
1
+ import * as v from 'valibot';
2
+
3
+ export declare const WebVitalsSchema: v.ObjectSchema<{
4
+ readonly lcp: v.NumberSchema<undefined>;
5
+ readonly fcp: v.NumberSchema<undefined>;
6
+ readonly cls: v.NumberSchema<undefined>;
7
+ readonly inp: v.NumberSchema<undefined>;
8
+ readonly ttfb: v.NumberSchema<undefined>;
9
+ }, undefined>;
10
+ export declare const ComponentIssueSchema: v.ObjectSchema<{
11
+ readonly id: v.StringSchema<undefined>;
12
+ readonly component: v.StringSchema<undefined>;
13
+ readonly file: v.StringSchema<undefined>;
14
+ readonly line: v.NumberSchema<undefined>;
15
+ readonly severity: v.PicklistSchema<["critical", "warning", "info"], undefined>;
16
+ readonly message: v.StringSchema<undefined>;
17
+ readonly suggestion: v.StringSchema<undefined>;
18
+ }, undefined>;
19
+ export declare const StaticReportSchema: v.ObjectSchema<{
20
+ readonly timestamp: v.StringSchema<undefined>;
21
+ readonly issues: v.ArraySchema<v.ObjectSchema<{
22
+ readonly id: v.StringSchema<undefined>;
23
+ readonly component: v.StringSchema<undefined>;
24
+ readonly file: v.StringSchema<undefined>;
25
+ readonly line: v.NumberSchema<undefined>;
26
+ readonly severity: v.PicklistSchema<["critical", "warning", "info"], undefined>;
27
+ readonly message: v.StringSchema<undefined>;
28
+ readonly suggestion: v.StringSchema<undefined>;
29
+ }, undefined>, undefined>;
30
+ readonly bundleSize: v.NumberSchema<undefined>;
31
+ readonly componentCount: v.NumberSchema<undefined>;
32
+ }, undefined>;
33
+ export declare const RuntimeReportSchema: v.ObjectSchema<{
34
+ readonly timestamp: v.StringSchema<undefined>;
35
+ readonly metrics: v.ObjectSchema<{
36
+ readonly lcp: v.NumberSchema<undefined>;
37
+ readonly fcp: v.NumberSchema<undefined>;
38
+ readonly cls: v.NumberSchema<undefined>;
39
+ readonly inp: v.NumberSchema<undefined>;
40
+ readonly ttfb: v.NumberSchema<undefined>;
41
+ }, undefined>;
42
+ readonly rerenders: v.RecordSchema<v.StringSchema<undefined>, v.NumberSchema<undefined>, undefined>;
43
+ readonly commitDurations: v.ArraySchema<v.NumberSchema<undefined>, undefined>;
44
+ }, undefined>;
45
+ export declare const SuggestionSchema: v.ObjectSchema<{
46
+ readonly id: v.StringSchema<undefined>;
47
+ readonly title: v.StringSchema<undefined>;
48
+ readonly description: v.StringSchema<undefined>;
49
+ readonly severity: v.PicklistSchema<["critical", "warning", "info"], undefined>;
50
+ readonly affectedComponent: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
51
+ readonly fix: v.StringSchema<undefined>;
52
+ }, undefined>;
53
+ export declare const FinalReportSchema: v.ObjectSchema<{
54
+ readonly projectName: v.StringSchema<undefined>;
55
+ readonly analyzedAt: v.StringSchema<undefined>;
56
+ readonly static: v.ObjectSchema<{
57
+ readonly timestamp: v.StringSchema<undefined>;
58
+ readonly issues: v.ArraySchema<v.ObjectSchema<{
59
+ readonly id: v.StringSchema<undefined>;
60
+ readonly component: v.StringSchema<undefined>;
61
+ readonly file: v.StringSchema<undefined>;
62
+ readonly line: v.NumberSchema<undefined>;
63
+ readonly severity: v.PicklistSchema<["critical", "warning", "info"], undefined>;
64
+ readonly message: v.StringSchema<undefined>;
65
+ readonly suggestion: v.StringSchema<undefined>;
66
+ }, undefined>, undefined>;
67
+ readonly bundleSize: v.NumberSchema<undefined>;
68
+ readonly componentCount: v.NumberSchema<undefined>;
69
+ }, undefined>;
70
+ readonly runtime: v.ObjectSchema<{
71
+ readonly timestamp: v.StringSchema<undefined>;
72
+ readonly metrics: v.ObjectSchema<{
73
+ readonly lcp: v.NumberSchema<undefined>;
74
+ readonly fcp: v.NumberSchema<undefined>;
75
+ readonly cls: v.NumberSchema<undefined>;
76
+ readonly inp: v.NumberSchema<undefined>;
77
+ readonly ttfb: v.NumberSchema<undefined>;
78
+ }, undefined>;
79
+ readonly rerenders: v.RecordSchema<v.StringSchema<undefined>, v.NumberSchema<undefined>, undefined>;
80
+ readonly commitDurations: v.ArraySchema<v.NumberSchema<undefined>, undefined>;
81
+ }, undefined>;
82
+ readonly suggestions: v.ArraySchema<v.ObjectSchema<{
83
+ readonly id: v.StringSchema<undefined>;
84
+ readonly title: v.StringSchema<undefined>;
85
+ readonly description: v.StringSchema<undefined>;
86
+ readonly severity: v.PicklistSchema<["critical", "warning", "info"], undefined>;
87
+ readonly affectedComponent: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
88
+ readonly fix: v.StringSchema<undefined>;
89
+ }, undefined>, undefined>;
90
+ readonly performanceScore: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 100, undefined>]>;
91
+ }, undefined>;
@@ -0,0 +1,82 @@
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.FinalReportSchema = exports.SuggestionSchema = exports.RuntimeReportSchema = exports.StaticReportSchema = exports.ComponentIssueSchema = exports.WebVitalsSchema = void 0;
37
+ const v = __importStar(require("valibot"));
38
+ // Valibot schemas for validation
39
+ exports.WebVitalsSchema = v.object({
40
+ lcp: v.number(),
41
+ fcp: v.number(),
42
+ cls: v.number(),
43
+ inp: v.number(),
44
+ ttfb: v.number(),
45
+ });
46
+ exports.ComponentIssueSchema = v.object({
47
+ id: v.string(),
48
+ component: v.string(),
49
+ file: v.string(),
50
+ line: v.number(),
51
+ severity: v.picklist(['critical', 'warning', 'info']),
52
+ message: v.string(),
53
+ suggestion: v.string(),
54
+ });
55
+ exports.StaticReportSchema = v.object({
56
+ timestamp: v.string(),
57
+ issues: v.array(exports.ComponentIssueSchema),
58
+ bundleSize: v.number(),
59
+ componentCount: v.number(),
60
+ });
61
+ exports.RuntimeReportSchema = v.object({
62
+ timestamp: v.string(),
63
+ metrics: exports.WebVitalsSchema,
64
+ rerenders: v.record(v.string(), v.number()),
65
+ commitDurations: v.array(v.number()),
66
+ });
67
+ exports.SuggestionSchema = v.object({
68
+ id: v.string(),
69
+ title: v.string(),
70
+ description: v.string(),
71
+ severity: v.picklist(['critical', 'warning', 'info']),
72
+ affectedComponent: v.optional(v.string()),
73
+ fix: v.string(),
74
+ });
75
+ exports.FinalReportSchema = v.object({
76
+ projectName: v.string(),
77
+ analyzedAt: v.string(),
78
+ static: exports.StaticReportSchema,
79
+ runtime: exports.RuntimeReportSchema,
80
+ suggestions: v.array(exports.SuggestionSchema),
81
+ performanceScore: v.pipe(v.number(), v.minValue(0), v.maxValue(100)),
82
+ });
@@ -0,0 +1,44 @@
1
+ export interface WebVitals {
2
+ lcp: number;
3
+ fcp: number;
4
+ cls: number;
5
+ inp: number;
6
+ ttfb: number;
7
+ }
8
+ export interface ComponentIssue {
9
+ id: string;
10
+ component: string;
11
+ file: string;
12
+ line: number;
13
+ severity: "critical" | "warning" | "info";
14
+ message: string;
15
+ suggestion: string;
16
+ }
17
+ export interface StaticReport {
18
+ timestamp: string;
19
+ issues: ComponentIssue[];
20
+ bundleSize: number;
21
+ componentCount: number;
22
+ }
23
+ export interface RuntimeReport {
24
+ timestamp: string;
25
+ metrics: WebVitals;
26
+ rerenders: Record<string, number>;
27
+ commitDurations: number[];
28
+ }
29
+ export interface Suggestion {
30
+ id: string;
31
+ title: string;
32
+ description: string;
33
+ severity: "critical" | "warning" | "info";
34
+ affectedComponent?: string;
35
+ fix: string;
36
+ }
37
+ export interface FinalReport {
38
+ projectName: string;
39
+ analyzedAt: string;
40
+ static: StaticReport;
41
+ runtime: RuntimeReport;
42
+ suggestions: Suggestion[];
43
+ performanceScore: number;
44
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "shared",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "shared",
9
+ "version": "1.0.0",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "valibot": "^1.2.0"
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.9.3"
16
+ }
17
+ },
18
+ "node_modules/typescript": {
19
+ "version": "5.9.3",
20
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
21
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
22
+ "devOptional": true,
23
+ "license": "Apache-2.0",
24
+ "bin": {
25
+ "tsc": "bin/tsc",
26
+ "tsserver": "bin/tsserver"
27
+ },
28
+ "engines": {
29
+ "node": ">=14.17"
30
+ }
31
+ },
32
+ "node_modules/valibot": {
33
+ "version": "1.2.0",
34
+ "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz",
35
+ "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==",
36
+ "license": "MIT",
37
+ "peerDependencies": {
38
+ "typescript": ">=5"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "typescript": {
42
+ "optional": true
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "shared",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "build": "tsc",
9
+ "dev": "tsc --watch"
10
+ },
11
+ "keywords": [],
12
+ "author": "",
13
+ "license": "ISC",
14
+ "type": "commonjs",
15
+ "dependencies": {
16
+ "valibot": "^1.2.0"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.9.3"
20
+ }
21
+ }
@@ -0,0 +1,4 @@
1
+
2
+ // Exporting all types
3
+ export * from './types';
4
+ export * from './schemas';