driftdetect-vscode 0.9.32 → 0.9.34
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/package.json +14 -14
- package/src/activation/activation-controller.ts +13 -13
- package/src/client/connection-manager.ts +11 -9
- package/src/client/request-middleware.ts +6 -6
- package/src/commands/command-router.ts +7 -5
- package/src/commands/handlers/connection-handlers.ts +5 -5
- package/src/commands/handlers/constants-handlers.ts +13 -12
- package/src/commands/handlers/pattern-handlers.ts +3 -3
- package/src/commands/handlers/scan-handlers.ts +1 -1
- package/src/commands/handlers/violation-handlers.ts +6 -3
- package/src/config/config-manager.ts +11 -2
- package/src/config/validator.ts +13 -17
- package/src/extension.ts +2 -1
- package/src/infrastructure/disposable-manager.ts +2 -1
- package/src/infrastructure/logger.ts +3 -8
- package/src/infrastructure/service-container.ts +6 -7
- package/src/state/state-manager.ts +8 -11
- package/src/types/extension-types.ts +1 -1
- package/src/ui/decorations/decoration-controller.ts +4 -5
- package/src/ui/notifications/notification-service.ts +5 -5
- package/src/ui/status-bar/status-bar-controller.ts +3 -3
- package/src/ui/status-bar/status-bar-modes.ts +24 -9
- package/src/views/constants-tree-provider.ts +17 -19
- package/src/views/files-tree-provider.ts +0 -4
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-lint.log +0 -4
- package/.turbo/turbo-test.log +0 -34
- package/LICENSE +0 -121
- package/tsconfig.tsbuildinfo +0 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "driftdetect-vscode",
|
|
3
3
|
"displayName": "Drift - Architectural Drift Detection",
|
|
4
4
|
"description": "Learn your codebase patterns and enforce consistency. Pattern detection, call graph analysis, security visualization, and MCP integration for AI agents. Supports TypeScript, Python, C#, Java, PHP.",
|
|
5
|
-
"version": "0.9.
|
|
5
|
+
"version": "0.9.34",
|
|
6
6
|
"publisher": "driftdetect",
|
|
7
7
|
"license": "Apache-2.0",
|
|
8
8
|
"icon": "resources/drift-icon.png",
|
|
@@ -377,18 +377,6 @@
|
|
|
377
377
|
}
|
|
378
378
|
]
|
|
379
379
|
},
|
|
380
|
-
"dependencies": {
|
|
381
|
-
"driftdetect-lsp": "^0.9.28",
|
|
382
|
-
"vscode-languageclient": "^9.0.1"
|
|
383
|
-
},
|
|
384
|
-
"devDependencies": {
|
|
385
|
-
"@types/node": "^20.10.0",
|
|
386
|
-
"@types/vscode": "^1.85.0",
|
|
387
|
-
"@vitest/coverage-v8": "^1.0.0",
|
|
388
|
-
"@vscode/vsce": "^2.22.0",
|
|
389
|
-
"typescript": "^5.3.0",
|
|
390
|
-
"vitest": "^1.0.0"
|
|
391
|
-
},
|
|
392
380
|
"scripts": {
|
|
393
381
|
"build": "tsc -p tsconfig.json",
|
|
394
382
|
"clean": "rm -rf dist",
|
|
@@ -401,5 +389,17 @@
|
|
|
401
389
|
"typecheck": "tsc --noEmit",
|
|
402
390
|
"vscode:prepublish": "pnpm run build",
|
|
403
391
|
"package": "vsce package --no-dependencies"
|
|
392
|
+
},
|
|
393
|
+
"dependencies": {
|
|
394
|
+
"driftdetect-lsp": "^0.9.28",
|
|
395
|
+
"vscode-languageclient": "^9.0.1"
|
|
396
|
+
},
|
|
397
|
+
"devDependencies": {
|
|
398
|
+
"@types/node": "^20.10.0",
|
|
399
|
+
"@types/vscode": "^1.85.0",
|
|
400
|
+
"@vitest/coverage-v8": "^1.0.0",
|
|
401
|
+
"@vscode/vsce": "^2.22.0",
|
|
402
|
+
"typescript": "^5.3.0",
|
|
403
|
+
"vitest": "^1.0.0"
|
|
404
404
|
}
|
|
405
|
-
}
|
|
405
|
+
}
|
|
@@ -77,7 +77,7 @@ export class ActivationController implements vscode.Disposable {
|
|
|
77
77
|
|
|
78
78
|
// Phase 2: Deferred (non-blocking)
|
|
79
79
|
setImmediate(() => {
|
|
80
|
-
this.runDeferredPhase().catch(error => {
|
|
80
|
+
this.runDeferredPhase().catch((error: unknown) => {
|
|
81
81
|
this.getLogger().error('Deferred activation failed:', error);
|
|
82
82
|
});
|
|
83
83
|
});
|
|
@@ -171,30 +171,30 @@ export class ActivationController implements vscode.Disposable {
|
|
|
171
171
|
private async initializePhase(name: string): Promise<void> {
|
|
172
172
|
switch (name) {
|
|
173
173
|
case 'infrastructure':
|
|
174
|
-
|
|
174
|
+
this.initializeInfrastructure();
|
|
175
175
|
break;
|
|
176
176
|
case 'state':
|
|
177
|
-
|
|
177
|
+
this.initializeState();
|
|
178
178
|
break;
|
|
179
179
|
case 'config':
|
|
180
|
-
|
|
180
|
+
this.initializeConfig();
|
|
181
181
|
break;
|
|
182
182
|
case 'statusBar':
|
|
183
|
-
|
|
183
|
+
this.initializeStatusBar();
|
|
184
184
|
break;
|
|
185
185
|
case 'connection':
|
|
186
186
|
await this.initializeConnection();
|
|
187
187
|
break;
|
|
188
188
|
case 'commands':
|
|
189
|
-
|
|
189
|
+
this.initializeCommands();
|
|
190
190
|
break;
|
|
191
191
|
case 'decorations':
|
|
192
|
-
|
|
192
|
+
this.initializeDecorations();
|
|
193
193
|
break;
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
private
|
|
197
|
+
private initializeInfrastructure(): void {
|
|
198
198
|
// Create logger
|
|
199
199
|
const logger = createLogger('Drift', 'info');
|
|
200
200
|
this.services.register(ServiceKeys.Logger, logger);
|
|
@@ -208,7 +208,7 @@ export class ActivationController implements vscode.Disposable {
|
|
|
208
208
|
logger.info('Infrastructure initialized');
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
private
|
|
211
|
+
private initializeState(): void {
|
|
212
212
|
const stateManager = createStateManager(this.context);
|
|
213
213
|
this.services.register(ServiceKeys.StateManager, stateManager);
|
|
214
214
|
this.disposables.add(stateManager);
|
|
@@ -216,7 +216,7 @@ export class ActivationController implements vscode.Disposable {
|
|
|
216
216
|
this.getLogger().debug('State manager initialized');
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
private
|
|
219
|
+
private initializeConfig(): void {
|
|
220
220
|
const configManager = createConfigManager(this.getLogger());
|
|
221
221
|
this.services.register(ServiceKeys.ConfigManager, configManager);
|
|
222
222
|
this.disposables.add(configManager);
|
|
@@ -224,7 +224,7 @@ export class ActivationController implements vscode.Disposable {
|
|
|
224
224
|
this.getLogger().debug('Config manager initialized');
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
private
|
|
227
|
+
private initializeStatusBar(): void {
|
|
228
228
|
const stateManager = this.services.get<StateManager>(ServiceKeys.StateManager);
|
|
229
229
|
const statusBar = createStatusBar(stateManager);
|
|
230
230
|
this.services.register(ServiceKeys.StatusBar, statusBar);
|
|
@@ -260,7 +260,7 @@ export class ActivationController implements vscode.Disposable {
|
|
|
260
260
|
this.getLogger().debug('Connection manager initialized');
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
private
|
|
263
|
+
private initializeCommands(): void {
|
|
264
264
|
const stateManager = this.services.get<StateManager>(ServiceKeys.StateManager);
|
|
265
265
|
const connectionManager = this.services.get<ConnectionManager>(ServiceKeys.ConnectionManager);
|
|
266
266
|
const eventBus = this.services.get<EventBus>('eventBus');
|
|
@@ -288,7 +288,7 @@ export class ActivationController implements vscode.Disposable {
|
|
|
288
288
|
this.getLogger().debug('Commands initialized');
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
private
|
|
291
|
+
private initializeDecorations(): void {
|
|
292
292
|
const configManager = this.services.get<ConfigManager>(ServiceKeys.ConfigManager);
|
|
293
293
|
|
|
294
294
|
const decorationController = createDecorationController(this.context, configManager);
|
|
@@ -149,7 +149,7 @@ export class ConnectionManager implements vscode.Disposable {
|
|
|
149
149
|
|
|
150
150
|
// Handle client errors
|
|
151
151
|
this.client.onDidChangeState((event: StateChangeEvent) => {
|
|
152
|
-
this.logger.debug(`Client state changed: ${event.oldState} -> ${event.newState}`);
|
|
152
|
+
this.logger.debug(`Client state changed: ${String(event.oldState)} -> ${String(event.newState)}`);
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -168,7 +168,7 @@ export class ConnectionManager implements vscode.Disposable {
|
|
|
168
168
|
await this.delay(100);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
throw new Error(`Server initialization timed out after ${timeout}ms`);
|
|
171
|
+
throw new Error(`Server initialization timed out after ${String(timeout)}ms`);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
private async handleConnectionError(error: unknown): Promise<void> {
|
|
@@ -183,7 +183,7 @@ export class ConnectionManager implements vscode.Disposable {
|
|
|
183
183
|
|
|
184
184
|
if (this.restartCount >= CONNECTION_CONFIG.maxRestarts) {
|
|
185
185
|
this.setState('failed');
|
|
186
|
-
this.logger.error(`Max restart attempts (${CONNECTION_CONFIG.maxRestarts}) reached`);
|
|
186
|
+
this.logger.error(`Max restart attempts (${String(CONNECTION_CONFIG.maxRestarts)}) reached`);
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
189
|
|
|
@@ -191,7 +191,7 @@ export class ConnectionManager implements vscode.Disposable {
|
|
|
191
191
|
this.restartCount++;
|
|
192
192
|
|
|
193
193
|
this.logger.info(
|
|
194
|
-
`Reconnecting in ${delay}ms (attempt ${this.restartCount}/${CONNECTION_CONFIG.maxRestarts})`
|
|
194
|
+
`Reconnecting in ${String(delay)}ms (attempt ${String(this.restartCount)}/${String(CONNECTION_CONFIG.maxRestarts)})`
|
|
195
195
|
);
|
|
196
196
|
|
|
197
197
|
this.setState('reconnecting');
|
|
@@ -208,11 +208,13 @@ export class ConnectionManager implements vscode.Disposable {
|
|
|
208
208
|
private startHealthCheck(): void {
|
|
209
209
|
this.stopHealthCheck();
|
|
210
210
|
|
|
211
|
-
this.healthCheckTimer = setInterval(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
211
|
+
this.healthCheckTimer = setInterval(() => {
|
|
212
|
+
void this.isHealthy().then(healthy => {
|
|
213
|
+
if (!healthy) {
|
|
214
|
+
this.logger.warn('Health check failed, reconnecting...');
|
|
215
|
+
void this.reconnect();
|
|
216
|
+
}
|
|
217
|
+
});
|
|
216
218
|
}, CONNECTION_CONFIG.healthCheckInterval);
|
|
217
219
|
}
|
|
218
220
|
|
|
@@ -56,14 +56,14 @@ export class RequestMiddleware {
|
|
|
56
56
|
|
|
57
57
|
if (attempt < opts.retries) {
|
|
58
58
|
this.logger.warn(
|
|
59
|
-
`Request ${method} failed (attempt ${attempt + 1}/${opts.retries + 1}): ${lastError.message}`
|
|
59
|
+
`Request ${method} failed (attempt ${String(attempt + 1)}/${String(opts.retries + 1)}): ${lastError.message}`
|
|
60
60
|
);
|
|
61
61
|
await this.delay(opts.retryDelay * (attempt + 1));
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
this.logger.error(`Request ${method} failed after ${opts.retries + 1} attempts`);
|
|
66
|
+
this.logger.error(`Request ${method} failed after ${String(opts.retries + 1)} attempts`);
|
|
67
67
|
throw lastError;
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -72,7 +72,7 @@ export class RequestMiddleware {
|
|
|
72
72
|
*/
|
|
73
73
|
notify(method: string, params?: unknown): void {
|
|
74
74
|
try {
|
|
75
|
-
this.client.sendNotification(method, params);
|
|
75
|
+
void this.client.sendNotification(method, params);
|
|
76
76
|
} catch (error) {
|
|
77
77
|
this.logger.error(`Notification ${method} failed:`, error);
|
|
78
78
|
}
|
|
@@ -85,7 +85,7 @@ export class RequestMiddleware {
|
|
|
85
85
|
): Promise<T> {
|
|
86
86
|
return new Promise<T>((resolve, reject) => {
|
|
87
87
|
const timer = setTimeout(() => {
|
|
88
|
-
reject(new Error(`Request ${method} timed out after ${timeout}ms`));
|
|
88
|
+
reject(new Error(`Request ${method} timed out after ${String(timeout)}ms`));
|
|
89
89
|
}, timeout);
|
|
90
90
|
|
|
91
91
|
this.client
|
|
@@ -94,9 +94,9 @@ export class RequestMiddleware {
|
|
|
94
94
|
clearTimeout(timer);
|
|
95
95
|
resolve(result);
|
|
96
96
|
})
|
|
97
|
-
.catch((error) => {
|
|
97
|
+
.catch((error: unknown) => {
|
|
98
98
|
clearTimeout(timer);
|
|
99
|
-
reject(error);
|
|
99
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
102
|
}
|
|
@@ -156,8 +156,10 @@ export class CommandRouter implements vscode.Disposable {
|
|
|
156
156
|
|
|
157
157
|
const next = async (): Promise<void> => {
|
|
158
158
|
if (index < this.middleware.length) {
|
|
159
|
-
const mw = this.middleware[index++]
|
|
160
|
-
|
|
159
|
+
const mw = this.middleware[index++];
|
|
160
|
+
if (mw) {
|
|
161
|
+
await mw(ctx, next);
|
|
162
|
+
}
|
|
161
163
|
} else {
|
|
162
164
|
await handler(ctx);
|
|
163
165
|
}
|
|
@@ -170,13 +172,13 @@ export class CommandRouter implements vscode.Disposable {
|
|
|
170
172
|
const message = error instanceof Error ? error.message : String(error);
|
|
171
173
|
this.logger.error(`Command ${command} failed:`, message);
|
|
172
174
|
|
|
173
|
-
vscode.window
|
|
175
|
+
void vscode.window
|
|
174
176
|
.showErrorMessage(`Drift: ${message}`, 'Retry', 'Show Logs')
|
|
175
177
|
.then((action) => {
|
|
176
178
|
if (action === 'Retry') {
|
|
177
|
-
vscode.commands.executeCommand(command);
|
|
179
|
+
void vscode.commands.executeCommand(command);
|
|
178
180
|
} else if (action === 'Show Logs') {
|
|
179
|
-
vscode.commands.executeCommand('drift.showLogs');
|
|
181
|
+
void vscode.commands.executeCommand('drift.showLogs');
|
|
180
182
|
}
|
|
181
183
|
});
|
|
182
184
|
}
|
|
@@ -44,10 +44,10 @@ export function createConnectionHandlers(
|
|
|
44
44
|
|
|
45
45
|
const items = [
|
|
46
46
|
`Connection: ${connection.status}`,
|
|
47
|
-
`Server Version: ${connection.serverVersion
|
|
48
|
-
`Patterns: ${patterns.total}`,
|
|
49
|
-
`Violations: ${violations.total}`,
|
|
50
|
-
`Last Scan: ${workspace.lastScanTime ? new Date(workspace.lastScanTime).toLocaleString() : 'Never'}`,
|
|
47
|
+
`Server Version: ${connection.serverVersion ?? 'Unknown'}`,
|
|
48
|
+
`Patterns: ${String(patterns.total)}`,
|
|
49
|
+
`Violations: ${String(violations.total)}`,
|
|
50
|
+
`Last Scan: ${workspace.lastScanTime !== null ? new Date(workspace.lastScanTime).toLocaleString() : 'Never'}`,
|
|
51
51
|
];
|
|
52
52
|
|
|
53
53
|
await notifications.info(items.join('\n'), [], { detail: 'Drift Status' });
|
|
@@ -61,7 +61,7 @@ export function createConnectionHandlers(
|
|
|
61
61
|
const state = stateManager.getState();
|
|
62
62
|
const error = state.connection.lastError;
|
|
63
63
|
|
|
64
|
-
if (error) {
|
|
64
|
+
if (error !== null && error !== '') {
|
|
65
65
|
await notifications.error(error, [
|
|
66
66
|
{ title: 'Retry', command: DRIFT_COMMANDS.reconnect },
|
|
67
67
|
{ title: 'Show Logs', command: 'drift.showLogs' },
|
|
@@ -18,7 +18,7 @@ export function createConstantsHandlers(
|
|
|
18
18
|
/**
|
|
19
19
|
* Show constants view by category
|
|
20
20
|
*/
|
|
21
|
-
'drift.showConstantsByCategory': async () => {
|
|
21
|
+
'drift.showConstantsByCategory': async (): Promise<void> => {
|
|
22
22
|
constantsProvider.setViewMode('category');
|
|
23
23
|
await vscode.commands.executeCommand('drift.constantsView.focus');
|
|
24
24
|
},
|
|
@@ -26,7 +26,7 @@ export function createConstantsHandlers(
|
|
|
26
26
|
/**
|
|
27
27
|
* Show constants view by language
|
|
28
28
|
*/
|
|
29
|
-
'drift.showConstantsByLanguage': async () => {
|
|
29
|
+
'drift.showConstantsByLanguage': async (): Promise<void> => {
|
|
30
30
|
constantsProvider.setViewMode('language');
|
|
31
31
|
await vscode.commands.executeCommand('drift.constantsView.focus');
|
|
32
32
|
},
|
|
@@ -34,7 +34,7 @@ export function createConstantsHandlers(
|
|
|
34
34
|
/**
|
|
35
35
|
* Show constant issues
|
|
36
36
|
*/
|
|
37
|
-
'drift.showConstantIssues': async () => {
|
|
37
|
+
'drift.showConstantIssues': async (): Promise<void> => {
|
|
38
38
|
constantsProvider.setViewMode('issues');
|
|
39
39
|
await vscode.commands.executeCommand('drift.constantsView.focus');
|
|
40
40
|
},
|
|
@@ -42,7 +42,7 @@ export function createConstantsHandlers(
|
|
|
42
42
|
/**
|
|
43
43
|
* Go to constant definition
|
|
44
44
|
*/
|
|
45
|
-
'drift.goToConstant': async (file: unknown, line: unknown) => {
|
|
45
|
+
'drift.goToConstant': async (file: unknown, line: unknown): Promise<void> => {
|
|
46
46
|
if (typeof file !== 'string' || typeof line !== 'number') {
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
@@ -58,30 +58,31 @@ export function createConstantsHandlers(
|
|
|
58
58
|
new vscode.Range(position, position),
|
|
59
59
|
vscode.TextEditorRevealType.InCenter
|
|
60
60
|
);
|
|
61
|
-
} catch
|
|
62
|
-
vscode.window.showErrorMessage(`Failed to open file: ${file}`);
|
|
61
|
+
} catch {
|
|
62
|
+
void vscode.window.showErrorMessage(`Failed to open file: ${file}`);
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* Find constant usages
|
|
68
68
|
*/
|
|
69
|
-
'drift.findConstantUsages': async (constantName: unknown) => {
|
|
70
|
-
|
|
69
|
+
'drift.findConstantUsages': async (constantName: unknown): Promise<void> => {
|
|
70
|
+
let searchName = constantName;
|
|
71
|
+
if (typeof searchName !== 'string' || searchName === '') {
|
|
71
72
|
// If no constant name provided, prompt for one
|
|
72
73
|
const input = await vscode.window.showInputBox({
|
|
73
74
|
prompt: 'Enter constant name to search for',
|
|
74
75
|
placeHolder: 'CONSTANT_NAME',
|
|
75
76
|
});
|
|
76
|
-
if (
|
|
77
|
+
if (input === null || input === '') {
|
|
77
78
|
return;
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
+
searchName = input;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
// Use VSCode's built-in search
|
|
83
84
|
await vscode.commands.executeCommand('workbench.action.findInFiles', {
|
|
84
|
-
query:
|
|
85
|
+
query: searchName,
|
|
85
86
|
isRegex: false,
|
|
86
87
|
isCaseSensitive: true,
|
|
87
88
|
matchWholeWord: true,
|
|
@@ -91,7 +92,7 @@ export function createConstantsHandlers(
|
|
|
91
92
|
/**
|
|
92
93
|
* Show constants overview
|
|
93
94
|
*/
|
|
94
|
-
'drift.showConstants': async () => {
|
|
95
|
+
'drift.showConstants': async (): Promise<void> => {
|
|
95
96
|
constantsProvider.setViewMode('category');
|
|
96
97
|
await vscode.commands.executeCommand('drift.constantsView.focus');
|
|
97
98
|
},
|
|
@@ -48,7 +48,7 @@ export function createPatternHandlers(
|
|
|
48
48
|
|
|
49
49
|
const patternId = ctx.args[0] as string | undefined;
|
|
50
50
|
|
|
51
|
-
if (
|
|
51
|
+
if (patternId === null || patternId === '') {
|
|
52
52
|
// Show pattern picker
|
|
53
53
|
const patterns = await client.sendRequest<Array<{ id: string; name: string }>>(
|
|
54
54
|
'drift/patterns/list',
|
|
@@ -90,7 +90,7 @@ export function createPatternHandlers(
|
|
|
90
90
|
|
|
91
91
|
const patternId = ctx.args[0] as string | undefined;
|
|
92
92
|
|
|
93
|
-
if (
|
|
93
|
+
if (patternId === null || patternId === '') {
|
|
94
94
|
// Show pattern picker
|
|
95
95
|
const patterns = await client.sendRequest<Array<{ id: string; name: string }>>(
|
|
96
96
|
'drift/patterns/list',
|
|
@@ -133,7 +133,7 @@ export function createPatternHandlers(
|
|
|
133
133
|
const patternId = ctx.args[0] as string | undefined;
|
|
134
134
|
const violationId = ctx.args[1] as string | undefined;
|
|
135
135
|
|
|
136
|
-
if (
|
|
136
|
+
if (patternId === null || patternId === '') {
|
|
137
137
|
await notifications.warning('No pattern specified for variant creation.');
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
@@ -67,7 +67,7 @@ export function createScanHandlers(
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
await notifications.info(
|
|
70
|
-
`Scan complete: ${result.patterns} patterns, ${result.violations} violations`
|
|
70
|
+
`Scan complete: ${String(result.patterns)} patterns, ${String(result.violations)} violations`
|
|
71
71
|
);
|
|
72
72
|
} catch (error) {
|
|
73
73
|
stateManager.update(draft => {
|
|
@@ -43,7 +43,7 @@ export function createViolationHandlers(
|
|
|
43
43
|
const uri = ctx.args[1] as string | undefined;
|
|
44
44
|
const line = ctx.args[2] as number | undefined;
|
|
45
45
|
|
|
46
|
-
if (
|
|
46
|
+
if (violationId === null || violationId === '' || uri === null || uri === '' || line === undefined) {
|
|
47
47
|
// Try to get from current editor position
|
|
48
48
|
const editor = vscode.window.activeTextEditor;
|
|
49
49
|
if (!editor) {
|
|
@@ -63,8 +63,11 @@ export function createViolationHandlers(
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// Extract violation ID from diagnostic data
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
interface DiagnosticData {
|
|
67
|
+
violationId?: string;
|
|
68
|
+
}
|
|
69
|
+
const data = (driftDiagnostic as vscode.Diagnostic & { data?: DiagnosticData }).data;
|
|
70
|
+
if (data?.violationId === null || data?.violationId === undefined) {
|
|
68
71
|
await notifications.warning('Cannot identify violation.');
|
|
69
72
|
return;
|
|
70
73
|
}
|
|
@@ -139,11 +139,20 @@ export class ConfigManager implements vscode.Disposable {
|
|
|
139
139
|
try {
|
|
140
140
|
if (fs.existsSync(configPath)) {
|
|
141
141
|
const content = fs.readFileSync(configPath, 'utf-8');
|
|
142
|
-
|
|
142
|
+
interface ParsedTeamConfig {
|
|
143
|
+
enforceApproved?: boolean;
|
|
144
|
+
requiredCategories?: string[];
|
|
145
|
+
customRules?: Array<string | { id: string; severity: string }>;
|
|
146
|
+
}
|
|
147
|
+
const parsed: ParsedTeamConfig = JSON.parse(content) as ParsedTeamConfig;
|
|
148
|
+
// Map customRules objects to just their IDs if they're objects
|
|
149
|
+
const customRules = parsed.customRules
|
|
150
|
+
? parsed.customRules.map((rule) => (typeof rule === 'string' ? rule : rule.id))
|
|
151
|
+
: DEFAULT_CONFIG.team.customRules;
|
|
143
152
|
return {
|
|
144
153
|
enforceApproved: parsed.enforceApproved ?? DEFAULT_CONFIG.team.enforceApproved,
|
|
145
154
|
requiredCategories: parsed.requiredCategories ?? DEFAULT_CONFIG.team.requiredCategories,
|
|
146
|
-
customRules
|
|
155
|
+
customRules,
|
|
147
156
|
};
|
|
148
157
|
}
|
|
149
158
|
} catch (error) {
|
package/src/config/validator.ts
CHANGED
|
@@ -36,39 +36,35 @@ export function validateConfig(config: Partial<DriftConfig>): ValidationResult {
|
|
|
36
36
|
const errors: string[] = [];
|
|
37
37
|
|
|
38
38
|
// Validate server config
|
|
39
|
-
if (config.server) {
|
|
40
|
-
|
|
41
|
-
errors.push(`Invalid trace level: ${config.server.trace}`);
|
|
42
|
-
}
|
|
39
|
+
if (config.server?.trace !== undefined && !VALID_TRACE_LEVELS.includes(config.server.trace)) {
|
|
40
|
+
errors.push(`Invalid trace level: ${config.server.trace}`);
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
// Validate scan config
|
|
46
|
-
if (config.scan) {
|
|
44
|
+
if (config.scan !== undefined) {
|
|
47
45
|
if (typeof config.scan.debounceMs === 'number' && config.scan.debounceMs < 0) {
|
|
48
46
|
errors.push('debounceMs must be non-negative');
|
|
49
47
|
}
|
|
50
|
-
if (config.scan.excludePatterns && !Array.isArray(config.scan.excludePatterns)) {
|
|
48
|
+
if (config.scan.excludePatterns !== undefined && !Array.isArray(config.scan.excludePatterns)) {
|
|
51
49
|
errors.push('excludePatterns must be an array');
|
|
52
50
|
}
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
// Validate display config
|
|
56
|
-
if (config.display) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
errors.push(`Invalid severity: ${severity}`);
|
|
61
|
-
}
|
|
54
|
+
if (config.display?.severityFilter !== undefined) {
|
|
55
|
+
for (const severity of config.display.severityFilter) {
|
|
56
|
+
if (!VALID_SEVERITIES.includes(severity)) {
|
|
57
|
+
errors.push(`Invalid severity: ${severity}`);
|
|
62
58
|
}
|
|
63
59
|
}
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
// Validate AI config
|
|
67
|
-
if (config.ai) {
|
|
68
|
-
if (config.ai.provider && !VALID_PROVIDERS.includes(config.ai.provider)) {
|
|
63
|
+
if (config.ai !== undefined) {
|
|
64
|
+
if (config.ai.provider !== undefined && !VALID_PROVIDERS.includes(config.ai.provider)) {
|
|
69
65
|
errors.push(`Invalid AI provider: ${config.ai.provider}`);
|
|
70
66
|
}
|
|
71
|
-
if (config.ai.enabled && config.ai.provider === 'none') {
|
|
67
|
+
if (config.ai.enabled === true && config.ai.provider === 'none') {
|
|
72
68
|
errors.push('AI enabled but provider is "none"');
|
|
73
69
|
}
|
|
74
70
|
}
|
|
@@ -99,14 +95,14 @@ export function validateConfigValue(
|
|
|
99
95
|
if (key === 'severityFilter' && Array.isArray(value)) {
|
|
100
96
|
for (const v of value) {
|
|
101
97
|
if (!VALID_SEVERITIES.includes(v as Severity)) {
|
|
102
|
-
errors.push(`Invalid severity: ${v}`);
|
|
98
|
+
errors.push(`Invalid severity: ${String(v)}`);
|
|
103
99
|
}
|
|
104
100
|
}
|
|
105
101
|
}
|
|
106
102
|
break;
|
|
107
103
|
case 'ai':
|
|
108
104
|
if (key === 'provider' && !VALID_PROVIDERS.includes(value as AIProvider)) {
|
|
109
|
-
errors.push(`Invalid AI provider: ${value}`);
|
|
105
|
+
errors.push(`Invalid AI provider: ${String(value)}`);
|
|
110
106
|
}
|
|
111
107
|
break;
|
|
112
108
|
}
|
package/src/extension.ts
CHANGED
|
@@ -15,7 +15,8 @@ import { createActivationController, type ActivationController } from './activat
|
|
|
15
15
|
*/
|
|
16
16
|
export function getVersion(): string {
|
|
17
17
|
const extension = vscode.extensions.getExtension('driftdetect.driftdetect-vscode');
|
|
18
|
-
|
|
18
|
+
const packageJSON = extension?.packageJSON as { version?: string } | undefined;
|
|
19
|
+
return packageJSON?.version ?? '0.0.0';
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
// For backwards compatibility
|
|
@@ -59,7 +59,8 @@ export class DisposableManager implements IDisposableManager, vscode.Disposable
|
|
|
59
59
|
try {
|
|
60
60
|
disposable?.dispose();
|
|
61
61
|
} catch (error) {
|
|
62
|
-
|
|
62
|
+
// Log error but continue disposing other resources
|
|
63
|
+
void error;
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
}
|
|
@@ -76,13 +76,13 @@ export class Logger implements ILogger {
|
|
|
76
76
|
const argsStr = args
|
|
77
77
|
.map(arg => {
|
|
78
78
|
if (arg instanceof Error) {
|
|
79
|
-
return `${arg.message}\n${arg.stack}`;
|
|
79
|
+
return `${arg.message}\n${arg.stack ?? ''}`;
|
|
80
80
|
}
|
|
81
|
-
if (typeof arg === 'object') {
|
|
81
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
82
82
|
try {
|
|
83
83
|
return JSON.stringify(arg, null, 2);
|
|
84
84
|
} catch {
|
|
85
|
-
return
|
|
85
|
+
return '[Object]';
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
return String(arg);
|
|
@@ -92,11 +92,6 @@ export class Logger implements ILogger {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
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
95
|
}
|
|
101
96
|
}
|
|
102
97
|
|
|
@@ -29,19 +29,19 @@ export class ServiceContainer implements IServiceContainer {
|
|
|
29
29
|
/**
|
|
30
30
|
* Register a service instance directly
|
|
31
31
|
*/
|
|
32
|
-
register
|
|
32
|
+
register(key: string, instance: unknown): void {
|
|
33
33
|
this.instances.set(key, instance);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Register a service factory for lazy instantiation
|
|
38
38
|
*/
|
|
39
|
-
registerFactory
|
|
39
|
+
registerFactory(
|
|
40
40
|
key: string,
|
|
41
|
-
factory: ServiceFactory<
|
|
41
|
+
factory: ServiceFactory<unknown>,
|
|
42
42
|
options: ServiceOptions = { singleton: true }
|
|
43
43
|
): void {
|
|
44
|
-
this.factories.set(key, factory
|
|
44
|
+
this.factories.set(key, factory);
|
|
45
45
|
this.options.set(key, options);
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -83,11 +83,10 @@ export class ServiceContainer implements IServiceContainer {
|
|
|
83
83
|
* Try to get a service, returning undefined if not found
|
|
84
84
|
*/
|
|
85
85
|
tryGet<T>(key: string): T | undefined {
|
|
86
|
-
|
|
86
|
+
if (this.has(key)) {
|
|
87
87
|
return this.get<T>(key);
|
|
88
|
-
} catch {
|
|
89
|
-
return undefined;
|
|
90
88
|
}
|
|
89
|
+
return undefined;
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
/**
|