frontend-guardian-core 3.5.1 → 3.5.2

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.
@@ -0,0 +1,351 @@
1
+ "use strict";
2
+ /**
3
+ * Dashboard HTML Generator
4
+ *
5
+ * Generates a self-contained SPA that fetches data from the
6
+ * dashboard server APIs and renders charts using Canvas.
7
+ *
8
+ * Zero external dependencies: pure native JS + Canvas.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.generateDashboardHtml = generateDashboardHtml;
12
+ function generateDashboardHtml() {
13
+ return `<!DOCTYPE html>
14
+ <html lang="en">
15
+ <head>
16
+ <meta charset="UTF-8">
17
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
+ <title>Frontend Guardian Dashboard</title>
19
+ <style>
20
+ * { margin: 0; padding: 0; box-sizing: border-box; }
21
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background: #f5f7fa; color: #333; line-height: 1.6; }
22
+ .container { max-width: 1200px; margin: 0 auto; padding: 32px 24px; }
23
+ h1 { font-size: 28px; font-weight: 600; margin-bottom: 8px; color: #1a1a2e; }
24
+ .subtitle { color: #888; font-size: 14px; margin-bottom: 24px; }
25
+ .project-select { margin-bottom: 24px; }
26
+ .project-select select { padding: 10px 16px; font-size: 15px; border: 1px solid #ddd; border-radius: 8px; background: #fff; min-width: 300px; cursor: pointer; }
27
+ .project-select label { font-size: 14px; color: #666; margin-right: 8px; }
28
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; margin-bottom: 32px; }
29
+ .card { background: #fff; border-radius: 12px; padding: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); }
30
+ .card h3 { font-size: 13px; color: #888; margin-bottom: 12px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px; }
31
+ .big-number { font-size: 42px; font-weight: 700; color: #1a1a2e; }
32
+ .big-number.critical { color: #e74c3c; }
33
+ .big-number.warning { color: #f39c12; }
34
+ .big-number.suggestion { color: #3498db; }
35
+ .chart-container { background: #fff; border-radius: 12px; padding: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); margin-bottom: 20px; }
36
+ .chart-container h3 { font-size: 16px; margin-bottom: 16px; color: #1a1a2e; }
37
+ canvas { width: 100%; height: 300px; }
38
+ table { width: 100%; border-collapse: collapse; font-size: 14px; }
39
+ th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #eee; }
40
+ th { font-weight: 600; color: #666; font-size: 12px; text-transform: uppercase; }
41
+ tr:hover { background: #f9fafb; }
42
+ .severity-critical { color: #e74c3c; font-weight: 600; }
43
+ .severity-warning { color: #f39c12; font-weight: 600; }
44
+ .severity-suggestion { color: #3498db; font-weight: 600; }
45
+ .footer { text-align: center; color: #aaa; font-size: 12px; margin-top: 40px; padding-bottom: 24px; }
46
+ .empty { text-align: center; padding: 60px 20px; color: #999; }
47
+ .empty-icon { font-size: 48px; margin-bottom: 16px; }
48
+ .spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid #ddd; border-top-color: #3498db; border-radius: 50%; animation: spin 1s linear infinite; }
49
+ @keyframes spin { to { transform: rotate(360deg); } }
50
+ .no-data { color: #999; font-style: italic; padding: 20px; text-align: center; }
51
+ </style>
52
+ </head>
53
+ <body>
54
+ <div class="container">
55
+ <h1>Frontend Guardian Dashboard</h1>
56
+ <p class="subtitle">Multi-project governance trends and scan history</p>
57
+
58
+ <div class="project-select">
59
+ <label for="project-select">Project:</label>
60
+ <select id="project-select">
61
+ <option value="">Loading projects...</option>
62
+ </select>
63
+ <span id="loading" style="margin-left:12px;display:none;"><span class="spinner"></span></span>
64
+ </div>
65
+
66
+ <div id="dashboard-content">
67
+ <div class="empty">
68
+ <div class="empty-icon">&#128202;</div>
69
+ <p>Select a project to view dashboard</p>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="footer">
74
+ Frontend Guardian v3.5.2
75
+ </div>
76
+ </div>
77
+
78
+ <script>
79
+ // ── Dashboard SPA ─────────────────────────────────────────────────────
80
+ const projectSelect = document.getElementById('project-select');
81
+ const dashboardContent = document.getElementById('dashboard-content');
82
+ const loading = document.getElementById('loading');
83
+
84
+ let currentProjectId = null;
85
+
86
+ // Load projects on page load
87
+ async function loadProjects() {
88
+ try {
89
+ const res = await fetch('/api/projects');
90
+ const data = await res.json();
91
+ const projects = data.projects || [];
92
+
93
+ projectSelect.innerHTML = '';
94
+ if (projects.length === 0) {
95
+ projectSelect.innerHTML = '<option value="">No projects yet</option>';
96
+ dashboardContent.innerHTML = '<div class="empty"><div class="empty-icon">&#128230;</div><p>No scan reports received yet.</p><p style="font-size:13px;margin-top:8px;">Run: fg-core ./project --scan --server http://' + location.host + '</p></div>';
97
+ return;
98
+ }
99
+
100
+ const placeholder = document.createElement('option');
101
+ placeholder.value = '';
102
+ placeholder.textContent = 'Select a project...';
103
+ projectSelect.appendChild(placeholder);
104
+
105
+ for (const p of projects) {
106
+ const opt = document.createElement('option');
107
+ opt.value = p.id;
108
+ opt.textContent = p.name + ' (' + p.reportCount + ' reports)';
109
+ projectSelect.appendChild(opt);
110
+ }
111
+ } catch (err) {
112
+ projectSelect.innerHTML = '<option value="">Failed to load projects</option>';
113
+ }
114
+ }
115
+
116
+ projectSelect.addEventListener('change', async () => {
117
+ const id = projectSelect.value;
118
+ if (!id) {
119
+ dashboardContent.innerHTML = '<div class="empty"><div class="empty-icon">&#128202;</div><p>Select a project to view dashboard</p></div>';
120
+ return;
121
+ }
122
+ currentProjectId = id;
123
+ loading.style.display = 'inline-block';
124
+ await loadDashboard(id);
125
+ loading.style.display = 'none';
126
+ });
127
+
128
+ async function loadDashboard(projectId) {
129
+ try {
130
+ const [trendsRes, latestRes, reportsRes] = await Promise.all([
131
+ fetch('/api/projects/' + projectId + '/trends'),
132
+ fetch('/api/projects/' + projectId + '/latest'),
133
+ fetch('/api/projects/' + projectId + '/reports?limit=20'),
134
+ ]);
135
+
136
+ const trendsData = await trendsRes.json();
137
+ const latestData = await latestRes.json();
138
+ const reportsData = await reportsRes.json();
139
+
140
+ renderDashboard(trendsData.trends || [], latestData.report || null, reportsData.reports || []);
141
+ } catch (err) {
142
+ dashboardContent.innerHTML = '<div class="empty"><div class="empty-icon">&#9888;</div><p>Failed to load dashboard data</p><p style="font-size:13px;">' + String(err) + '</p></div>';
143
+ }
144
+ }
145
+
146
+ function renderDashboard(trends, latest, reports) {
147
+ const hasData = trends.length > 0;
148
+ const latestCounts = latest ? {
149
+ critical: latest.result.issues.critical.length,
150
+ warning: latest.result.issues.warning.length,
151
+ suggestion: latest.result.issues.suggestion.length,
152
+ } : { critical: 0, warning: 0, suggestion: 0 };
153
+
154
+ // Fix rate: compare first and last trend point
155
+ let fixRate = 0;
156
+ let trendDir = '-';
157
+ if (trends.length >= 2) {
158
+ const first = trends[0];
159
+ const last = trends[trends.length - 1];
160
+ const firstTotal = first.critical + first.warning + first.suggestion;
161
+ const lastTotal = last.critical + last.warning + last.suggestion;
162
+ fixRate = firstTotal > 0 ? Math.round(((firstTotal - lastTotal) / firstTotal) * 100) : 0;
163
+ trendDir = fixRate >= 0 ? 'Improving' : 'Worsening';
164
+ }
165
+
166
+ let html = '<div class="grid">';
167
+ html += '<div class="card"><h3>Critical</h3><div class="big-number critical">' + latestCounts.critical + '</div></div>';
168
+ html += '<div class="card"><h3>Warning</h3><div class="big-number warning">' + latestCounts.warning + '</div></div>';
169
+ html += '<div class="card"><h3>Suggestion</h3><div class="big-number suggestion">' + latestCounts.suggestion + '</div></div>';
170
+ html += '<div class="card"><h3>Trend</h3><div class="big-number">' + Math.abs(fixRate) + '%</div><span style="font-size:13px;color:#666;">' + trendDir + '</span></div>';
171
+ html += '</div>';
172
+
173
+ if (hasData) {
174
+ html += '<div class="chart-container"><h3>Issue Trends</h3><canvas id="trendChart"></canvas></div>';
175
+
176
+ html += '<div class="grid">';
177
+ html += '<div class="chart-container"><h3>Severity Distribution</h3><canvas id="severityChart"></canvas></div>';
178
+ html += '<div class="chart-container"><h3>Module Distribution</h3><canvas id="moduleChart"></canvas></div>';
179
+ html += '</div>';
180
+ }
181
+
182
+ // Scan history table
183
+ html += '<div class="chart-container"><h3>Scan History</h3>';
184
+ if (reports.length === 0) {
185
+ html += '<p class="no-data">No scan history</p>';
186
+ } else {
187
+ html += '<table><thead><tr><th>Time</th><th>Module</th><th>Critical</th><th>Warning</th><th>Suggestion</th></tr></thead><tbody>';
188
+ for (const r of reports) {
189
+ const d = new Date(r.timestamp);
190
+ const time = d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0') + ' ' + String(d.getHours()).padStart(2,'0') + ':' + String(d.getMinutes()).padStart(2,'0');
191
+ html += '<tr>';
192
+ html += '<td>' + time + '</td>';
193
+ html += '<td>' + r.module + '</td>';
194
+ html += '<td class="severity-critical">' + r.counts.critical + '</td>';
195
+ html += '<td class="severity-warning">' + r.counts.warning + '</td>';
196
+ html += '<td class="severity-suggestion">' + r.counts.suggestion + '</td>';
197
+ html += '</tr>';
198
+ }
199
+ html += '</tbody></table>';
200
+ }
201
+ html += '</div>';
202
+
203
+ dashboardContent.innerHTML = html;
204
+
205
+ if (hasData) {
206
+ drawTrendChart(trends);
207
+ drawSeverityChart(latestCounts);
208
+ drawModuleChart(latest ? latest.module : 'all');
209
+ }
210
+ }
211
+
212
+ function drawTrendChart(trends) {
213
+ const canvas = document.getElementById('trendChart');
214
+ if (!canvas) return;
215
+ const ctx = canvas.getContext('2d');
216
+ const dpr = window.devicePixelRatio || 1;
217
+ const rect = canvas.getBoundingClientRect();
218
+ canvas.width = rect.width * dpr;
219
+ canvas.height = rect.height * dpr;
220
+ ctx.scale(dpr, dpr);
221
+
222
+ const W = rect.width, H = rect.height;
223
+ const pad = { t: 30, r: 30, b: 50, l: 50 };
224
+ const gw = W - pad.l - pad.r, gh = H - pad.t - pad.b;
225
+
226
+ const maxVal = Math.max(...trends.map(d => d.total), 1);
227
+ const getX = i => pad.l + (i / (trends.length - 1 || 1)) * gw;
228
+ const getY = v => pad.t + gh - (v / maxVal) * gh;
229
+
230
+ // Grid
231
+ ctx.strokeStyle = '#eee'; ctx.lineWidth = 1;
232
+ for (let i = 0; i <= 4; i++) {
233
+ const y = pad.t + (i / 4) * gh;
234
+ ctx.beginPath(); ctx.moveTo(pad.l, y); ctx.lineTo(W - pad.r, y); ctx.stroke();
235
+ ctx.fillStyle = '#999'; ctx.font = '12px sans-serif';
236
+ ctx.fillText(Math.round(maxVal * (1 - i / 4)), 5, y + 4);
237
+ }
238
+
239
+ // Axes
240
+ ctx.strokeStyle = '#ddd';
241
+ ctx.beginPath(); ctx.moveTo(pad.l, pad.t); ctx.lineTo(pad.l, H - pad.b); ctx.stroke();
242
+ ctx.beginPath(); ctx.moveTo(pad.l, H - pad.b); ctx.lineTo(W - pad.r, H - pad.b); ctx.stroke();
243
+
244
+ // Lines
245
+ const colors = { critical: '#e74c3c', warning: '#f39c12', suggestion: '#3498db' };
246
+ ['critical', 'warning', 'suggestion'].forEach(key => {
247
+ ctx.strokeStyle = colors[key]; ctx.lineWidth = 2;
248
+ ctx.beginPath();
249
+ trends.forEach((d, i) => {
250
+ const x = getX(i), y = getY(d[key]);
251
+ if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
252
+ });
253
+ ctx.stroke();
254
+ ctx.fillStyle = colors[key];
255
+ trends.forEach((d, i) => {
256
+ const x = getX(i), y = getY(d[key]);
257
+ ctx.beginPath(); ctx.arc(x, y, 3, 0, Math.PI * 2); ctx.fill();
258
+ });
259
+ });
260
+
261
+ // X labels
262
+ ctx.fillStyle = '#999'; ctx.font = '11px sans-serif'; ctx.textAlign = 'center';
263
+ trends.forEach((d, i) => {
264
+ if (trends.length <= 10 || i % Math.ceil(trends.length / 10) === 0) {
265
+ const date = new Date(d.timestamp);
266
+ const label = String(date.getMonth()+1).padStart(2,'0') + '-' + String(date.getDate()).padStart(2,'0');
267
+ ctx.fillText(label, getX(i), H - pad.b + 20);
268
+ }
269
+ });
270
+
271
+ // Legend
272
+ const legend = [{c:'#e74c3c',l:'Critical'},{c:'#f39c12',l:'Warning'},{c:'#3498db',l:'Suggestion'}];
273
+ legend.forEach((item, i) => {
274
+ const lx = W - pad.r - 200 + i * 70;
275
+ ctx.fillStyle = item.c; ctx.fillRect(lx, 10, 12, 12);
276
+ ctx.fillStyle = '#666'; ctx.font = '12px sans-serif'; ctx.textAlign = 'left';
277
+ ctx.fillText(item.l, lx + 16, 21);
278
+ });
279
+ }
280
+
281
+ function drawSeverityChart(counts) {
282
+ const canvas = document.getElementById('severityChart');
283
+ if (!canvas) return;
284
+ const ctx = canvas.getContext('2d');
285
+ const dpr = window.devicePixelRatio || 1;
286
+ const rect = canvas.getBoundingClientRect();
287
+ canvas.width = rect.width * dpr; canvas.height = rect.height * dpr;
288
+ ctx.scale(dpr, dpr);
289
+ const W = rect.width, H = rect.height;
290
+ const pad = { t: 30, r: 30, b: 40, l: 50 };
291
+ const gw = W - pad.l - pad.r, gh = H - pad.t - pad.b;
292
+
293
+ const bars = [
294
+ { label: 'Critical', value: counts.critical, color: '#e74c3c' },
295
+ { label: 'Warning', value: counts.warning, color: '#f39c12' },
296
+ { label: 'Suggestion', value: counts.suggestion, color: '#3498db' },
297
+ ];
298
+ const maxVal = Math.max(...bars.map(b => b.value), 1);
299
+
300
+ bars.forEach((bar, i) => {
301
+ const bw = gw / bars.length * 0.5;
302
+ const bx = pad.l + (i + 0.5) * (gw / bars.length) - bw / 2;
303
+ const bh = (bar.value / maxVal) * gh;
304
+ const by = pad.t + gh - bh;
305
+ ctx.fillStyle = bar.color;
306
+ ctx.fillRect(bx, by, bw, bh);
307
+ ctx.fillStyle = '#555'; ctx.font = '13px sans-serif'; ctx.textAlign = 'center';
308
+ ctx.fillText(bar.label, bx + bw / 2, H - pad.b + 20);
309
+ ctx.fillStyle = '#333'; ctx.font = 'bold 14px sans-serif';
310
+ ctx.fillText(bar.value, bx + bw / 2, by - 8);
311
+ });
312
+
313
+ ctx.strokeStyle = '#eee'; ctx.lineWidth = 1;
314
+ for (let i = 0; i <= 4; i++) {
315
+ const y = pad.t + (i / 4) * gh;
316
+ ctx.beginPath(); ctx.moveTo(pad.l, y); ctx.lineTo(W - pad.r, y); ctx.stroke();
317
+ }
318
+ }
319
+
320
+ function drawModuleChart(moduleName) {
321
+ const canvas = document.getElementById('moduleChart');
322
+ if (!canvas) return;
323
+ const ctx = canvas.getContext('2d');
324
+ const dpr = window.devicePixelRatio || 1;
325
+ const rect = canvas.getBoundingClientRect();
326
+ canvas.width = rect.width * dpr; canvas.height = rect.height * dpr;
327
+ ctx.scale(dpr, dpr);
328
+ const W = rect.width, H = rect.height;
329
+
330
+ // Simple pie showing just the module name since we aggregate per-module
331
+ const cx = W / 2, cy = H / 2, r = Math.min(W, H) / 2 - 40;
332
+ ctx.fillStyle = '#3498db';
333
+ ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.fill();
334
+ ctx.fillStyle = '#fff'; ctx.font = 'bold 16px sans-serif'; ctx.textAlign = 'center';
335
+ ctx.fillText(moduleName, cx, cy + 6);
336
+ ctx.fillStyle = '#999'; ctx.font = '12px sans-serif';
337
+ ctx.fillText('Module', cx, cy + 22);
338
+ }
339
+
340
+ // Auto-refresh every 30 seconds
341
+ setInterval(() => {
342
+ if (currentProjectId) loadDashboard(currentProjectId);
343
+ }, 30000);
344
+
345
+ // Initial load
346
+ loadProjects();
347
+ </script>
348
+ </body>
349
+ </html>`;
350
+ }
351
+ //# sourceMappingURL=dashboard-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-html.js","sourceRoot":"","sources":["../../src/server/dashboard-html.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAEH,sDAkVC;AAlVD,SAAgB,qBAAqB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAgVH,CAAC;AACT,CAAC"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Governance Dashboard Server
3
+ *
4
+ * v3.5.2: Central HTTP server for collecting and displaying
5
+ * multi-project scan results.
6
+ *
7
+ * Zero external dependencies: uses node:http and node:fs only.
8
+ */
9
+ import { type Server } from "node:http";
10
+ import type { ScanResult, Issue } from "../types.js";
11
+ /** Project metadata stored on the server */
12
+ export interface DashboardProject {
13
+ id: string;
14
+ name: string;
15
+ path: string;
16
+ createdAt: number;
17
+ lastScanAt: number;
18
+ reportCount: number;
19
+ }
20
+ /** A single scan report stored on the server */
21
+ export interface DashboardReport {
22
+ id: string;
23
+ projectId: string;
24
+ timestamp: number;
25
+ module: string;
26
+ result: ScanResult;
27
+ issues: Issue[];
28
+ git?: {
29
+ commit?: string;
30
+ branch?: string;
31
+ };
32
+ meta?: {
33
+ strategy?: string;
34
+ duration?: number;
35
+ filesScanned?: number;
36
+ };
37
+ }
38
+ /** Server options */
39
+ export interface DashboardServerOptions {
40
+ /** Data directory (default: ~/.frontend-guardian-server) */
41
+ dataDir?: string;
42
+ /** CORS origin (default: no CORS headers) */
43
+ cors?: string;
44
+ /** Optional auth token for POST /api/reports */
45
+ authToken?: string;
46
+ }
47
+ /** POST /api/reports request body */
48
+ export interface ReportPayload {
49
+ projectName: string;
50
+ projectPath: string;
51
+ module: string;
52
+ result: ScanResult;
53
+ issues: Issue[];
54
+ git?: {
55
+ commit?: string;
56
+ branch?: string;
57
+ };
58
+ meta?: {
59
+ strategy?: string;
60
+ duration?: number;
61
+ filesScanned?: number;
62
+ };
63
+ }
64
+ /** Trend data point */
65
+ export interface TrendPoint {
66
+ timestamp: number;
67
+ critical: number;
68
+ warning: number;
69
+ suggestion: number;
70
+ total: number;
71
+ }
72
+ /**
73
+ * Governance Dashboard HTTP Server
74
+ *
75
+ * Collects scan reports from multiple projects and serves
76
+ * a web dashboard for trend visualization.
77
+ */
78
+ export declare class DashboardServer {
79
+ private dataDir;
80
+ private options;
81
+ private server;
82
+ private projectsFile;
83
+ constructor(options?: DashboardServerOptions);
84
+ /** Start the HTTP server */
85
+ start(port?: number): Promise<void>;
86
+ /** Stop the HTTP server */
87
+ stop(): Promise<void>;
88
+ /** Get the underlying http.Server instance (for testing) */
89
+ getServer(): Server | null;
90
+ /** Get the data directory path */
91
+ getDataDir(): string;
92
+ private handleRequest;
93
+ private handlePostReport;
94
+ private handleGetProjects;
95
+ private handleGetProject;
96
+ private handleGetReports;
97
+ private handleGetTrends;
98
+ private handleGetLatest;
99
+ private handleGetDashboard;
100
+ private ensureDir;
101
+ private loadProjects;
102
+ private saveProjects;
103
+ private countReports;
104
+ }
105
+ //# sourceMappingURL=dashboard-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-server.d.ts","sourceRoot":"","sources":["../../src/server/dashboard-server.ts"],"names":[],"mappings":"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"}