react-doctor-cli-dev 1.0.3 → 1.0.4

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.
Binary file
@@ -1,69 +1,26 @@
1
1
  "use strict";
2
- // ─────────────────────────────────────────────────────────────
3
- // backend/src/db.ts
4
- //
5
- // SQLite database via better-sqlite3 (synchronous, fast).
6
- //
7
- // SCHEMA:
8
- //
9
- // reports — one row per analysis run
10
- // id — auto-increment PK
11
- // project — project name / hostname
12
- // score — 0-100 overall performance score
13
- // grade — A+, A, B, C, D, F
14
- // analyzed_at — ISO timestamp from the CLI
15
- // created_at — when the row was inserted
16
- // static_json — full StaticReport as JSON string
17
- // runtime_json — full RuntimeReport map as JSON string
18
- // (screenshots stripped — stored as files)
19
- // suggestions — full Suggestion[] as JSON string
20
- //
21
- // WHY SPLIT COLUMNS INSTEAD OF ONE payload COLUMN?
22
- // The old schema stored everything in a single `payload` TEXT
23
- // column. This meant the dashboard couldn't query individual
24
- // fields without parsing the whole blob. Splitting into three
25
- // typed columns lets us:
26
- // • SELECT only the part we need (e.g. just static_json for
27
- // the issues page)
28
- // • Keep the list endpoint fast (no giant blobs per row)
29
- // • Add indices on score/grade later without changing schema
30
- //
31
- // SCREENSHOT STORAGE:
32
- // Screenshots are base64 PNGs — up to 200KB each. Storing them
33
- // in SQLite bloats the DB and makes every query slower. Instead:
34
- // • The upload route strips dataUrls from runtime_json
35
- // • Saves each screenshot as a .png file in data/screenshots/
36
- // • Replaces dataUrl with a relative path /screenshots/<file>
37
- // The dashboard fetches screenshots via the static file route.
38
- // ─────────────────────────────────────────────────────────────
39
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
40
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
41
4
  };
42
5
  Object.defineProperty(exports, "__esModule", { value: true });
43
- exports.screenshotsDir = void 0;
44
6
  const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
45
7
  const path_1 = __importDefault(require("path"));
46
8
  const fs_1 = __importDefault(require("fs"));
47
- const dotenv_1 = __importDefault(require("dotenv"));
48
- dotenv_1.default.config({ path: path_1.default.join(__dirname, "..", ".env") });
49
- const PACKAGE_ROOT = path_1.default.resolve(__dirname, "..", "..");
50
- const DEFAULT_DB = path_1.default.join(PACKAGE_ROOT, "backend", "data", "reports.db");
51
- const dbPath = process.env.DB_PATH || DEFAULT_DB;
9
+ const os_1 = __importDefault(require("os"));
10
+ // Store DB in user's home directory — works regardless of where process spawns from
11
+ const dbDir = path_1.default.join(os_1.default.homedir(), ".react-doctor");
12
+ const dbPath = process.env.DB_PATH || path_1.default.join(dbDir, "reports.db");
52
13
  fs_1.default.mkdirSync(path_1.default.dirname(dbPath), { recursive: true });
53
- fs_1.default.mkdirSync(path_1.default.join(path_1.default.dirname(dbPath), "screenshots"), { recursive: true });
54
14
  const db = new better_sqlite3_1.default(dbPath);
55
15
  db.exec(`
56
- CREATE TABLE IF NOT EXISTS reports (
57
- id INTEGER PRIMARY KEY AUTOINCREMENT,
58
- project TEXT NOT NULL,
59
- score INTEGER NOT NULL,
60
- grade TEXT NOT NULL,
61
- analyzed_at TEXT NOT NULL,
62
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
63
- static_json TEXT NOT NULL,
64
- runtime_json TEXT NOT NULL,
65
- suggestions TEXT NOT NULL
66
- );
16
+ CREATE TABLE IF NOT EXISTS reports (
17
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
18
+ project TEXT NOT NULL,
19
+ score INTEGER NOT NULL,
20
+ grade TEXT NOT NULL,
21
+ analyzed_at TEXT NOT NULL,
22
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
23
+ payload TEXT NOT NULL
24
+ );
67
25
  `);
68
- exports.screenshotsDir = path_1.default.join(path_1.default.dirname(dbPath), "screenshots");
69
26
  exports.default = db;
@@ -9,34 +9,27 @@ const helmet_1 = __importDefault(require("helmet"));
9
9
  const dotenv_1 = __importDefault(require("dotenv"));
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const reports_1 = __importDefault(require("./routes/reports"));
12
- const db_1 = require("./db");
13
- dotenv_1.default.config({ path: path_1.default.join(__dirname, "..", ".env") });
12
+ // Load .env from the backend folder, not from cwd
13
+ dotenv_1.default.config({ path: path_1.default.join(__dirname, '..', '.env') });
14
14
  const app = (0, express_1.default)();
15
15
  const PORT = process.env.PORT || 3000;
16
- // ── Middleware ────────────────────────────────────────────────
16
+ const API_KEY = process.env.API_KEY || "react-doctor-secret-key-change-this";
17
+ // Make API_KEY available globally so auth middleware can use it
18
+ process.env.API_KEY = API_KEY;
17
19
  app.use((0, helmet_1.default)());
18
20
  app.use((0, cors_1.default)());
19
- app.use(express_1.default.json({ limit: "50mb" }));
20
- // ── Static: screenshots saved by the upload route ────────────
21
- // Served at /screenshots/<filename>
22
- // The dashboard uses these as <img src="/screenshots/...">
23
- app.use("/screenshots", express_1.default.static(db_1.screenshotsDir));
24
- // ── Routes ────────────────────────────────────────────────────
25
- app.use("/api/reports", reports_1.default);
26
- // ── Health check ──────────────────────────────────────────────
27
- app.get("/health", (_req, res) => {
28
- res.json({ status: "ok", timestamp: new Date().toISOString() });
21
+ app.use(express_1.default.json({ limit: '50mb' }));
22
+ app.use('/api/reports', reports_1.default);
23
+ app.get('/health', (_req, res) => {
24
+ res.json({ status: 'ok', timestamp: new Date().toISOString() });
29
25
  });
30
- // ── 404 ───────────────────────────────────────────────────────
31
- app.use((_req, res) => {
32
- res.status(404).json({ message: "Route not found" });
26
+ app.use((req, res) => {
27
+ res.status(404).json({ message: 'Route not found' });
33
28
  });
34
- // ── Error handler ─────────────────────────────────────────────
35
29
  app.use((err, _req, res, _next) => {
36
30
  console.error(err.stack);
37
- res.status(500).json({ message: "Internal Server Error" });
31
+ res.status(500).json({ message: 'Internal Server Error' });
38
32
  });
39
- // ── Start ─────────────────────────────────────────────────────
40
33
  app.listen(PORT, () => {
41
- console.log(`React Doctor backend running on http://localhost:${PORT}`);
34
+ console.log(`🩺 React Doctor backend running on http://localhost:${PORT}`);
42
35
  });
@@ -31,39 +31,6 @@
31
31
  // /screenshots/<filename>
32
32
  // so the dashboard can load them as normal <img> tags.
33
33
  // ─────────────────────────────────────────────────────────────
34
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
35
- if (k2 === undefined) k2 = k;
36
- var desc = Object.getOwnPropertyDescriptor(m, k);
37
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
38
- desc = { enumerable: true, get: function() { return m[k]; } };
39
- }
40
- Object.defineProperty(o, k2, desc);
41
- }) : (function(o, m, k, k2) {
42
- if (k2 === undefined) k2 = k;
43
- o[k2] = m[k];
44
- }));
45
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
46
- Object.defineProperty(o, "default", { enumerable: true, value: v });
47
- }) : function(o, v) {
48
- o["default"] = v;
49
- });
50
- var __importStar = (this && this.__importStar) || (function () {
51
- var ownKeys = function(o) {
52
- ownKeys = Object.getOwnPropertyNames || function (o) {
53
- var ar = [];
54
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
55
- return ar;
56
- };
57
- return ownKeys(o);
58
- };
59
- return function (mod) {
60
- if (mod && mod.__esModule) return mod;
61
- var result = {};
62
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
63
- __setModuleDefault(result, mod);
64
- return result;
65
- };
66
- })();
67
34
  var __importDefault = (this && this.__importDefault) || function (mod) {
68
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
69
36
  };
@@ -71,8 +38,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
71
38
  const express_1 = require("express");
72
39
  const path_1 = __importDefault(require("path"));
73
40
  const fs_1 = __importDefault(require("fs"));
74
- const db_1 = __importStar(require("../db"));
41
+ const db_1 = __importDefault(require("../db"));
75
42
  const auth_1 = require("../middleware/auth");
43
+ const screenshotsDir = "data/screenshots";
76
44
  const router = (0, express_1.Router)();
77
45
  // ── GET /api/reports ─────────────────────────────────────────
78
46
  // Summary list — no blobs, just the columns the history page needs.
@@ -241,7 +209,7 @@ function saveScreenshots(reportId, pending) {
241
209
  const safeRoute = shot.routeKey.replace(/[/:]/g, "-").replace(/^-+/, "");
242
210
  const safeLabel = shot.label.replace(/[^a-z0-9]/gi, "-");
243
211
  const filename = `${reportId}-${safeRoute}-${safeLabel}.png`;
244
- const fullPath = path_1.default.join(db_1.screenshotsDir, filename);
212
+ const fullPath = path_1.default.join(screenshotsDir, filename);
245
213
  try {
246
214
  fs_1.default.writeFileSync(fullPath, shot.buffer);
247
215
  saved.push({
package/backend/src/db.ts CHANGED
@@ -1,70 +1,25 @@
1
- // ─────────────────────────────────────────────────────────────
2
- // backend/src/db.ts
3
- //
4
- // SQLite database via better-sqlite3 (synchronous, fast).
5
- //
6
- // SCHEMA:
7
- //
8
- // reports — one row per analysis run
9
- // id — auto-increment PK
10
- // project — project name / hostname
11
- // score — 0-100 overall performance score
12
- // grade — A+, A, B, C, D, F
13
- // analyzed_at — ISO timestamp from the CLI
14
- // created_at — when the row was inserted
15
- // static_json — full StaticReport as JSON string
16
- // runtime_json — full RuntimeReport map as JSON string
17
- // (screenshots stripped — stored as files)
18
- // suggestions — full Suggestion[] as JSON string
19
- //
20
- // WHY SPLIT COLUMNS INSTEAD OF ONE payload COLUMN?
21
- // The old schema stored everything in a single `payload` TEXT
22
- // column. This meant the dashboard couldn't query individual
23
- // fields without parsing the whole blob. Splitting into three
24
- // typed columns lets us:
25
- // • SELECT only the part we need (e.g. just static_json for
26
- // the issues page)
27
- // • Keep the list endpoint fast (no giant blobs per row)
28
- // • Add indices on score/grade later without changing schema
29
- //
30
- // SCREENSHOT STORAGE:
31
- // Screenshots are base64 PNGs — up to 200KB each. Storing them
32
- // in SQLite bloats the DB and makes every query slower. Instead:
33
- // • The upload route strips dataUrls from runtime_json
34
- // • Saves each screenshot as a .png file in data/screenshots/
35
- // • Replaces dataUrl with a relative path /screenshots/<file>
36
- // The dashboard fetches screenshots via the static file route.
37
- // ─────────────────────────────────────────────────────────────
38
-
39
1
  import Database from "better-sqlite3";
40
- import path from "path";
41
- import fs from "fs";
42
- import dotenv from "dotenv";
43
-
44
- dotenv.config({ path: path.join(__dirname, "..", ".env") });
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import os from "os";
45
5
 
46
- const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
47
- const DEFAULT_DB = path.join(PACKAGE_ROOT, "backend", "data", "reports.db");
48
- const dbPath = process.env.DB_PATH || DEFAULT_DB;
6
+ // Store DB in user's home directory — works regardless of where process spawns from
7
+ const dbDir = path.join(os.homedir(), ".react-doctor");
8
+ const dbPath = process.env.DB_PATH || path.join(dbDir, "reports.db");
49
9
 
50
10
  fs.mkdirSync(path.dirname(dbPath), { recursive: true });
51
- fs.mkdirSync(path.join(path.dirname(dbPath), "screenshots"), { recursive: true });
52
11
 
53
12
  const db = new Database(dbPath);
54
-
55
13
  db.exec(`
56
- CREATE TABLE IF NOT EXISTS reports (
57
- id INTEGER PRIMARY KEY AUTOINCREMENT,
58
- project TEXT NOT NULL,
59
- score INTEGER NOT NULL,
60
- grade TEXT NOT NULL,
61
- analyzed_at TEXT NOT NULL,
62
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
63
- static_json TEXT NOT NULL,
64
- runtime_json TEXT NOT NULL,
65
- suggestions TEXT NOT NULL
66
- );
14
+ CREATE TABLE IF NOT EXISTS reports (
15
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
16
+ project TEXT NOT NULL,
17
+ score INTEGER NOT NULL,
18
+ grade TEXT NOT NULL,
19
+ analyzed_at TEXT NOT NULL,
20
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
21
+ payload TEXT NOT NULL
22
+ );
67
23
  `);
68
24
 
69
- export const screenshotsDir = path.join(path.dirname(dbPath), "screenshots");
70
- export default db;
25
+ export default db;
@@ -1,46 +1,39 @@
1
- import express, { Request, Response, NextFunction } from "express";
2
- import cors from "cors";
3
- import helmet from "helmet";
4
- import dotenv from "dotenv";
5
- import path from "path";
6
- import reportRoutes from "./routes/reports";
7
- import db, { screenshotsDir } from "./db";
8
-
9
- dotenv.config({ path: path.join(__dirname, "..", ".env") });
10
-
11
- const app = express();
1
+ import express, { Request, Response, NextFunction } from 'express';
2
+ import cors from 'cors';
3
+ import helmet from 'helmet';
4
+ import dotenv from 'dotenv';
5
+ import path from 'path';
6
+ import reportRoutes from './routes/reports';
7
+ import db from './db';
8
+
9
+ // Load .env from the backend folder, not from cwd
10
+ dotenv.config({ path: path.join(__dirname, '..', '.env') });
11
+
12
+ const app = express();
12
13
  const PORT = process.env.PORT || 3000;
14
+ const API_KEY = process.env.API_KEY || "react-doctor-secret-key-change-this";
15
+
16
+ // Make API_KEY available globally so auth middleware can use it
17
+ process.env.API_KEY = API_KEY;
13
18
 
14
- // ── Middleware ────────────────────────────────────────────────
15
19
  app.use(helmet());
16
20
  app.use(cors());
17
- app.use(express.json({ limit: "50mb" }));
21
+ app.use(express.json({ limit: '50mb' }));
22
+ app.use('/api/reports', reportRoutes);
18
23
 
19
- // ── Static: screenshots saved by the upload route ────────────
20
- // Served at /screenshots/<filename>
21
- // The dashboard uses these as <img src="/screenshots/...">
22
- app.use("/screenshots", express.static(screenshotsDir));
23
-
24
- // ── Routes ────────────────────────────────────────────────────
25
- app.use("/api/reports", reportRoutes);
26
-
27
- // ── Health check ──────────────────────────────────────────────
28
- app.get("/health", (_req: Request, res: Response) => {
29
- res.json({ status: "ok", timestamp: new Date().toISOString() });
24
+ app.get('/health', (_req: Request, res: Response) => {
25
+ res.json({ status: 'ok', timestamp: new Date().toISOString() });
30
26
  });
31
27
 
32
- // ── 404 ───────────────────────────────────────────────────────
33
- app.use((_req: Request, res: Response) => {
34
- res.status(404).json({ message: "Route not found" });
28
+ app.use((req: Request, res: Response) => {
29
+ res.status(404).json({ message: 'Route not found' });
35
30
  });
36
31
 
37
- // ── Error handler ─────────────────────────────────────────────
38
32
  app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
39
33
  console.error(err.stack);
40
- res.status(500).json({ message: "Internal Server Error" });
34
+ res.status(500).json({ message: 'Internal Server Error' });
41
35
  });
42
36
 
43
- // ── Start ─────────────────────────────────────────────────────
44
37
  app.listen(PORT, () => {
45
- console.log(`React Doctor backend running on http://localhost:${PORT}`);
46
- });
38
+ console.log(`🩺 React Doctor backend running on http://localhost:${PORT}`);
39
+ });
@@ -34,9 +34,11 @@
34
34
  import { Router, Request, Response, RequestHandler } from "express";
35
35
  import path from "path";
36
36
  import fs from "fs";
37
- import db, { screenshotsDir } from "../db";
37
+ import db from "../db";
38
38
  import { requireApiKey } from "../middleware/auth";
39
39
 
40
+ const screenshotsDir = "data/screenshots";
41
+
40
42
  const router = Router();
41
43
 
42
44
  // ── GET /api/reports ─────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor-cli-dev",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "React performance analyzer with static analysis, runtime profiling, rule engine, and dashboard upload",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",