driftdetect-vscode 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.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +138 -0
- package/.vscode/launch.json +17 -0
- package/.vscode/tasks.json +15 -0
- package/LICENSE +121 -0
- package/dist/activation/activation-controller.d.ts +61 -0
- package/dist/activation/activation-controller.d.ts.map +1 -0
- package/dist/activation/activation-controller.js +235 -0
- package/dist/activation/activation-controller.js.map +1 -0
- package/dist/activation/activation-phases.d.ts +28 -0
- package/dist/activation/activation-phases.d.ts.map +1 -0
- package/dist/activation/activation-phases.js +80 -0
- package/dist/activation/activation-phases.js.map +1 -0
- package/dist/activation/index.d.ts +6 -0
- package/dist/activation/index.d.ts.map +1 -0
- package/dist/activation/index.js +6 -0
- package/dist/activation/index.js.map +1 -0
- package/dist/client/connection-config.d.ts +50 -0
- package/dist/client/connection-config.d.ts.map +1 -0
- package/dist/client/connection-config.js +56 -0
- package/dist/client/connection-config.js.map +1 -0
- package/dist/client/connection-manager.d.ts +70 -0
- package/dist/client/connection-manager.d.ts.map +1 -0
- package/dist/client/connection-manager.js +214 -0
- package/dist/client/connection-manager.js.map +1 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/language-client-factory.d.ts +29 -0
- package/dist/client/language-client-factory.d.ts.map +1 -0
- package/dist/client/language-client-factory.js +76 -0
- package/dist/client/language-client-factory.js.map +1 -0
- package/dist/client/request-middleware.d.ts +38 -0
- package/dist/client/request-middleware.d.ts.map +1 -0
- package/dist/client/request-middleware.js +85 -0
- package/dist/client/request-middleware.js.map +1 -0
- package/dist/commands/command-definitions.d.ts +53 -0
- package/dist/commands/command-definitions.d.ts.map +1 -0
- package/dist/commands/command-definitions.js +212 -0
- package/dist/commands/command-definitions.js.map +1 -0
- package/dist/commands/command-router.d.ts +80 -0
- package/dist/commands/command-router.d.ts.map +1 -0
- package/dist/commands/command-router.js +127 -0
- package/dist/commands/command-router.js.map +1 -0
- package/dist/commands/handlers/connection-handlers.d.ts +14 -0
- package/dist/commands/handlers/connection-handlers.d.ts.map +1 -0
- package/dist/commands/handlers/connection-handlers.js +57 -0
- package/dist/commands/handlers/connection-handlers.js.map +1 -0
- package/dist/commands/handlers/constants-handlers.d.ts +11 -0
- package/dist/commands/handlers/constants-handlers.d.ts.map +1 -0
- package/dist/commands/handlers/constants-handlers.js +84 -0
- package/dist/commands/handlers/constants-handlers.js.map +1 -0
- package/dist/commands/handlers/index.d.ts +10 -0
- package/dist/commands/handlers/index.d.ts.map +1 -0
- package/dist/commands/handlers/index.js +10 -0
- package/dist/commands/handlers/index.js.map +1 -0
- package/dist/commands/handlers/pattern-handlers.d.ts +13 -0
- package/dist/commands/handlers/pattern-handlers.d.ts.map +1 -0
- package/dist/commands/handlers/pattern-handlers.js +127 -0
- package/dist/commands/handlers/pattern-handlers.js.map +1 -0
- package/dist/commands/handlers/scan-handlers.d.ts +15 -0
- package/dist/commands/handlers/scan-handlers.d.ts.map +1 -0
- package/dist/commands/handlers/scan-handlers.js +74 -0
- package/dist/commands/handlers/scan-handlers.js.map +1 -0
- package/dist/commands/handlers/ui-handlers.d.ts +12 -0
- package/dist/commands/handlers/ui-handlers.d.ts.map +1 -0
- package/dist/commands/handlers/ui-handlers.js +74 -0
- package/dist/commands/handlers/ui-handlers.js.map +1 -0
- package/dist/commands/handlers/violation-handlers.d.ts +13 -0
- package/dist/commands/handlers/violation-handlers.d.ts.map +1 -0
- package/dist/commands/handlers/violation-handlers.js +76 -0
- package/dist/commands/handlers/violation-handlers.js.map +1 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/middleware/connection-check-middleware.d.ts +12 -0
- package/dist/commands/middleware/connection-check-middleware.d.ts.map +1 -0
- package/dist/commands/middleware/connection-check-middleware.js +34 -0
- package/dist/commands/middleware/connection-check-middleware.js.map +1 -0
- package/dist/commands/middleware/index.d.ts +7 -0
- package/dist/commands/middleware/index.d.ts.map +1 -0
- package/dist/commands/middleware/index.js +7 -0
- package/dist/commands/middleware/index.js.map +1 -0
- package/dist/commands/middleware/logging-middleware.d.ts +12 -0
- package/dist/commands/middleware/logging-middleware.d.ts.map +1 -0
- package/dist/commands/middleware/logging-middleware.js +24 -0
- package/dist/commands/middleware/logging-middleware.js.map +1 -0
- package/dist/commands/middleware/telemetry-middleware.d.ts +22 -0
- package/dist/commands/middleware/telemetry-middleware.d.ts.map +1 -0
- package/dist/commands/middleware/telemetry-middleware.js +30 -0
- package/dist/commands/middleware/telemetry-middleware.js.map +1 -0
- package/dist/config/config-manager.d.ts +53 -0
- package/dist/config/config-manager.d.ts.map +1 -0
- package/dist/config/config-manager.js +178 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/defaults.d.ts +11 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +43 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +7 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/validator.d.ts +22 -0
- package/dist/config/validator.d.ts.map +1 -0
- package/dist/config/validator.js +93 -0
- package/dist/config/validator.js.map +1 -0
- package/dist/extension.d.ts +32 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +50 -0
- package/dist/extension.js.map +1 -0
- package/dist/infrastructure/disposable-manager.d.ts +43 -0
- package/dist/infrastructure/disposable-manager.d.ts.map +1 -0
- package/dist/infrastructure/disposable-manager.js +75 -0
- package/dist/infrastructure/disposable-manager.js.map +1 -0
- package/dist/infrastructure/event-bus.d.ts +85 -0
- package/dist/infrastructure/event-bus.d.ts.map +1 -0
- package/dist/infrastructure/event-bus.js +74 -0
- package/dist/infrastructure/event-bus.js.map +1 -0
- package/dist/infrastructure/index.d.ts +10 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +10 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/infrastructure/logger.d.ts +37 -0
- package/dist/infrastructure/logger.d.ts.map +1 -0
- package/dist/infrastructure/logger.js +86 -0
- package/dist/infrastructure/logger.js.map +1 -0
- package/dist/infrastructure/service-container.d.ts +68 -0
- package/dist/infrastructure/service-container.d.ts.map +1 -0
- package/dist/infrastructure/service-container.js +94 -0
- package/dist/infrastructure/service-container.js.map +1 -0
- package/dist/state/index.d.ts +7 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +7 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/initial-state.d.ts +11 -0
- package/dist/state/initial-state.d.ts.map +1 -0
- package/dist/state/initial-state.js +58 -0
- package/dist/state/initial-state.js.map +1 -0
- package/dist/state/selectors.d.ts +41 -0
- package/dist/state/selectors.d.ts.map +1 -0
- package/dist/state/selectors.js +61 -0
- package/dist/state/selectors.js.map +1 -0
- package/dist/state/state-manager.d.ts +54 -0
- package/dist/state/state-manager.d.ts.map +1 -0
- package/dist/state/state-manager.js +166 -0
- package/dist/state/state-manager.js.map +1 -0
- package/dist/types/config-types.d.ts +69 -0
- package/dist/types/config-types.d.ts.map +1 -0
- package/dist/types/config-types.js +5 -0
- package/dist/types/config-types.js.map +1 -0
- package/dist/types/extension-types.d.ts +45 -0
- package/dist/types/extension-types.d.ts.map +1 -0
- package/dist/types/extension-types.js +5 -0
- package/dist/types/extension-types.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lsp-types.d.ts +70 -0
- package/dist/types/lsp-types.d.ts.map +1 -0
- package/dist/types/lsp-types.js +5 -0
- package/dist/types/lsp-types.js.map +1 -0
- package/dist/types/state-types.d.ts +82 -0
- package/dist/types/state-types.d.ts.map +1 -0
- package/dist/types/state-types.js +5 -0
- package/dist/types/state-types.js.map +1 -0
- package/dist/types/vscode-types.d.ts +36 -0
- package/dist/types/vscode-types.d.ts.map +1 -0
- package/dist/types/vscode-types.js +7 -0
- package/dist/types/vscode-types.js.map +1 -0
- package/dist/ui/decorations/decoration-controller.d.ts +45 -0
- package/dist/ui/decorations/decoration-controller.d.ts.map +1 -0
- package/dist/ui/decorations/decoration-controller.js +198 -0
- package/dist/ui/decorations/decoration-controller.js.map +1 -0
- package/dist/ui/decorations/decoration-types.d.ts +28 -0
- package/dist/ui/decorations/decoration-types.d.ts.map +1 -0
- package/dist/ui/decorations/decoration-types.js +98 -0
- package/dist/ui/decorations/decoration-types.js.map +1 -0
- package/dist/ui/decorations/index.d.ts +7 -0
- package/dist/ui/decorations/index.d.ts.map +1 -0
- package/dist/ui/decorations/index.js +6 -0
- package/dist/ui/decorations/index.js.map +1 -0
- package/dist/ui/index.d.ts +7 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +7 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/notifications/index.d.ts +5 -0
- package/dist/ui/notifications/index.d.ts.map +1 -0
- package/dist/ui/notifications/index.js +5 -0
- package/dist/ui/notifications/index.js.map +1 -0
- package/dist/ui/notifications/notification-service.d.ts +66 -0
- package/dist/ui/notifications/notification-service.d.ts.map +1 -0
- package/dist/ui/notifications/notification-service.js +103 -0
- package/dist/ui/notifications/notification-service.js.map +1 -0
- package/dist/ui/status-bar/index.d.ts +6 -0
- package/dist/ui/status-bar/index.d.ts.map +1 -0
- package/dist/ui/status-bar/index.js +6 -0
- package/dist/ui/status-bar/index.js.map +1 -0
- package/dist/ui/status-bar/status-bar-controller.d.ts +37 -0
- package/dist/ui/status-bar/status-bar-controller.d.ts.map +1 -0
- package/dist/ui/status-bar/status-bar-controller.js +111 -0
- package/dist/ui/status-bar/status-bar-controller.js.map +1 -0
- package/dist/ui/status-bar/status-bar-modes.d.ts +26 -0
- package/dist/ui/status-bar/status-bar-modes.d.ts.map +1 -0
- package/dist/ui/status-bar/status-bar-modes.js +97 -0
- package/dist/ui/status-bar/status-bar-modes.js.map +1 -0
- package/dist/views/base-tree-provider.d.ts +74 -0
- package/dist/views/base-tree-provider.d.ts.map +1 -0
- package/dist/views/base-tree-provider.js +95 -0
- package/dist/views/base-tree-provider.js.map +1 -0
- package/dist/views/constants-tree-provider.d.ts +112 -0
- package/dist/views/constants-tree-provider.d.ts.map +1 -0
- package/dist/views/constants-tree-provider.js +344 -0
- package/dist/views/constants-tree-provider.js.map +1 -0
- package/dist/views/files-tree-provider.d.ts +37 -0
- package/dist/views/files-tree-provider.d.ts.map +1 -0
- package/dist/views/files-tree-provider.js +98 -0
- package/dist/views/files-tree-provider.js.map +1 -0
- package/dist/views/index.d.ts +10 -0
- package/dist/views/index.d.ts.map +1 -0
- package/dist/views/index.js +10 -0
- package/dist/views/index.js.map +1 -0
- package/dist/views/patterns-tree-provider.d.ts +39 -0
- package/dist/views/patterns-tree-provider.d.ts.map +1 -0
- package/dist/views/patterns-tree-provider.js +139 -0
- package/dist/views/patterns-tree-provider.js.map +1 -0
- package/dist/views/violations-tree-provider.d.ts +46 -0
- package/dist/views/violations-tree-provider.d.ts.map +1 -0
- package/dist/views/violations-tree-provider.js +158 -0
- package/dist/views/violations-tree-provider.js.map +1 -0
- package/dist/webview/index.d.ts +7 -0
- package/dist/webview/index.d.ts.map +1 -0
- package/dist/webview/index.js +7 -0
- package/dist/webview/index.js.map +1 -0
- package/dist/webview/webview-manager.d.ts +57 -0
- package/dist/webview/webview-manager.d.ts.map +1 -0
- package/dist/webview/webview-manager.js +167 -0
- package/dist/webview/webview-manager.js.map +1 -0
- package/package.json +405 -0
- package/resources/drift-icon.png +0 -0
- package/resources/drift-icon.svg +5 -0
- package/resources/icons/error.svg +4 -0
- package/resources/icons/info.svg +4 -0
- package/resources/icons/lightbulb.svg +4 -0
- package/resources/icons/warning.svg +4 -0
- package/src/activation/activation-controller.ts +320 -0
- package/src/activation/activation-phases.ts +96 -0
- package/src/activation/index.ts +6 -0
- package/src/client/connection-config.ts +64 -0
- package/src/client/connection-manager.ts +263 -0
- package/src/client/index.ts +8 -0
- package/src/client/language-client-factory.ts +111 -0
- package/src/client/request-middleware.ts +117 -0
- package/src/commands/command-definitions.ts +243 -0
- package/src/commands/command-router.ts +194 -0
- package/src/commands/handlers/connection-handlers.ts +74 -0
- package/src/commands/handlers/constants-handlers.ts +99 -0
- package/src/commands/handlers/index.ts +10 -0
- package/src/commands/handlers/pattern-handlers.ts +167 -0
- package/src/commands/handlers/scan-handlers.ts +107 -0
- package/src/commands/handlers/ui-handlers.ts +88 -0
- package/src/commands/handlers/violation-handlers.ts +97 -0
- package/src/commands/index.ts +7 -0
- package/src/commands/middleware/connection-check-middleware.ts +46 -0
- package/src/commands/middleware/index.ts +7 -0
- package/src/commands/middleware/logging-middleware.ts +28 -0
- package/src/commands/middleware/telemetry-middleware.ts +46 -0
- package/src/config/config-manager.ts +213 -0
- package/src/config/defaults.ts +45 -0
- package/src/config/index.ts +7 -0
- package/src/config/validator.ts +118 -0
- package/src/extension.ts +57 -0
- package/src/infrastructure/disposable-manager.ts +87 -0
- package/src/infrastructure/event-bus.ts +121 -0
- package/src/infrastructure/index.ts +10 -0
- package/src/infrastructure/logger.ts +108 -0
- package/src/infrastructure/service-container.ts +123 -0
- package/src/state/index.ts +7 -0
- package/src/state/initial-state.ts +60 -0
- package/src/state/selectors.ts +126 -0
- package/src/state/state-manager.ts +198 -0
- package/src/types/config-types.ts +77 -0
- package/src/types/extension-types.ts +58 -0
- package/src/types/index.ts +12 -0
- package/src/types/lsp-types.ts +77 -0
- package/src/types/state-types.ts +92 -0
- package/src/types/vscode-types.ts +40 -0
- package/src/ui/decorations/decoration-controller.ts +252 -0
- package/src/ui/decorations/decoration-types.ts +129 -0
- package/src/ui/decorations/index.ts +7 -0
- package/src/ui/index.ts +7 -0
- package/src/ui/notifications/index.ts +5 -0
- package/src/ui/notifications/notification-service.ts +167 -0
- package/src/ui/status-bar/index.ts +6 -0
- package/src/ui/status-bar/status-bar-controller.ts +135 -0
- package/src/ui/status-bar/status-bar-modes.ts +119 -0
- package/src/views/base-tree-provider.ts +127 -0
- package/src/views/constants-tree-provider.ts +525 -0
- package/src/views/files-tree-provider.ts +140 -0
- package/src/views/index.ts +10 -0
- package/src/views/patterns-tree-provider.ts +179 -0
- package/src/views/violations-tree-provider.ts +210 -0
- package/src/webview/index.ts +7 -0
- package/src/webview/webview-manager.ts +238 -0
- package/tsconfig.json +22 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger - Centralized logging with output channel integration
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: Provide structured logging to VS Code output channel.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as vscode from 'vscode';
|
|
8
|
+
|
|
9
|
+
import type { Logger as ILogger } from '../types/index.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Log levels for filtering
|
|
13
|
+
*/
|
|
14
|
+
type LogLevel = 'error' | 'warn' | 'info' | 'debug';
|
|
15
|
+
|
|
16
|
+
const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
|
|
17
|
+
error: 0,
|
|
18
|
+
warn: 1,
|
|
19
|
+
info: 2,
|
|
20
|
+
debug: 3,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Logger implementation with VS Code output channel
|
|
25
|
+
*/
|
|
26
|
+
export class Logger implements ILogger {
|
|
27
|
+
private readonly channel: vscode.OutputChannel;
|
|
28
|
+
private readonly minLevel: LogLevel;
|
|
29
|
+
|
|
30
|
+
constructor(channelName: string, minLevel: LogLevel = 'info') {
|
|
31
|
+
this.channel = vscode.window.createOutputChannel(channelName);
|
|
32
|
+
this.minLevel = minLevel;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
error(message: string, ...args: unknown[]): void {
|
|
36
|
+
this.log('error', message, args);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
warn(message: string, ...args: unknown[]): void {
|
|
40
|
+
this.log('warn', message, args);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
info(message: string, ...args: unknown[]): void {
|
|
44
|
+
this.log('info', message, args);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
debug(message: string, ...args: unknown[]): void {
|
|
48
|
+
this.log('debug', message, args);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Show the output channel
|
|
53
|
+
*/
|
|
54
|
+
show(): void {
|
|
55
|
+
this.channel.show();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Dispose the output channel
|
|
60
|
+
*/
|
|
61
|
+
dispose(): void {
|
|
62
|
+
this.channel.dispose();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private log(level: LogLevel, message: string, args: unknown[]): void {
|
|
66
|
+
if (LOG_LEVEL_PRIORITY[level] > LOG_LEVEL_PRIORITY[this.minLevel]) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const timestamp = new Date().toISOString();
|
|
71
|
+
const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
|
|
72
|
+
|
|
73
|
+
let formattedMessage = `${prefix} ${message}`;
|
|
74
|
+
|
|
75
|
+
if (args.length > 0) {
|
|
76
|
+
const argsStr = args
|
|
77
|
+
.map(arg => {
|
|
78
|
+
if (arg instanceof Error) {
|
|
79
|
+
return `${arg.message}\n${arg.stack}`;
|
|
80
|
+
}
|
|
81
|
+
if (typeof arg === 'object') {
|
|
82
|
+
try {
|
|
83
|
+
return JSON.stringify(arg, null, 2);
|
|
84
|
+
} catch {
|
|
85
|
+
return String(arg);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return String(arg);
|
|
89
|
+
})
|
|
90
|
+
.join(' ');
|
|
91
|
+
formattedMessage += ` ${argsStr}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.channel.appendLine(formattedMessage);
|
|
95
|
+
|
|
96
|
+
// Also log errors to console for debugging
|
|
97
|
+
if (level === 'error') {
|
|
98
|
+
console.error(`[Drift] ${message}`, ...args);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Factory function for creating loggers
|
|
105
|
+
*/
|
|
106
|
+
export function createLogger(channelName: string = 'Drift', minLevel: LogLevel = 'info'): Logger {
|
|
107
|
+
return new Logger(channelName, minLevel);
|
|
108
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ServiceContainer - Dependency injection container
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: Manage service instances and dependencies.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ServiceContainer as IServiceContainer } from '../types/index.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Service registration options
|
|
11
|
+
*/
|
|
12
|
+
interface ServiceOptions {
|
|
13
|
+
singleton?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Service factory function type
|
|
18
|
+
*/
|
|
19
|
+
type ServiceFactory<T> = (container: ServiceContainer) => T;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Simple dependency injection container
|
|
23
|
+
*/
|
|
24
|
+
export class ServiceContainer implements IServiceContainer {
|
|
25
|
+
private readonly instances = new Map<string, unknown>();
|
|
26
|
+
private readonly factories = new Map<string, ServiceFactory<unknown>>();
|
|
27
|
+
private readonly options = new Map<string, ServiceOptions>();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Register a service instance directly
|
|
31
|
+
*/
|
|
32
|
+
register<T>(key: string, instance: T): void {
|
|
33
|
+
this.instances.set(key, instance);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Register a service factory for lazy instantiation
|
|
38
|
+
*/
|
|
39
|
+
registerFactory<T>(
|
|
40
|
+
key: string,
|
|
41
|
+
factory: ServiceFactory<T>,
|
|
42
|
+
options: ServiceOptions = { singleton: true }
|
|
43
|
+
): void {
|
|
44
|
+
this.factories.set(key, factory as ServiceFactory<unknown>);
|
|
45
|
+
this.options.set(key, options);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get a service instance
|
|
50
|
+
*/
|
|
51
|
+
get<T>(key: string): T {
|
|
52
|
+
// Check for existing instance
|
|
53
|
+
if (this.instances.has(key)) {
|
|
54
|
+
return this.instances.get(key) as T;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for factory
|
|
58
|
+
const factory = this.factories.get(key);
|
|
59
|
+
if (!factory) {
|
|
60
|
+
throw new Error(`Service not registered: ${key}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Create instance
|
|
64
|
+
const instance = factory(this) as T;
|
|
65
|
+
|
|
66
|
+
// Cache if singleton
|
|
67
|
+
const opts = this.options.get(key);
|
|
68
|
+
if (opts?.singleton !== false) {
|
|
69
|
+
this.instances.set(key, instance);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return instance;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a service is registered
|
|
77
|
+
*/
|
|
78
|
+
has(key: string): boolean {
|
|
79
|
+
return this.instances.has(key) || this.factories.has(key);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Try to get a service, returning undefined if not found
|
|
84
|
+
*/
|
|
85
|
+
tryGet<T>(key: string): T | undefined {
|
|
86
|
+
try {
|
|
87
|
+
return this.get<T>(key);
|
|
88
|
+
} catch {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Clear all registered services
|
|
95
|
+
*/
|
|
96
|
+
clear(): void {
|
|
97
|
+
this.instances.clear();
|
|
98
|
+
this.factories.clear();
|
|
99
|
+
this.options.clear();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Service keys for type-safe access
|
|
105
|
+
*/
|
|
106
|
+
export const ServiceKeys = {
|
|
107
|
+
Logger: 'logger',
|
|
108
|
+
StateManager: 'stateManager',
|
|
109
|
+
ConfigManager: 'configManager',
|
|
110
|
+
ConnectionManager: 'connectionManager',
|
|
111
|
+
LanguageClient: 'languageClient',
|
|
112
|
+
CommandRouter: 'commandRouter',
|
|
113
|
+
TelemetryService: 'telemetryService',
|
|
114
|
+
StatusBar: 'statusBar',
|
|
115
|
+
DecorationController: 'decorationController',
|
|
116
|
+
} as const;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Factory function for creating service containers
|
|
120
|
+
*/
|
|
121
|
+
export function createServiceContainer(): ServiceContainer {
|
|
122
|
+
return new ServiceContainer();
|
|
123
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initial state factory
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: Provide default state values.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ExtensionState } from '../types/index.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create the initial extension state
|
|
11
|
+
*/
|
|
12
|
+
export function createInitialState(): ExtensionState {
|
|
13
|
+
return {
|
|
14
|
+
connection: {
|
|
15
|
+
status: 'disconnected',
|
|
16
|
+
serverVersion: null,
|
|
17
|
+
lastError: null,
|
|
18
|
+
restartCount: 0,
|
|
19
|
+
},
|
|
20
|
+
workspace: {
|
|
21
|
+
initialized: false,
|
|
22
|
+
projectRoot: null,
|
|
23
|
+
configPath: null,
|
|
24
|
+
lastScanTime: null,
|
|
25
|
+
scanning: false,
|
|
26
|
+
},
|
|
27
|
+
patterns: {
|
|
28
|
+
total: 0,
|
|
29
|
+
byCategory: {},
|
|
30
|
+
byStatus: {
|
|
31
|
+
discovered: 0,
|
|
32
|
+
approved: 0,
|
|
33
|
+
ignored: 0,
|
|
34
|
+
},
|
|
35
|
+
lastUpdated: null,
|
|
36
|
+
},
|
|
37
|
+
violations: {
|
|
38
|
+
total: 0,
|
|
39
|
+
bySeverity: {
|
|
40
|
+
error: 0,
|
|
41
|
+
warning: 0,
|
|
42
|
+
info: 0,
|
|
43
|
+
hint: 0,
|
|
44
|
+
},
|
|
45
|
+
activeFile: null,
|
|
46
|
+
activeFileCount: 0,
|
|
47
|
+
},
|
|
48
|
+
ui: {
|
|
49
|
+
statusBarVisible: true,
|
|
50
|
+
sidebarExpanded: false,
|
|
51
|
+
activePanel: null,
|
|
52
|
+
},
|
|
53
|
+
preferences: {
|
|
54
|
+
autoScan: true,
|
|
55
|
+
showInlineHints: true,
|
|
56
|
+
severityFilter: ['error', 'warning'],
|
|
57
|
+
categoryFilter: [],
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State selectors - Reusable state accessors
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: Provide typed access to state slices.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { StateSelector } from '../types/index.js';
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Connection Selectors
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export const selectConnectionStatus: StateSelector<string> =
|
|
14
|
+
(state) => state.connection.status;
|
|
15
|
+
|
|
16
|
+
export const selectIsConnected: StateSelector<boolean> =
|
|
17
|
+
(state) => state.connection.status === 'connected';
|
|
18
|
+
|
|
19
|
+
export const selectConnectionError: StateSelector<string | null> =
|
|
20
|
+
(state) => state.connection.lastError;
|
|
21
|
+
|
|
22
|
+
export const selectServerVersion: StateSelector<string | null> =
|
|
23
|
+
(state) => state.connection.serverVersion;
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Workspace Selectors
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
export const selectIsInitialized: StateSelector<boolean> =
|
|
30
|
+
(state) => state.workspace.initialized;
|
|
31
|
+
|
|
32
|
+
export const selectProjectRoot: StateSelector<string | null> =
|
|
33
|
+
(state) => state.workspace.projectRoot;
|
|
34
|
+
|
|
35
|
+
export const selectIsScanning: StateSelector<boolean> =
|
|
36
|
+
(state) => state.workspace.scanning;
|
|
37
|
+
|
|
38
|
+
export const selectLastScanTime: StateSelector<number | null> =
|
|
39
|
+
(state) => state.workspace.lastScanTime;
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Pattern Selectors
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
export const selectPatternTotal: StateSelector<number> =
|
|
46
|
+
(state) => state.patterns.total;
|
|
47
|
+
|
|
48
|
+
export const selectPatternsByCategory: StateSelector<Record<string, number>> =
|
|
49
|
+
(state) => state.patterns.byCategory;
|
|
50
|
+
|
|
51
|
+
export const selectPatternsByStatus: StateSelector<Record<string, number>> =
|
|
52
|
+
(state) => state.patterns.byStatus;
|
|
53
|
+
|
|
54
|
+
export const selectPatternsLastUpdated: StateSelector<number | null> =
|
|
55
|
+
(state) => state.patterns.lastUpdated;
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Violation Selectors
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
export const selectViolationTotal: StateSelector<number> =
|
|
62
|
+
(state) => state.violations.total;
|
|
63
|
+
|
|
64
|
+
export const selectViolationsBySeverity: StateSelector<Record<string, number>> =
|
|
65
|
+
(state) => state.violations.bySeverity;
|
|
66
|
+
|
|
67
|
+
export const selectActiveFileViolations: StateSelector<number> =
|
|
68
|
+
(state) => state.violations.activeFileCount;
|
|
69
|
+
|
|
70
|
+
export const selectActiveFile: StateSelector<string | null> =
|
|
71
|
+
(state) => state.violations.activeFile;
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// UI Selectors
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
export const selectStatusBarVisible: StateSelector<boolean> =
|
|
78
|
+
(state) => state.ui.statusBarVisible;
|
|
79
|
+
|
|
80
|
+
export const selectSidebarExpanded: StateSelector<boolean> =
|
|
81
|
+
(state) => state.ui.sidebarExpanded;
|
|
82
|
+
|
|
83
|
+
export const selectActivePanel: StateSelector<string | null> =
|
|
84
|
+
(state) => state.ui.activePanel;
|
|
85
|
+
|
|
86
|
+
// ============================================================================
|
|
87
|
+
// Preferences Selectors
|
|
88
|
+
// ============================================================================
|
|
89
|
+
|
|
90
|
+
export const selectAutoScan: StateSelector<boolean> =
|
|
91
|
+
(state) => state.preferences.autoScan;
|
|
92
|
+
|
|
93
|
+
export const selectShowInlineHints: StateSelector<boolean> =
|
|
94
|
+
(state) => state.preferences.showInlineHints;
|
|
95
|
+
|
|
96
|
+
export const selectSeverityFilter: StateSelector<string[]> =
|
|
97
|
+
(state) => state.preferences.severityFilter;
|
|
98
|
+
|
|
99
|
+
export const selectCategoryFilter: StateSelector<string[]> =
|
|
100
|
+
(state) => state.preferences.categoryFilter;
|
|
101
|
+
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// Composite Selectors
|
|
104
|
+
// ============================================================================
|
|
105
|
+
|
|
106
|
+
export const selectStatusBarData: StateSelector<{
|
|
107
|
+
status: string;
|
|
108
|
+
violations: number;
|
|
109
|
+
scanning: boolean;
|
|
110
|
+
}> = (state) => ({
|
|
111
|
+
status: state.connection.status,
|
|
112
|
+
violations: state.violations.total,
|
|
113
|
+
scanning: state.workspace.scanning,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export const selectHealthSummary: StateSelector<{
|
|
117
|
+
connected: boolean;
|
|
118
|
+
patterns: number;
|
|
119
|
+
violations: number;
|
|
120
|
+
lastScan: number | null;
|
|
121
|
+
}> = (state) => ({
|
|
122
|
+
connected: state.connection.status === 'connected',
|
|
123
|
+
patterns: state.patterns.total,
|
|
124
|
+
violations: state.violations.total,
|
|
125
|
+
lastScan: state.workspace.lastScanTime,
|
|
126
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StateManager - Centralized reactive state management
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: Manage extension state with subscriptions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as vscode from 'vscode';
|
|
8
|
+
|
|
9
|
+
import { createInitialState } from './initial-state.js';
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
ExtensionState,
|
|
13
|
+
StateUpdater,
|
|
14
|
+
StateSelector,
|
|
15
|
+
StateSubscriber,
|
|
16
|
+
} from '../types/index.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Persistence key for state
|
|
20
|
+
*/
|
|
21
|
+
const PERSISTENCE_KEY = 'drift.state';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* State manager with selector-based subscriptions
|
|
25
|
+
*/
|
|
26
|
+
export class StateManager implements vscode.Disposable {
|
|
27
|
+
private state: ExtensionState;
|
|
28
|
+
private readonly listeners = new Set<(state: ExtensionState) => void>();
|
|
29
|
+
private readonly context: vscode.ExtensionContext;
|
|
30
|
+
|
|
31
|
+
constructor(context: vscode.ExtensionContext) {
|
|
32
|
+
this.context = context;
|
|
33
|
+
this.state = this.loadState();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get current state (readonly)
|
|
38
|
+
*/
|
|
39
|
+
getState(): Readonly<ExtensionState> {
|
|
40
|
+
return this.state;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Update state with an updater function
|
|
45
|
+
*/
|
|
46
|
+
update(updater: StateUpdater): void {
|
|
47
|
+
// Create a shallow copy for the updater
|
|
48
|
+
const draft = this.createDraft();
|
|
49
|
+
updater(draft);
|
|
50
|
+
|
|
51
|
+
// Apply changes
|
|
52
|
+
this.state = draft;
|
|
53
|
+
this.notifyListeners();
|
|
54
|
+
this.persistState();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Batch multiple updates
|
|
59
|
+
*/
|
|
60
|
+
batch(updaters: StateUpdater[]): void {
|
|
61
|
+
const draft = this.createDraft();
|
|
62
|
+
for (const updater of updaters) {
|
|
63
|
+
updater(draft);
|
|
64
|
+
}
|
|
65
|
+
this.state = draft;
|
|
66
|
+
this.notifyListeners();
|
|
67
|
+
this.persistState();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Subscribe to state changes with a selector
|
|
72
|
+
*/
|
|
73
|
+
subscribe<T>(
|
|
74
|
+
selector: StateSelector<T>,
|
|
75
|
+
callback: StateSubscriber<T>
|
|
76
|
+
): vscode.Disposable {
|
|
77
|
+
let previousValue = selector(this.state);
|
|
78
|
+
|
|
79
|
+
const listener = (state: ExtensionState): void => {
|
|
80
|
+
const newValue = selector(state);
|
|
81
|
+
if (!this.shallowEqual(previousValue, newValue)) {
|
|
82
|
+
previousValue = newValue;
|
|
83
|
+
callback(newValue);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
this.listeners.add(listener);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
dispose: () => {
|
|
91
|
+
this.listeners.delete(listener);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Subscribe to all state changes
|
|
98
|
+
*/
|
|
99
|
+
subscribeAll(callback: (state: ExtensionState) => void): vscode.Disposable {
|
|
100
|
+
this.listeners.add(callback);
|
|
101
|
+
return {
|
|
102
|
+
dispose: () => {
|
|
103
|
+
this.listeners.delete(callback);
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Reset state to initial values
|
|
110
|
+
*/
|
|
111
|
+
reset(): void {
|
|
112
|
+
this.state = createInitialState();
|
|
113
|
+
this.notifyListeners();
|
|
114
|
+
this.persistState();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Dispose the state manager
|
|
119
|
+
*/
|
|
120
|
+
dispose(): void {
|
|
121
|
+
this.listeners.clear();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private createDraft(): ExtensionState {
|
|
125
|
+
// Deep clone for immutability
|
|
126
|
+
return JSON.parse(JSON.stringify(this.state));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private notifyListeners(): void {
|
|
130
|
+
for (const listener of this.listeners) {
|
|
131
|
+
try {
|
|
132
|
+
listener(this.state);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('[Drift] State listener error:', error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private loadState(): ExtensionState {
|
|
140
|
+
const initial = createInitialState();
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const persisted = this.context.globalState.get<Partial<ExtensionState>>(PERSISTENCE_KEY);
|
|
144
|
+
if (persisted) {
|
|
145
|
+
// Only restore preferences, not transient state
|
|
146
|
+
return {
|
|
147
|
+
...initial,
|
|
148
|
+
preferences: {
|
|
149
|
+
...initial.preferences,
|
|
150
|
+
...persisted.preferences,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error('[Drift] Failed to load persisted state:', error);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return initial;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private persistState(): void {
|
|
162
|
+
// Only persist user preferences
|
|
163
|
+
const toPersist = {
|
|
164
|
+
preferences: this.state.preferences,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
this.context.globalState.update(PERSISTENCE_KEY, toPersist).then(
|
|
168
|
+
() => {},
|
|
169
|
+
(error) => { console.error('[Drift] Failed to persist state:', error); }
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private shallowEqual(a: unknown, b: unknown): boolean {
|
|
174
|
+
if (a === b) {return true;}
|
|
175
|
+
if (typeof a !== typeof b) {return false;}
|
|
176
|
+
if (typeof a !== 'object' || a === null || b === null) {return false;}
|
|
177
|
+
|
|
178
|
+
const keysA = Object.keys(a);
|
|
179
|
+
const keysB = Object.keys(b as object);
|
|
180
|
+
|
|
181
|
+
if (keysA.length !== keysB.length) {return false;}
|
|
182
|
+
|
|
183
|
+
for (const key of keysA) {
|
|
184
|
+
if ((a as Record<string, unknown>)[key] !== (b as Record<string, unknown>)[key]) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Factory function for creating state manager
|
|
195
|
+
*/
|
|
196
|
+
export function createStateManager(context: vscode.ExtensionContext): StateManager {
|
|
197
|
+
return new StateManager(context);
|
|
198
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration type definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Severity } from './extension-types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Server configuration
|
|
9
|
+
*/
|
|
10
|
+
export interface ServerConfig {
|
|
11
|
+
path: string;
|
|
12
|
+
args: string[];
|
|
13
|
+
trace: 'off' | 'messages' | 'verbose';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Scan configuration
|
|
18
|
+
*/
|
|
19
|
+
export interface ScanConfig {
|
|
20
|
+
onSave: boolean;
|
|
21
|
+
onOpen: boolean;
|
|
22
|
+
debounceMs: number;
|
|
23
|
+
excludePatterns: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Display configuration
|
|
28
|
+
*/
|
|
29
|
+
export interface DisplayConfig {
|
|
30
|
+
showStatusBar: boolean;
|
|
31
|
+
showInlineHints: boolean;
|
|
32
|
+
showGutterIcons: boolean;
|
|
33
|
+
severityFilter: Severity[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* AI provider types
|
|
38
|
+
*/
|
|
39
|
+
export type AIProvider = 'openai' | 'anthropic' | 'ollama' | 'none';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* AI configuration
|
|
43
|
+
*/
|
|
44
|
+
export interface AIConfig {
|
|
45
|
+
enabled: boolean;
|
|
46
|
+
provider: AIProvider;
|
|
47
|
+
model: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Team configuration (from .drift/config.json)
|
|
52
|
+
*/
|
|
53
|
+
export interface TeamConfig {
|
|
54
|
+
enforceApproved: boolean;
|
|
55
|
+
requiredCategories: string[];
|
|
56
|
+
customRules: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Complete extension configuration
|
|
61
|
+
*/
|
|
62
|
+
export interface DriftConfig {
|
|
63
|
+
server: ServerConfig;
|
|
64
|
+
scan: ScanConfig;
|
|
65
|
+
display: DisplayConfig;
|
|
66
|
+
ai: AIConfig;
|
|
67
|
+
team: TeamConfig;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Configuration change event
|
|
72
|
+
*/
|
|
73
|
+
export interface ConfigChangeEvent {
|
|
74
|
+
section: keyof DriftConfig;
|
|
75
|
+
oldValue: unknown;
|
|
76
|
+
newValue: unknown;
|
|
77
|
+
}
|