react-doctor-cli-dev 1.0.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.
Files changed (82) hide show
  1. package/backend/.env +3 -0
  2. package/backend/dist/index.js +43 -0
  3. package/backend/dist/middleware/auth.js +16 -0
  4. package/backend/dist/routes/reports.js +93 -0
  5. package/backend/package-lock.json +2000 -0
  6. package/backend/package.json +30 -0
  7. package/backend/src/db.ts +24 -0
  8. package/backend/src/index.ts +49 -0
  9. package/backend/src/middleware/auth.ts +21 -0
  10. package/backend/src/routes/reports.ts +110 -0
  11. package/backend/tsconfig.json +12 -0
  12. package/cli/bin/react-doctor.js +29 -0
  13. package/cli/dist/commands/analyze.js +125 -0
  14. package/cli/dist/commands/full.js +366 -0
  15. package/cli/dist/commands/install.js +138 -0
  16. package/cli/dist/commands/profile.js +166 -0
  17. package/cli/dist/index.js +78 -0
  18. package/cli/dist/ui.js +113 -0
  19. package/cli/package-lock.json +936 -0
  20. package/cli/package.json +34 -0
  21. package/cli/src/commands/analyze.ts +162 -0
  22. package/cli/src/commands/full.ts +574 -0
  23. package/cli/src/commands/install.ts +163 -0
  24. package/cli/src/commands/profile.ts +246 -0
  25. package/cli/src/index.ts +84 -0
  26. package/cli/src/ui.ts +120 -0
  27. package/cli/tsconfig.json +16 -0
  28. package/core/report-compiler/index.ts +359 -0
  29. package/core/report-compiler/test-report-compiler.ts +126 -0
  30. package/core/rule-engine/context-builder.ts +146 -0
  31. package/core/rule-engine/evaluator.ts +131 -0
  32. package/core/rule-engine/index.ts +222 -0
  33. package/core/rule-engine/rules.json +304 -0
  34. package/core/rule-engine/suggestion-builder.ts +209 -0
  35. package/core/rule-engine/test-rule-engine.ts +144 -0
  36. package/core/rule-engine/types.ts +202 -0
  37. package/core/runtime/profiler/browser.ts +121 -0
  38. package/core/runtime/profiler/collectors.ts +216 -0
  39. package/core/runtime/profiler/index.ts +311 -0
  40. package/core/runtime/profiler/porfiler.ts +967 -0
  41. package/core/runtime/profiler/route-scanner.ts +76 -0
  42. package/core/runtime/profiler/score.ts +59 -0
  43. package/core/runtime/profiler/server.ts +115 -0
  44. package/core/runtime/profiler/types.ts +65 -0
  45. package/core/runtime/test-runtime-profiler.ts +226 -0
  46. package/core/static-ana/static/analyzer.ts +145 -0
  47. package/core/static-ana/static/ast-parser.ts +31 -0
  48. package/core/static-ana/static/detectors/console-log.ts +49 -0
  49. package/core/static-ana/static/detectors/dead-code.ts +51 -0
  50. package/core/static-ana/static/detectors/effect-loop.ts +45 -0
  51. package/core/static-ana/static/detectors/index.ts +16 -0
  52. package/core/static-ana/static/detectors/inline-function.ts +59 -0
  53. package/core/static-ana/static/detectors/inline-style.ts +52 -0
  54. package/core/static-ana/static/detectors/large-component.ts +79 -0
  55. package/core/static-ana/static/detectors/missing-key.ts +56 -0
  56. package/core/static-ana/static/detectors/missing-memo.ts +59 -0
  57. package/core/static-ana/static/detectors/prop-drilling.ts +66 -0
  58. package/core/static-ana/static/helpers.ts +81 -0
  59. package/core/static-ana/static/scanner.ts +93 -0
  60. package/core/static-ana/test-analyzer.ts +115 -0
  61. package/core/static-ana/types.ts +25 -0
  62. package/core/tests/mock-react-project/src/app.tsx +22 -0
  63. package/core/tests/mock-react-project/src/components/Button.tsx +9 -0
  64. package/core/tests/mock-react-project/src/components/Header.tsx +3 -0
  65. package/core/tests/mock-react-project/src/components/ListTesting.tsx +51 -0
  66. package/core/tests/mock-react-project/src/components/UserDashboard.tsx +66 -0
  67. package/core/tests/mock-react-project/src/utils.ts +4 -0
  68. package/package.json +55 -0
  69. package/react-doctor-cli-dev-1.0.0.tgz +0 -0
  70. package/shared/dist/index.d.ts +2 -0
  71. package/shared/dist/index.js +19 -0
  72. package/shared/dist/schemas.d.ts +91 -0
  73. package/shared/dist/schemas.js +82 -0
  74. package/shared/dist/types.d.ts +44 -0
  75. package/shared/dist/types.js +2 -0
  76. package/shared/package-lock.json +47 -0
  77. package/shared/package.json +21 -0
  78. package/shared/src/index.ts +4 -0
  79. package/shared/src/schemas.ts +136 -0
  80. package/shared/src/types.ts +137 -0
  81. package/shared/tsconfig.json +15 -0
  82. package/tsconfig.json +25 -0
@@ -0,0 +1,93 @@
1
+ import { glob } from "glob";
2
+ import path from "path";
3
+
4
+ export interface ScannedFile {
5
+ path: string; // Full absolute path
6
+ relativePath: string; // Relative to project root
7
+ name: string; // Filename only
8
+ extension: string; // .tsx or .jsx
9
+ }
10
+
11
+ export class FileScanner {
12
+ /**
13
+ * Find all React component files (.jsx, .tsx) in a project
14
+ * @param projectPath - Absolute or relative path to React project root
15
+ * @returns Array of scanned file metadata
16
+ */
17
+ async findFiles(projectPath: string): Promise<ScannedFile[]> {
18
+ // 1. Ensure the path is absolute and uses forward slashes
19
+ const normalizedPath = path.resolve(projectPath).replace(/\\/g, "/");
20
+
21
+ // 2. Try to scan src/ folder first (standard React location)
22
+ const srcPattern = `${normalizedPath}/src/**/*.{ts,js,jsx,tsx}`;
23
+
24
+ try {
25
+ // 3. Run the glob search (focusing on src/ folder)
26
+ let files = await glob(srcPattern, {
27
+ ignore: [
28
+ "**/node_modules/**",
29
+ "**/dist/**",
30
+ "**/build/**",
31
+ "**/.next/**",
32
+ "**/coverage/**",
33
+ "**/*.test.{jsx,tsx}", // Skip test files
34
+ "**/*.spec.{jsx,tsx}", // Skip spec files
35
+ "**/*.stories.{jsx,tsx}", // Skip Storybook files
36
+ ],
37
+ nodir: true, // We only want files, not folders
38
+ absolute: true, // Return absolute paths
39
+ });
40
+
41
+ // 4. If no files found in src/, try scanning entire project as fallback
42
+ if (files.length === 0) {
43
+ console.warn(`⚠️ No files found in src/, scanning entire project...`);
44
+ const rootPattern = `${normalizedPath}/**/*.{ts,js,jsx,tsx}`;
45
+
46
+ files = await glob(rootPattern, {
47
+ ignore: [
48
+ "**/node_modules/**",
49
+ "**/dist/**",
50
+ "**/build/**",
51
+ "**/.next/**",
52
+ "**/coverage/**",
53
+ "**/*.test.{jsx,tsx}",
54
+ "**/*.spec.{jsx,tsx}",
55
+ "**/*.stories.{jsx,tsx}",
56
+ ],
57
+ nodir: true,
58
+ absolute: true,
59
+ });
60
+ }
61
+
62
+ console.log(`✅ Found ${files.length} React component files`);
63
+
64
+ // 5. Map to structured metadata
65
+ return files.map(filePath => ({
66
+ path: filePath,
67
+ relativePath: path.relative(normalizedPath, filePath),
68
+ name: path.basename(filePath),
69
+ extension: path.extname(filePath),
70
+ }));
71
+
72
+ } catch (error) {
73
+ console.error("❌ Scanner Error:", error);
74
+ throw new Error(`Failed to scan files: ${error instanceof Error ? error.message : 'Unknown error'}`);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Get statistics about scanned files
80
+ */
81
+ getStats(files: ScannedFile[]): {
82
+ total: number;
83
+ byExtension: Record<string, number>;
84
+ } {
85
+ return {
86
+ total: files.length,
87
+ byExtension: files.reduce((acc, file) => {
88
+ acc[file.extension] = (acc[file.extension] || 0) + 1;
89
+ return acc;
90
+ }, {} as Record<string, number>),
91
+ };
92
+ }
93
+ }
@@ -0,0 +1,115 @@
1
+ import { FileScanner } from './static/scanner';
2
+ import { StaticAnalyzer } from './static/analyzer';
3
+ import path from 'path';
4
+
5
+ export async function testCompleteAnalysis() {
6
+ console.log("\n" + "=".repeat(70));
7
+ console.log("🩺 React Doctor - Complete Static Analysis Test");
8
+ console.log("=".repeat(70));
9
+
10
+ try {
11
+ const scanner = new FileScanner();
12
+ const targetDir = path.join(process.cwd(), 'tests', 'mock-react-project');
13
+
14
+ const files = await scanner.findFiles(targetDir);
15
+
16
+ if (files.length === 0) {
17
+ console.log("⚠️ No React files found!");
18
+ return;
19
+ }
20
+
21
+ const analyzer = new StaticAnalyzer();
22
+ const report = await analyzer.analyze(files);
23
+
24
+ // ======================================================================
25
+ // 🏆 NEW: HEALTH GRADE SECTION
26
+ // ======================================================================
27
+ console.log("\n" + "=".repeat(70));
28
+ console.log(`🏆 PROJECT HEALTH GRADE: ${report.grade}`);
29
+ console.log("=".repeat(70));
30
+ console.log(getDoctorRecommendation(report.grade));
31
+ console.log("=".repeat(70));
32
+
33
+ // Display basic stats
34
+ console.log(`\n📊 Analysis Summary:`);
35
+ console.log(`Timestamp: ${new Date(report.timestamp).toLocaleString()}`);
36
+ console.log(`Files analyzed: ${report.filesAnalyzed}`);
37
+ console.log(`Total issues: ${report.issues.length}`);
38
+
39
+ const summary = analyzer.getSummary(report);
40
+ console.log(`\n🎯 Issues by Severity:`);
41
+ console.log(` 🔴 Critical: ${summary.critical}`);
42
+ console.log(` 🟡 Warnings: ${summary.warnings}`);
43
+ console.log(` 🔵 Info: ${summary.info}`);
44
+
45
+ console.log(`\n🔍 Issues by Type:`);
46
+ Object.entries(summary.byDetector).forEach(([type, count]) => {
47
+ console.log(` ${type.padEnd(10)}: ${count}`);
48
+ });
49
+
50
+ // Display issues with corrected math
51
+ const DISPLAY_LIMIT = 30;
52
+ if (report.issues.length > 0) {
53
+ console.log(`\n📋 Detailed Issues (showing up to ${DISPLAY_LIMIT}):`);
54
+ console.log("-".repeat(70));
55
+
56
+ report.issues.slice(0, DISPLAY_LIMIT).forEach((issue, index) => {
57
+ console.log(`\n${index + 1}. ${issue.file}:${issue.line}:${issue.column || 0}`);
58
+ console.log(` Component: ${issue.component}`);
59
+ console.log(` Severity: ${getSeverityIcon(issue.severity)} ${issue.severity}`);
60
+ console.log(` Message: ${issue.message}`);
61
+ console.log(` 💡 Fix: ${issue.suggestion}`);
62
+ });
63
+
64
+ // Fixed the negative math bug here:
65
+ if (report.issues.length > DISPLAY_LIMIT) {
66
+ console.log(`\n... and ${report.issues.length - DISPLAY_LIMIT} more issue(s)`);
67
+ }
68
+ } else {
69
+ console.log(`\n✨ No issues found! Your code is in peak condition!`);
70
+ }
71
+
72
+ console.log("\n" + "=".repeat(70));
73
+ const reportPath = analyzer.saveReport(report);
74
+ console.log("\n" + "=".repeat(70));
75
+ console.log(`💾 JSON Report saved to: ${path.basename(reportPath)}`);
76
+ console.log("=".repeat(70));
77
+
78
+ } catch (error) {
79
+ console.error("\n❌ Test Failed:");
80
+ console.error((error as Error).message);
81
+ process.exit(1);
82
+ }
83
+ }
84
+
85
+ function getSeverityIcon(severity: string): string {
86
+ switch (severity) {
87
+ case 'critical': return '🔴';
88
+ case 'warning': return '🟡';
89
+ case 'info': return '🔵';
90
+ default: return '⚪';
91
+ }
92
+ }
93
+
94
+ /**
95
+ * 🩺 Provides a clinical recommendation based on the grade
96
+ */
97
+ function getDoctorRecommendation(grade: string): string {
98
+ switch (grade) {
99
+ case 'A+': case 'A':
100
+ return "✅ DOCTOR'S NOTE: Excellent health. Continue with regular checkups.";
101
+ case 'B':
102
+ return "🟡 DOCTOR'S NOTE: Minor congestion detected. Consider cleaning up warnings.";
103
+ case 'C':
104
+ return "⚠️ DOCTOR'S NOTE: Chronic issues found. Technical debt is accumulating.";
105
+ case 'D':
106
+ return "🟠 DOCTOR'S NOTE: Patient in poor condition. Immediate refactoring advised.";
107
+ case 'F':
108
+ return "🚨 DOCTOR'S NOTE: CRITICAL CONDITION. Fix infinite loops and memory leaks immediately!";
109
+ default:
110
+ return "🩺 DOCTOR'S NOTE: Analysis inconclusive.";
111
+ }
112
+ }
113
+
114
+ testCompleteAnalysis();
115
+
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Represents a single issue found in a component
3
+ */
4
+ export interface ComponentIssue {
5
+ id: string; // Unique identifier (e.g., "large-component-App.tsx-12")
6
+ component: string; // Component name (e.g., "UserDashboard")
7
+ file: string; // File path
8
+ line: number; // Line number where issue was found
9
+ column?: number; // Column number (optional)
10
+ severity: 'critical' | 'warning' | 'info';
11
+ message: string; // What's wrong (e.g., "Component is 450 lines")
12
+ suggestion: string; // How to fix it (e.g., "Split into smaller components")
13
+ }
14
+
15
+ /**
16
+ * Complete static analysis report
17
+ */
18
+ export interface StaticReport {
19
+ timestamp: string;
20
+ componentCount: number; // Number of components analyzed
21
+ issues: ComponentIssue[];
22
+ filesAnalyzed: number;
23
+ filesFailed: number;
24
+ grade: string;
25
+ }
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+
3
+ // This component has multiple issues for testing
4
+ export default function App() {
5
+ console.log('App rendered'); // Issue: console.log
6
+
7
+ return (
8
+ <div>
9
+ <h1>React Doctor Test App</h1>
10
+
11
+ {/* Issue: Inline function */}
12
+ <button onClick={() => console.log('clicked')}>
13
+ Click Me
14
+ </button>
15
+
16
+ {/* Another inline function */}
17
+ <button onClick={() => alert('test')}>
18
+ Alert
19
+ </button>
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,9 @@
1
+ export default function Button() {
2
+ console.warn('Button component');
3
+
4
+ return (
5
+ <button onClick={() => console.log('inline')}>
6
+ Button
7
+ </button>
8
+ );
9
+ }
@@ -0,0 +1,3 @@
1
+ export default function Header() {
2
+ return <header>Header Component</header>;
3
+ }
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+
3
+ export default function ListTesting() {
4
+ const items = ['Apple', 'Banana', 'Cherry'];
5
+
6
+ // 1. ❌ THE DISEASE: Map Failure
7
+ const renderMap = () => {
8
+ return items.map(item => (
9
+ <div>{item}</div>
10
+ /* ✅ THE CURE:
11
+ <div key={item}>{item}</div>
12
+ */
13
+ ));
14
+ };
15
+
16
+ // 2. ❌ THE DISEASE: Literal Array Failure
17
+ const staticList = [
18
+ <span>First Item</span>,
19
+ <span>Second Item</span>,
20
+ /* ✅ THE CURE:
21
+ <span key="first">First Item</span>,
22
+ <span key="second">Second Item</span>,
23
+ */
24
+ ];
25
+
26
+ return (
27
+ <div className="list-container">
28
+ {renderMap()}
29
+
30
+ <ul>
31
+ {/* 3. ❌ THE DISEASE: Array.from Failure */}
32
+ {Array.from({ length: 3 }).map((_, i) => (
33
+ <li>Item {i}</li>
34
+ /* ✅ THE CURE:
35
+ <li key={i}>Item {i}</li>
36
+ (Note: Use 'i' only if the list order is static!)
37
+ */
38
+ ))}
39
+ </ul>
40
+
41
+ <div className="static-section">
42
+ {staticList}
43
+ </div>
44
+
45
+ {/* 4. ✨ HEALTHY: Should NOT be flagged by the Doctor */}
46
+ {items.map(item => (
47
+ <p key={item}>{item}</p>
48
+ ))}
49
+ </div>
50
+ );
51
+ }
@@ -0,0 +1,66 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { fetchUser } from './api'; // Imagine this exists
3
+ import { GhostData } from './data'; // DEAD CODE: Unused import
4
+
5
+ /**
6
+ * 1. GRANDPARENT: Has the data
7
+ */
8
+ export default function UserDashboard({ theme }) {
9
+ const [user, setUser] = useState({ name: 'Shaheen', role: 'Admin' });
10
+ const [ticks, setTicks] = useState(0);
11
+ const unusedSecret = "12345"; // DEAD CODE: Unused variable
12
+
13
+ console.log("Dashboard rendering..."); // CONSOLE LOG
14
+
15
+ // 2. INFINITE LOOP: Missing dependency array
16
+ useEffect(() => {
17
+ setTicks(ticks + 1);
18
+ });
19
+
20
+ return (
21
+ <div className="dashboard">
22
+ <h1>User Panel</h1>
23
+ {/* Passing 'user' to Content (Drilling starts here) */}
24
+ <Content user={user} theme={theme} />
25
+ </div>
26
+ );
27
+ }
28
+
29
+ /**
30
+ * 3. PARENT (The Middleman): PROP DRILLING candidate
31
+ * Receives 'user', doesn't use it, just passes it to Profile.
32
+ */
33
+ function Content({ user, theme }) {
34
+ return (
35
+ <main
36
+ // 4. LARGE INLINE STYLE: > 5 properties
37
+ style={{
38
+ margin: '20px',
39
+ padding: '10px',
40
+ border: '1px solid black',
41
+ borderRadius: '5px',
42
+ backgroundColor: '#f0f0f0',
43
+ display: 'flex',
44
+ flexDirection: 'column'
45
+ }}
46
+ >
47
+ <h2>Main Content</h2>
48
+ <Profile user={user} theme={theme} />
49
+ </main>
50
+ );
51
+ }
52
+
53
+ /**
54
+ * 5. CHILD: Finally uses the data
55
+ */
56
+ function Profile({ user, theme }) {
57
+ return (
58
+ <div className={`profile-${theme}`}>
59
+ <p>Name: {user.name}</p>
60
+ {/* 6. INLINE FUNCTION */}
61
+ <button onClick={() => console.log('Deleted!')}>
62
+ Delete Account
63
+ </button>
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,4 @@
1
+ // This should be ignored (not a React component)
2
+ export function add(a: number, b: number) {
3
+ return a + b;
4
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "react-doctor-cli-dev",
3
+ "version": "1.0.0",
4
+ "description": "React performance analyzer with static analysis, runtime profiling, rule engine, and dashboard upload",
5
+ "main": "index.js",
6
+ "type": "commonjs",
7
+
8
+ "bin": {
9
+ "react-doctor": "./cli/bin/react-doctor.js"
10
+ },
11
+
12
+ "scripts": {},
13
+
14
+ "keywords": [
15
+ "react",
16
+ "performance",
17
+ "profiler",
18
+ "cli",
19
+ "analyzer"
20
+ ],
21
+
22
+ "author": "Ozma",
23
+ "license": "ISC",
24
+
25
+ "dependencies": {
26
+ "@babel/parser": "^7.29.0",
27
+ "@babel/traverse": "^7.29.0",
28
+ "@babel/types": "^7.29.0",
29
+ "axios": "^1.6.0",
30
+ "better-sqlite3": "^12.10.0",
31
+ "chalk": "^4.1.2",
32
+ "commander": "^12.0.0",
33
+ "cors": "^2.8.6",
34
+ "dotenv": "^17.4.2",
35
+ "express": "^5.2.1",
36
+ "fs-extra": "^11.3.4",
37
+ "glob": "^13.0.6",
38
+ "helmet": "^8.2.0",
39
+ "ora": "^5.4.1",
40
+ "puppeteer-core": "^22.0.0",
41
+ "ts-node": "^10.9.2",
42
+ "typescript": "^5.0.0",
43
+ "web-vitals": "^5.1.0"
44
+ },
45
+
46
+ "devDependencies": {
47
+ "@types/babel__traverse": "^7.28.0",
48
+ "@types/better-sqlite3": "^7.6.13",
49
+ "@types/cors": "^2.8.19",
50
+ "@types/express": "^5.0.6",
51
+ "@types/fs-extra": "^11.0.4",
52
+ "@types/node": "^25.9.1",
53
+ "nodemon": "^3.1.14"
54
+ }
55
+ }
Binary file
@@ -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
+ });