driftdetect-dashboard 0.8.1 → 0.8.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,55 @@
1
+ /**
2
+ * Pattern Watcher
3
+ *
4
+ * Watches the .drift/patterns/ directory for changes and emits events
5
+ * when patterns are added, updated, or removed.
6
+ *
7
+ * @requirements Phase 5 - Dashboard auto-refresh when watch mode updates patterns
8
+ */
9
+ import { EventEmitter } from 'node:events';
10
+ export interface PatternChangeEvent {
11
+ type: 'created' | 'updated' | 'deleted';
12
+ category: string;
13
+ status: string;
14
+ timestamp: string;
15
+ }
16
+ export interface PatternWatcherOptions {
17
+ /** Path to the .drift directory */
18
+ driftDir: string;
19
+ /** Debounce delay in milliseconds (default: 500) */
20
+ debounceMs?: number;
21
+ }
22
+ export declare class PatternWatcher extends EventEmitter {
23
+ private readonly driftDir;
24
+ private readonly patternsDir;
25
+ private readonly debounceMs;
26
+ private watchers;
27
+ private debounceTimers;
28
+ private isWatching;
29
+ constructor(options: PatternWatcherOptions);
30
+ /**
31
+ * Start watching for pattern changes
32
+ */
33
+ start(): void;
34
+ /**
35
+ * Stop watching for pattern changes
36
+ */
37
+ stop(): void;
38
+ /**
39
+ * Check if currently watching
40
+ */
41
+ get watching(): boolean;
42
+ /**
43
+ * Watch a directory for changes
44
+ */
45
+ private watchDirectory;
46
+ /**
47
+ * Debounce file change events
48
+ */
49
+ private debounceChange;
50
+ /**
51
+ * Handle a file change event
52
+ */
53
+ private handleFileChange;
54
+ }
55
+ //# sourceMappingURL=pattern-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-watcher.d.ts","sourceRoot":"","sources":["../../src/server/pattern-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAM3C,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,UAAU,CAAkB;gBAExB,OAAO,EAAE,qBAAqB;IAO1C;;OAEG;IACH,KAAK,IAAI,IAAI;IAsBb;;OAEG;IACH,IAAI,IAAI,IAAI;IAsBZ;;OAEG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAMD;;OAEG;IACH,OAAO,CAAC,cAAc;IAyCtB;;OAEG;IACH,OAAO,CAAC,cAAc;IActB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA+BzB"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Pattern Watcher
3
+ *
4
+ * Watches the .drift/patterns/ directory for changes and emits events
5
+ * when patterns are added, updated, or removed.
6
+ *
7
+ * @requirements Phase 5 - Dashboard auto-refresh when watch mode updates patterns
8
+ */
9
+ import * as fs from 'node:fs';
10
+ import * as path from 'node:path';
11
+ import { EventEmitter } from 'node:events';
12
+ // ============================================================================
13
+ // Pattern Watcher
14
+ // ============================================================================
15
+ export class PatternWatcher extends EventEmitter {
16
+ driftDir;
17
+ patternsDir;
18
+ debounceMs;
19
+ watchers = [];
20
+ debounceTimers = new Map();
21
+ isWatching = false;
22
+ constructor(options) {
23
+ super();
24
+ this.driftDir = options.driftDir;
25
+ this.patternsDir = path.join(options.driftDir, 'patterns');
26
+ this.debounceMs = options.debounceMs ?? 500;
27
+ }
28
+ /**
29
+ * Start watching for pattern changes
30
+ */
31
+ start() {
32
+ if (this.isWatching) {
33
+ return;
34
+ }
35
+ this.isWatching = true;
36
+ // Watch the patterns directory and subdirectories
37
+ const statusDirs = ['discovered', 'approved', 'ignored'];
38
+ for (const status of statusDirs) {
39
+ const statusDir = path.join(this.patternsDir, status);
40
+ this.watchDirectory(statusDir, status);
41
+ }
42
+ // Also watch the index directory for file-map changes
43
+ const indexDir = path.join(this.driftDir, 'index');
44
+ this.watchDirectory(indexDir, 'index');
45
+ this.emit('started');
46
+ }
47
+ /**
48
+ * Stop watching for pattern changes
49
+ */
50
+ stop() {
51
+ if (!this.isWatching) {
52
+ return;
53
+ }
54
+ this.isWatching = false;
55
+ // Close all watchers
56
+ for (const watcher of this.watchers) {
57
+ watcher.close();
58
+ }
59
+ this.watchers = [];
60
+ // Clear all debounce timers
61
+ for (const timer of this.debounceTimers.values()) {
62
+ clearTimeout(timer);
63
+ }
64
+ this.debounceTimers.clear();
65
+ this.emit('stopped');
66
+ }
67
+ /**
68
+ * Check if currently watching
69
+ */
70
+ get watching() {
71
+ return this.isWatching;
72
+ }
73
+ // ==========================================================================
74
+ // Private Methods
75
+ // ==========================================================================
76
+ /**
77
+ * Watch a directory for changes
78
+ */
79
+ watchDirectory(dirPath, status) {
80
+ // Ensure directory exists
81
+ if (!fs.existsSync(dirPath)) {
82
+ try {
83
+ fs.mkdirSync(dirPath, { recursive: true });
84
+ }
85
+ catch {
86
+ // Directory might be created later
87
+ return;
88
+ }
89
+ }
90
+ try {
91
+ const watcher = fs.watch(dirPath, (eventType, filename) => {
92
+ if (!filename || !filename.endsWith('.json')) {
93
+ return;
94
+ }
95
+ // Skip temp files
96
+ if (filename.endsWith('.tmp') || filename.startsWith('.')) {
97
+ return;
98
+ }
99
+ const filePath = path.join(dirPath, filename);
100
+ const category = filename.replace('.json', '');
101
+ // Debounce to avoid multiple events for the same file
102
+ this.debounceChange(filePath, () => {
103
+ this.handleFileChange(filePath, category, status, eventType);
104
+ });
105
+ });
106
+ this.watchers.push(watcher);
107
+ watcher.on('error', (error) => {
108
+ console.error(`Watcher error for ${dirPath}:`, error);
109
+ });
110
+ }
111
+ catch (error) {
112
+ console.error(`Failed to watch ${dirPath}:`, error);
113
+ }
114
+ }
115
+ /**
116
+ * Debounce file change events
117
+ */
118
+ debounceChange(key, callback) {
119
+ const existing = this.debounceTimers.get(key);
120
+ if (existing) {
121
+ clearTimeout(existing);
122
+ }
123
+ const timer = setTimeout(() => {
124
+ this.debounceTimers.delete(key);
125
+ callback();
126
+ }, this.debounceMs);
127
+ this.debounceTimers.set(key, timer);
128
+ }
129
+ /**
130
+ * Handle a file change event
131
+ */
132
+ handleFileChange(filePath, category, status, eventType) {
133
+ // Determine the change type
134
+ let changeType;
135
+ if (!fs.existsSync(filePath)) {
136
+ changeType = 'deleted';
137
+ }
138
+ else if (eventType === 'rename') {
139
+ // 'rename' can mean created or deleted
140
+ changeType = 'created';
141
+ }
142
+ else {
143
+ changeType = 'updated';
144
+ }
145
+ const event = {
146
+ type: changeType,
147
+ category,
148
+ status,
149
+ timestamp: new Date().toISOString(),
150
+ };
151
+ console.log(`[PatternWatcher] Detected ${changeType}: ${status}/${category}.json`);
152
+ this.emit('change', event);
153
+ // Also emit specific events
154
+ this.emit(changeType, event);
155
+ }
156
+ }
157
+ //# sourceMappingURL=pattern-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-watcher.js","sourceRoot":"","sources":["../../src/server/pattern-watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Quality Gates API Handler
3
+ *
4
+ * Server-side API for quality gates dashboard integration.
5
+ *
6
+ * @license Apache-2.0
7
+ */
8
+ import type { Request, Response } from 'express';
9
+ export declare function handleQualityGatesRequest(req: Request, res: Response, projectRoot: string): Promise<void>;
10
+ import { Router } from 'express';
11
+ export declare function createQualityGatesRouter(projectRoot: string): Router;
12
+ //# sourceMappingURL=quality-gates-api.d.ts.map
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Quality Gates API Handler
3
+ *
4
+ * Server-side API for quality gates dashboard integration.
5
+ *
6
+ * @license Apache-2.0
7
+ */
8
+ import { GateOrchestrator, PolicyLoader, GateRunStore, } from 'driftdetect-core';
9
+ // ============================================================================
10
+ // Handler
11
+ // ============================================================================
12
+ export async function handleQualityGatesRequest(req, res, projectRoot) {
13
+ try {
14
+ const action = req.query['action'] || req.body?.['action'] || 'latest';
15
+ switch (action) {
16
+ case 'latest':
17
+ await handleLatest(res, projectRoot);
18
+ break;
19
+ case 'history':
20
+ await handleHistory(req, res, projectRoot);
21
+ break;
22
+ case 'policies':
23
+ await handlePolicies(res, projectRoot);
24
+ break;
25
+ case 'run':
26
+ await handleRun(req, res, projectRoot);
27
+ break;
28
+ case 'policy':
29
+ await handleGetPolicy(req, res, projectRoot);
30
+ break;
31
+ default:
32
+ res.status(400).json({
33
+ success: false,
34
+ error: `Unknown action: ${action}`,
35
+ });
36
+ }
37
+ }
38
+ catch (error) {
39
+ const message = error instanceof Error ? error.message : 'Unknown error';
40
+ res.status(500).json({
41
+ success: false,
42
+ error: message,
43
+ });
44
+ }
45
+ }
46
+ // ============================================================================
47
+ // Action Handlers
48
+ // ============================================================================
49
+ /**
50
+ * Get the latest quality gate run result.
51
+ */
52
+ async function handleLatest(res, projectRoot) {
53
+ const store = new GateRunStore(projectRoot);
54
+ const recentRuns = await store.getRecent(1);
55
+ const latestRun = recentRuns[0] ?? null;
56
+ if (!latestRun) {
57
+ res.json({
58
+ success: true,
59
+ data: null,
60
+ });
61
+ return;
62
+ }
63
+ // Convert stored run to full result format
64
+ const result = await expandRunRecord(latestRun, projectRoot);
65
+ res.json({
66
+ success: true,
67
+ data: result,
68
+ });
69
+ }
70
+ /**
71
+ * Get quality gate run history.
72
+ */
73
+ async function handleHistory(req, res, projectRoot) {
74
+ const limit = parseInt(req.query['limit']) || 10;
75
+ const branch = req.query['branch'];
76
+ const store = new GateRunStore(projectRoot);
77
+ const runs = branch
78
+ ? await store.getByBranch(branch, limit)
79
+ : await store.getRecent(limit);
80
+ res.json({
81
+ success: true,
82
+ data: {
83
+ runs,
84
+ total: runs.length,
85
+ },
86
+ });
87
+ }
88
+ /**
89
+ * Get available policies.
90
+ */
91
+ async function handlePolicies(res, projectRoot) {
92
+ const loader = new PolicyLoader(projectRoot);
93
+ const policies = await loader.listAll();
94
+ const simplifiedPolicies = policies.map(p => ({
95
+ id: p.id,
96
+ name: p.name,
97
+ description: p.description,
98
+ version: p.version,
99
+ }));
100
+ res.json({
101
+ success: true,
102
+ data: {
103
+ policies: simplifiedPolicies,
104
+ },
105
+ });
106
+ }
107
+ /**
108
+ * Run quality gates.
109
+ */
110
+ async function handleRun(req, res, projectRoot) {
111
+ const { policy, files } = req.body;
112
+ const options = {
113
+ projectRoot,
114
+ policy: policy || 'default',
115
+ files: files || [],
116
+ branch: 'main', // Could be detected from git
117
+ ci: false,
118
+ saveHistory: true,
119
+ };
120
+ const orchestrator = new GateOrchestrator(projectRoot);
121
+ const result = await orchestrator.run(options);
122
+ res.json({
123
+ success: true,
124
+ data: result,
125
+ });
126
+ }
127
+ /**
128
+ * Get a specific policy by ID.
129
+ */
130
+ async function handleGetPolicy(req, res, projectRoot) {
131
+ const policyId = req.query['policyId'];
132
+ if (!policyId) {
133
+ res.status(400).json({
134
+ success: false,
135
+ error: 'policyId is required',
136
+ });
137
+ return;
138
+ }
139
+ const loader = new PolicyLoader(projectRoot);
140
+ try {
141
+ const policy = await loader.load(policyId);
142
+ res.json({
143
+ success: true,
144
+ data: policy,
145
+ });
146
+ }
147
+ catch (error) {
148
+ res.status(404).json({
149
+ success: false,
150
+ error: `Policy not found: ${policyId}`,
151
+ });
152
+ }
153
+ }
154
+ // ============================================================================
155
+ // Helpers
156
+ // ============================================================================
157
+ /**
158
+ * Expand a stored run record to a full result format.
159
+ * This reconstructs the full result from the stored summary.
160
+ */
161
+ async function expandRunRecord(run, projectRoot) {
162
+ const loader = new PolicyLoader(projectRoot);
163
+ let policyName = run.policyId;
164
+ try {
165
+ const policy = await loader.load(run.policyId);
166
+ policyName = policy.name;
167
+ }
168
+ catch {
169
+ // Use ID as name if policy not found
170
+ }
171
+ // Reconstruct gate results from summary
172
+ const gates = {};
173
+ for (const [gateId, gateSummary] of Object.entries(run.gates)) {
174
+ gates[gateId] = {
175
+ gateId,
176
+ gateName: gateId.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
177
+ status: gateSummary.passed ? 'passed' : 'failed',
178
+ passed: gateSummary.passed,
179
+ score: gateSummary.score,
180
+ summary: gateSummary.passed ? 'Passed' : 'Failed',
181
+ violations: [],
182
+ warnings: [],
183
+ executionTimeMs: 0,
184
+ details: {},
185
+ };
186
+ }
187
+ return {
188
+ passed: run.passed,
189
+ status: run.passed ? 'passed' : 'failed',
190
+ score: run.score,
191
+ summary: run.passed ? 'All gates passed' : 'Some gates failed',
192
+ gates: gates,
193
+ violations: [],
194
+ warnings: [],
195
+ policy: {
196
+ id: run.policyId,
197
+ name: policyName,
198
+ },
199
+ metadata: {
200
+ executionTimeMs: run.executionTimeMs,
201
+ filesChecked: 0,
202
+ gatesRun: Object.keys(run.gates),
203
+ gatesSkipped: [],
204
+ timestamp: run.timestamp,
205
+ branch: run.branch,
206
+ ...(run.commitSha ? { commitSha: run.commitSha } : {}),
207
+ ci: run.ci,
208
+ },
209
+ exitCode: run.passed ? 0 : 1,
210
+ };
211
+ }
212
+ // ============================================================================
213
+ // Express Router Setup
214
+ // ============================================================================
215
+ import { Router } from 'express';
216
+ export function createQualityGatesRouter(projectRoot) {
217
+ const router = Router();
218
+ router.get('/quality-gates', (req, res) => {
219
+ handleQualityGatesRequest(req, res, projectRoot);
220
+ });
221
+ router.post('/quality-gates', (req, res) => {
222
+ handleQualityGatesRequest(req, res, projectRoot);
223
+ });
224
+ return router;
225
+ }
226
+ //# sourceMappingURL=quality-gates-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality-gates-api.js","sourceRoot":"","sources":["../../src/server/quality-gates-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * WebSocket Server
3
+ *
4
+ * Manages WebSocket connections for realtime violation streaming.
5
+ *
6
+ * @requirements 2.1 - Expose a WebSocket endpoint at `/ws` for realtime communication
7
+ * @requirements 2.3 - Broadcast violations to all connected WebSocket clients
8
+ */
9
+ import type { Server } from 'http';
10
+ import type { DashboardViolation } from './drift-data-reader.js';
11
+ export type WebSocketMessageType = 'violation' | 'pattern_updated' | 'patterns_changed' | 'stats_updated' | 'ping' | 'pong' | 'connected';
12
+ export interface WebSocketMessage {
13
+ type: WebSocketMessageType;
14
+ payload?: unknown;
15
+ timestamp: string;
16
+ }
17
+ export interface PatternUpdatePayload {
18
+ id: string;
19
+ status: string;
20
+ action: 'approved' | 'ignored' | 'deleted';
21
+ }
22
+ export interface PatternsChangedPayload {
23
+ type: 'created' | 'updated' | 'deleted';
24
+ category: string;
25
+ status: string;
26
+ }
27
+ export declare class WebSocketManager {
28
+ private wss;
29
+ private clients;
30
+ private pingInterval;
31
+ /**
32
+ * Get the number of connected clients
33
+ */
34
+ get clientCount(): number;
35
+ /**
36
+ * Attach WebSocket server to an HTTP server
37
+ * @requirements 2.1 - WebSocket endpoint at /ws
38
+ */
39
+ attach(server: Server): void;
40
+ /**
41
+ * Close the WebSocket server and all connections
42
+ */
43
+ close(): void;
44
+ /**
45
+ * Broadcast a violation to all connected clients
46
+ * @requirements 2.3 - Broadcast violations to all connected clients
47
+ */
48
+ broadcastViolation(violation: DashboardViolation): void;
49
+ /**
50
+ * Broadcast a pattern update to all connected clients
51
+ */
52
+ broadcastPatternUpdate(update: PatternUpdatePayload): void;
53
+ /**
54
+ * Broadcast stats update to all connected clients
55
+ */
56
+ broadcastStatsUpdate(stats: unknown): void;
57
+ /**
58
+ * Broadcast patterns changed event to all connected clients
59
+ * Triggers client-side data refresh
60
+ */
61
+ broadcastPatternsChanged(payload: PatternsChangedPayload): void;
62
+ /**
63
+ * Handle a new WebSocket connection
64
+ */
65
+ private handleConnection;
66
+ /**
67
+ * Handle incoming message from a client
68
+ */
69
+ private handleMessage;
70
+ /**
71
+ * Send a message to a specific client
72
+ */
73
+ private send;
74
+ /**
75
+ * Broadcast a message to all connected clients
76
+ */
77
+ private broadcast;
78
+ /**
79
+ * Start ping interval for connection health monitoring
80
+ */
81
+ private startPingInterval;
82
+ }
83
+ //# sourceMappingURL=websocket-server.d.ts.map