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.
Files changed (309) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +138 -0
  3. package/.vscode/launch.json +17 -0
  4. package/.vscode/tasks.json +15 -0
  5. package/LICENSE +121 -0
  6. package/dist/activation/activation-controller.d.ts +61 -0
  7. package/dist/activation/activation-controller.d.ts.map +1 -0
  8. package/dist/activation/activation-controller.js +235 -0
  9. package/dist/activation/activation-controller.js.map +1 -0
  10. package/dist/activation/activation-phases.d.ts +28 -0
  11. package/dist/activation/activation-phases.d.ts.map +1 -0
  12. package/dist/activation/activation-phases.js +80 -0
  13. package/dist/activation/activation-phases.js.map +1 -0
  14. package/dist/activation/index.d.ts +6 -0
  15. package/dist/activation/index.d.ts.map +1 -0
  16. package/dist/activation/index.js +6 -0
  17. package/dist/activation/index.js.map +1 -0
  18. package/dist/client/connection-config.d.ts +50 -0
  19. package/dist/client/connection-config.d.ts.map +1 -0
  20. package/dist/client/connection-config.js +56 -0
  21. package/dist/client/connection-config.js.map +1 -0
  22. package/dist/client/connection-manager.d.ts +70 -0
  23. package/dist/client/connection-manager.d.ts.map +1 -0
  24. package/dist/client/connection-manager.js +214 -0
  25. package/dist/client/connection-manager.js.map +1 -0
  26. package/dist/client/index.d.ts +8 -0
  27. package/dist/client/index.d.ts.map +1 -0
  28. package/dist/client/index.js +8 -0
  29. package/dist/client/index.js.map +1 -0
  30. package/dist/client/language-client-factory.d.ts +29 -0
  31. package/dist/client/language-client-factory.d.ts.map +1 -0
  32. package/dist/client/language-client-factory.js +76 -0
  33. package/dist/client/language-client-factory.js.map +1 -0
  34. package/dist/client/request-middleware.d.ts +38 -0
  35. package/dist/client/request-middleware.d.ts.map +1 -0
  36. package/dist/client/request-middleware.js +85 -0
  37. package/dist/client/request-middleware.js.map +1 -0
  38. package/dist/commands/command-definitions.d.ts +53 -0
  39. package/dist/commands/command-definitions.d.ts.map +1 -0
  40. package/dist/commands/command-definitions.js +212 -0
  41. package/dist/commands/command-definitions.js.map +1 -0
  42. package/dist/commands/command-router.d.ts +80 -0
  43. package/dist/commands/command-router.d.ts.map +1 -0
  44. package/dist/commands/command-router.js +127 -0
  45. package/dist/commands/command-router.js.map +1 -0
  46. package/dist/commands/handlers/connection-handlers.d.ts +14 -0
  47. package/dist/commands/handlers/connection-handlers.d.ts.map +1 -0
  48. package/dist/commands/handlers/connection-handlers.js +57 -0
  49. package/dist/commands/handlers/connection-handlers.js.map +1 -0
  50. package/dist/commands/handlers/constants-handlers.d.ts +11 -0
  51. package/dist/commands/handlers/constants-handlers.d.ts.map +1 -0
  52. package/dist/commands/handlers/constants-handlers.js +84 -0
  53. package/dist/commands/handlers/constants-handlers.js.map +1 -0
  54. package/dist/commands/handlers/index.d.ts +10 -0
  55. package/dist/commands/handlers/index.d.ts.map +1 -0
  56. package/dist/commands/handlers/index.js +10 -0
  57. package/dist/commands/handlers/index.js.map +1 -0
  58. package/dist/commands/handlers/pattern-handlers.d.ts +13 -0
  59. package/dist/commands/handlers/pattern-handlers.d.ts.map +1 -0
  60. package/dist/commands/handlers/pattern-handlers.js +127 -0
  61. package/dist/commands/handlers/pattern-handlers.js.map +1 -0
  62. package/dist/commands/handlers/scan-handlers.d.ts +15 -0
  63. package/dist/commands/handlers/scan-handlers.d.ts.map +1 -0
  64. package/dist/commands/handlers/scan-handlers.js +74 -0
  65. package/dist/commands/handlers/scan-handlers.js.map +1 -0
  66. package/dist/commands/handlers/ui-handlers.d.ts +12 -0
  67. package/dist/commands/handlers/ui-handlers.d.ts.map +1 -0
  68. package/dist/commands/handlers/ui-handlers.js +74 -0
  69. package/dist/commands/handlers/ui-handlers.js.map +1 -0
  70. package/dist/commands/handlers/violation-handlers.d.ts +13 -0
  71. package/dist/commands/handlers/violation-handlers.d.ts.map +1 -0
  72. package/dist/commands/handlers/violation-handlers.js +76 -0
  73. package/dist/commands/handlers/violation-handlers.js.map +1 -0
  74. package/dist/commands/index.d.ts +7 -0
  75. package/dist/commands/index.d.ts.map +1 -0
  76. package/dist/commands/index.js +7 -0
  77. package/dist/commands/index.js.map +1 -0
  78. package/dist/commands/middleware/connection-check-middleware.d.ts +12 -0
  79. package/dist/commands/middleware/connection-check-middleware.d.ts.map +1 -0
  80. package/dist/commands/middleware/connection-check-middleware.js +34 -0
  81. package/dist/commands/middleware/connection-check-middleware.js.map +1 -0
  82. package/dist/commands/middleware/index.d.ts +7 -0
  83. package/dist/commands/middleware/index.d.ts.map +1 -0
  84. package/dist/commands/middleware/index.js +7 -0
  85. package/dist/commands/middleware/index.js.map +1 -0
  86. package/dist/commands/middleware/logging-middleware.d.ts +12 -0
  87. package/dist/commands/middleware/logging-middleware.d.ts.map +1 -0
  88. package/dist/commands/middleware/logging-middleware.js +24 -0
  89. package/dist/commands/middleware/logging-middleware.js.map +1 -0
  90. package/dist/commands/middleware/telemetry-middleware.d.ts +22 -0
  91. package/dist/commands/middleware/telemetry-middleware.d.ts.map +1 -0
  92. package/dist/commands/middleware/telemetry-middleware.js +30 -0
  93. package/dist/commands/middleware/telemetry-middleware.js.map +1 -0
  94. package/dist/config/config-manager.d.ts +53 -0
  95. package/dist/config/config-manager.d.ts.map +1 -0
  96. package/dist/config/config-manager.js +178 -0
  97. package/dist/config/config-manager.js.map +1 -0
  98. package/dist/config/defaults.d.ts +11 -0
  99. package/dist/config/defaults.d.ts.map +1 -0
  100. package/dist/config/defaults.js +43 -0
  101. package/dist/config/defaults.js.map +1 -0
  102. package/dist/config/index.d.ts +7 -0
  103. package/dist/config/index.d.ts.map +1 -0
  104. package/dist/config/index.js +7 -0
  105. package/dist/config/index.js.map +1 -0
  106. package/dist/config/validator.d.ts +22 -0
  107. package/dist/config/validator.d.ts.map +1 -0
  108. package/dist/config/validator.js +93 -0
  109. package/dist/config/validator.js.map +1 -0
  110. package/dist/extension.d.ts +32 -0
  111. package/dist/extension.d.ts.map +1 -0
  112. package/dist/extension.js +50 -0
  113. package/dist/extension.js.map +1 -0
  114. package/dist/infrastructure/disposable-manager.d.ts +43 -0
  115. package/dist/infrastructure/disposable-manager.d.ts.map +1 -0
  116. package/dist/infrastructure/disposable-manager.js +75 -0
  117. package/dist/infrastructure/disposable-manager.js.map +1 -0
  118. package/dist/infrastructure/event-bus.d.ts +85 -0
  119. package/dist/infrastructure/event-bus.d.ts.map +1 -0
  120. package/dist/infrastructure/event-bus.js +74 -0
  121. package/dist/infrastructure/event-bus.js.map +1 -0
  122. package/dist/infrastructure/index.d.ts +10 -0
  123. package/dist/infrastructure/index.d.ts.map +1 -0
  124. package/dist/infrastructure/index.js +10 -0
  125. package/dist/infrastructure/index.js.map +1 -0
  126. package/dist/infrastructure/logger.d.ts +37 -0
  127. package/dist/infrastructure/logger.d.ts.map +1 -0
  128. package/dist/infrastructure/logger.js +86 -0
  129. package/dist/infrastructure/logger.js.map +1 -0
  130. package/dist/infrastructure/service-container.d.ts +68 -0
  131. package/dist/infrastructure/service-container.d.ts.map +1 -0
  132. package/dist/infrastructure/service-container.js +94 -0
  133. package/dist/infrastructure/service-container.js.map +1 -0
  134. package/dist/state/index.d.ts +7 -0
  135. package/dist/state/index.d.ts.map +1 -0
  136. package/dist/state/index.js +7 -0
  137. package/dist/state/index.js.map +1 -0
  138. package/dist/state/initial-state.d.ts +11 -0
  139. package/dist/state/initial-state.d.ts.map +1 -0
  140. package/dist/state/initial-state.js +58 -0
  141. package/dist/state/initial-state.js.map +1 -0
  142. package/dist/state/selectors.d.ts +41 -0
  143. package/dist/state/selectors.d.ts.map +1 -0
  144. package/dist/state/selectors.js +61 -0
  145. package/dist/state/selectors.js.map +1 -0
  146. package/dist/state/state-manager.d.ts +54 -0
  147. package/dist/state/state-manager.d.ts.map +1 -0
  148. package/dist/state/state-manager.js +166 -0
  149. package/dist/state/state-manager.js.map +1 -0
  150. package/dist/types/config-types.d.ts +69 -0
  151. package/dist/types/config-types.d.ts.map +1 -0
  152. package/dist/types/config-types.js +5 -0
  153. package/dist/types/config-types.js.map +1 -0
  154. package/dist/types/extension-types.d.ts +45 -0
  155. package/dist/types/extension-types.d.ts.map +1 -0
  156. package/dist/types/extension-types.js +5 -0
  157. package/dist/types/extension-types.js.map +1 -0
  158. package/dist/types/index.d.ts +12 -0
  159. package/dist/types/index.d.ts.map +1 -0
  160. package/dist/types/index.js +12 -0
  161. package/dist/types/index.js.map +1 -0
  162. package/dist/types/lsp-types.d.ts +70 -0
  163. package/dist/types/lsp-types.d.ts.map +1 -0
  164. package/dist/types/lsp-types.js +5 -0
  165. package/dist/types/lsp-types.js.map +1 -0
  166. package/dist/types/state-types.d.ts +82 -0
  167. package/dist/types/state-types.d.ts.map +1 -0
  168. package/dist/types/state-types.js +5 -0
  169. package/dist/types/state-types.js.map +1 -0
  170. package/dist/types/vscode-types.d.ts +36 -0
  171. package/dist/types/vscode-types.d.ts.map +1 -0
  172. package/dist/types/vscode-types.js +7 -0
  173. package/dist/types/vscode-types.js.map +1 -0
  174. package/dist/ui/decorations/decoration-controller.d.ts +45 -0
  175. package/dist/ui/decorations/decoration-controller.d.ts.map +1 -0
  176. package/dist/ui/decorations/decoration-controller.js +198 -0
  177. package/dist/ui/decorations/decoration-controller.js.map +1 -0
  178. package/dist/ui/decorations/decoration-types.d.ts +28 -0
  179. package/dist/ui/decorations/decoration-types.d.ts.map +1 -0
  180. package/dist/ui/decorations/decoration-types.js +98 -0
  181. package/dist/ui/decorations/decoration-types.js.map +1 -0
  182. package/dist/ui/decorations/index.d.ts +7 -0
  183. package/dist/ui/decorations/index.d.ts.map +1 -0
  184. package/dist/ui/decorations/index.js +6 -0
  185. package/dist/ui/decorations/index.js.map +1 -0
  186. package/dist/ui/index.d.ts +7 -0
  187. package/dist/ui/index.d.ts.map +1 -0
  188. package/dist/ui/index.js +7 -0
  189. package/dist/ui/index.js.map +1 -0
  190. package/dist/ui/notifications/index.d.ts +5 -0
  191. package/dist/ui/notifications/index.d.ts.map +1 -0
  192. package/dist/ui/notifications/index.js +5 -0
  193. package/dist/ui/notifications/index.js.map +1 -0
  194. package/dist/ui/notifications/notification-service.d.ts +66 -0
  195. package/dist/ui/notifications/notification-service.d.ts.map +1 -0
  196. package/dist/ui/notifications/notification-service.js +103 -0
  197. package/dist/ui/notifications/notification-service.js.map +1 -0
  198. package/dist/ui/status-bar/index.d.ts +6 -0
  199. package/dist/ui/status-bar/index.d.ts.map +1 -0
  200. package/dist/ui/status-bar/index.js +6 -0
  201. package/dist/ui/status-bar/index.js.map +1 -0
  202. package/dist/ui/status-bar/status-bar-controller.d.ts +37 -0
  203. package/dist/ui/status-bar/status-bar-controller.d.ts.map +1 -0
  204. package/dist/ui/status-bar/status-bar-controller.js +111 -0
  205. package/dist/ui/status-bar/status-bar-controller.js.map +1 -0
  206. package/dist/ui/status-bar/status-bar-modes.d.ts +26 -0
  207. package/dist/ui/status-bar/status-bar-modes.d.ts.map +1 -0
  208. package/dist/ui/status-bar/status-bar-modes.js +97 -0
  209. package/dist/ui/status-bar/status-bar-modes.js.map +1 -0
  210. package/dist/views/base-tree-provider.d.ts +74 -0
  211. package/dist/views/base-tree-provider.d.ts.map +1 -0
  212. package/dist/views/base-tree-provider.js +95 -0
  213. package/dist/views/base-tree-provider.js.map +1 -0
  214. package/dist/views/constants-tree-provider.d.ts +112 -0
  215. package/dist/views/constants-tree-provider.d.ts.map +1 -0
  216. package/dist/views/constants-tree-provider.js +344 -0
  217. package/dist/views/constants-tree-provider.js.map +1 -0
  218. package/dist/views/files-tree-provider.d.ts +37 -0
  219. package/dist/views/files-tree-provider.d.ts.map +1 -0
  220. package/dist/views/files-tree-provider.js +98 -0
  221. package/dist/views/files-tree-provider.js.map +1 -0
  222. package/dist/views/index.d.ts +10 -0
  223. package/dist/views/index.d.ts.map +1 -0
  224. package/dist/views/index.js +10 -0
  225. package/dist/views/index.js.map +1 -0
  226. package/dist/views/patterns-tree-provider.d.ts +39 -0
  227. package/dist/views/patterns-tree-provider.d.ts.map +1 -0
  228. package/dist/views/patterns-tree-provider.js +139 -0
  229. package/dist/views/patterns-tree-provider.js.map +1 -0
  230. package/dist/views/violations-tree-provider.d.ts +46 -0
  231. package/dist/views/violations-tree-provider.d.ts.map +1 -0
  232. package/dist/views/violations-tree-provider.js +158 -0
  233. package/dist/views/violations-tree-provider.js.map +1 -0
  234. package/dist/webview/index.d.ts +7 -0
  235. package/dist/webview/index.d.ts.map +1 -0
  236. package/dist/webview/index.js +7 -0
  237. package/dist/webview/index.js.map +1 -0
  238. package/dist/webview/webview-manager.d.ts +57 -0
  239. package/dist/webview/webview-manager.d.ts.map +1 -0
  240. package/dist/webview/webview-manager.js +167 -0
  241. package/dist/webview/webview-manager.js.map +1 -0
  242. package/package.json +405 -0
  243. package/resources/drift-icon.png +0 -0
  244. package/resources/drift-icon.svg +5 -0
  245. package/resources/icons/error.svg +4 -0
  246. package/resources/icons/info.svg +4 -0
  247. package/resources/icons/lightbulb.svg +4 -0
  248. package/resources/icons/warning.svg +4 -0
  249. package/src/activation/activation-controller.ts +320 -0
  250. package/src/activation/activation-phases.ts +96 -0
  251. package/src/activation/index.ts +6 -0
  252. package/src/client/connection-config.ts +64 -0
  253. package/src/client/connection-manager.ts +263 -0
  254. package/src/client/index.ts +8 -0
  255. package/src/client/language-client-factory.ts +111 -0
  256. package/src/client/request-middleware.ts +117 -0
  257. package/src/commands/command-definitions.ts +243 -0
  258. package/src/commands/command-router.ts +194 -0
  259. package/src/commands/handlers/connection-handlers.ts +74 -0
  260. package/src/commands/handlers/constants-handlers.ts +99 -0
  261. package/src/commands/handlers/index.ts +10 -0
  262. package/src/commands/handlers/pattern-handlers.ts +167 -0
  263. package/src/commands/handlers/scan-handlers.ts +107 -0
  264. package/src/commands/handlers/ui-handlers.ts +88 -0
  265. package/src/commands/handlers/violation-handlers.ts +97 -0
  266. package/src/commands/index.ts +7 -0
  267. package/src/commands/middleware/connection-check-middleware.ts +46 -0
  268. package/src/commands/middleware/index.ts +7 -0
  269. package/src/commands/middleware/logging-middleware.ts +28 -0
  270. package/src/commands/middleware/telemetry-middleware.ts +46 -0
  271. package/src/config/config-manager.ts +213 -0
  272. package/src/config/defaults.ts +45 -0
  273. package/src/config/index.ts +7 -0
  274. package/src/config/validator.ts +118 -0
  275. package/src/extension.ts +57 -0
  276. package/src/infrastructure/disposable-manager.ts +87 -0
  277. package/src/infrastructure/event-bus.ts +121 -0
  278. package/src/infrastructure/index.ts +10 -0
  279. package/src/infrastructure/logger.ts +108 -0
  280. package/src/infrastructure/service-container.ts +123 -0
  281. package/src/state/index.ts +7 -0
  282. package/src/state/initial-state.ts +60 -0
  283. package/src/state/selectors.ts +126 -0
  284. package/src/state/state-manager.ts +198 -0
  285. package/src/types/config-types.ts +77 -0
  286. package/src/types/extension-types.ts +58 -0
  287. package/src/types/index.ts +12 -0
  288. package/src/types/lsp-types.ts +77 -0
  289. package/src/types/state-types.ts +92 -0
  290. package/src/types/vscode-types.ts +40 -0
  291. package/src/ui/decorations/decoration-controller.ts +252 -0
  292. package/src/ui/decorations/decoration-types.ts +129 -0
  293. package/src/ui/decorations/index.ts +7 -0
  294. package/src/ui/index.ts +7 -0
  295. package/src/ui/notifications/index.ts +5 -0
  296. package/src/ui/notifications/notification-service.ts +167 -0
  297. package/src/ui/status-bar/index.ts +6 -0
  298. package/src/ui/status-bar/status-bar-controller.ts +135 -0
  299. package/src/ui/status-bar/status-bar-modes.ts +119 -0
  300. package/src/views/base-tree-provider.ts +127 -0
  301. package/src/views/constants-tree-provider.ts +525 -0
  302. package/src/views/files-tree-provider.ts +140 -0
  303. package/src/views/index.ts +10 -0
  304. package/src/views/patterns-tree-provider.ts +179 -0
  305. package/src/views/violations-tree-provider.ts +210 -0
  306. package/src/webview/index.ts +7 -0
  307. package/src/webview/webview-manager.ts +238 -0
  308. package/tsconfig.json +22 -0
  309. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * ConfigManager - Configuration management with change detection
3
+ *
4
+ * Single responsibility: Load, validate, and watch configuration.
5
+ */
6
+
7
+ import * as fs from 'node:fs';
8
+ import * as path from 'node:path';
9
+
10
+ import * as vscode from 'vscode';
11
+
12
+ import { DEFAULT_CONFIG } from './defaults.js';
13
+ import { validateConfig } from './validator.js';
14
+
15
+ import type { Logger } from '../infrastructure/index.js';
16
+ import type { DriftConfig, ConfigChangeEvent, TeamConfig } from '../types/index.js';
17
+
18
+ /**
19
+ * Configuration manager with reactive updates
20
+ */
21
+ export class ConfigManager implements vscode.Disposable {
22
+ private config: DriftConfig;
23
+ private readonly disposables: vscode.Disposable[] = [];
24
+ private readonly changeEmitter = new vscode.EventEmitter<ConfigChangeEvent>();
25
+
26
+ readonly onConfigChange = this.changeEmitter.event;
27
+
28
+ constructor(private readonly logger: Logger) {
29
+ this.config = this.loadConfig();
30
+ this.watchConfig();
31
+ }
32
+
33
+ /**
34
+ * Get a configuration section
35
+ */
36
+ get<K extends keyof DriftConfig>(section: K): DriftConfig[K] {
37
+ return this.config[section];
38
+ }
39
+
40
+ /**
41
+ * Get the full configuration
42
+ */
43
+ getAll(): Readonly<DriftConfig> {
44
+ return this.config;
45
+ }
46
+
47
+ /**
48
+ * Update a configuration section
49
+ */
50
+ async update<K extends keyof DriftConfig>(
51
+ section: K,
52
+ value: Partial<DriftConfig[K]>,
53
+ target: vscode.ConfigurationTarget = vscode.ConfigurationTarget.Workspace
54
+ ): Promise<void> {
55
+ const vsConfig = vscode.workspace.getConfiguration('drift');
56
+ const current = vsConfig.get<DriftConfig[K]>(section);
57
+ const merged = { ...current, ...value };
58
+
59
+ // Validate before updating
60
+ const validation = validateConfig({ [section]: merged });
61
+ if (!validation.valid) {
62
+ this.logger.error('Invalid configuration:', validation.errors);
63
+ throw new Error(`Invalid configuration: ${validation.errors.join(', ')}`);
64
+ }
65
+
66
+ await vsConfig.update(section, merged, target);
67
+ }
68
+
69
+ /**
70
+ * Reset configuration to defaults
71
+ */
72
+ async reset(target: vscode.ConfigurationTarget = vscode.ConfigurationTarget.Workspace): Promise<void> {
73
+ const vsConfig = vscode.workspace.getConfiguration('drift');
74
+
75
+ for (const [section, value] of Object.entries(DEFAULT_CONFIG)) {
76
+ if (section !== 'team') {
77
+ await vsConfig.update(section, value, target);
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Reload configuration from VS Code settings
84
+ */
85
+ reload(): void {
86
+ const oldConfig = this.config;
87
+ this.config = this.loadConfig();
88
+ this.emitChanges(oldConfig, this.config);
89
+ }
90
+
91
+ /**
92
+ * Dispose resources
93
+ */
94
+ dispose(): void {
95
+ this.changeEmitter.dispose();
96
+ for (const d of this.disposables) {
97
+ d.dispose();
98
+ }
99
+ }
100
+
101
+ private loadConfig(): DriftConfig {
102
+ const vsConfig = vscode.workspace.getConfiguration('drift');
103
+
104
+ return {
105
+ server: {
106
+ path: vsConfig.get('server.path', DEFAULT_CONFIG.server.path),
107
+ args: vsConfig.get('server.args', DEFAULT_CONFIG.server.args),
108
+ trace: vsConfig.get('server.trace', DEFAULT_CONFIG.server.trace),
109
+ },
110
+ scan: {
111
+ onSave: vsConfig.get('scan.onSave', DEFAULT_CONFIG.scan.onSave),
112
+ onOpen: vsConfig.get('scan.onOpen', DEFAULT_CONFIG.scan.onOpen),
113
+ debounceMs: vsConfig.get('scan.debounceMs', DEFAULT_CONFIG.scan.debounceMs),
114
+ excludePatterns: vsConfig.get('scan.excludePatterns', DEFAULT_CONFIG.scan.excludePatterns),
115
+ },
116
+ display: {
117
+ showStatusBar: vsConfig.get('display.showStatusBar', DEFAULT_CONFIG.display.showStatusBar),
118
+ showInlineHints: vsConfig.get('display.showInlineHints', DEFAULT_CONFIG.display.showInlineHints),
119
+ showGutterIcons: vsConfig.get('display.showGutterIcons', DEFAULT_CONFIG.display.showGutterIcons),
120
+ severityFilter: vsConfig.get('display.severityFilter', DEFAULT_CONFIG.display.severityFilter),
121
+ },
122
+ ai: {
123
+ enabled: vsConfig.get('ai.enabled', DEFAULT_CONFIG.ai.enabled),
124
+ provider: vsConfig.get('ai.provider', DEFAULT_CONFIG.ai.provider),
125
+ model: vsConfig.get('ai.model', DEFAULT_CONFIG.ai.model),
126
+ },
127
+ team: this.loadTeamConfig(),
128
+ };
129
+ }
130
+
131
+ private loadTeamConfig(): TeamConfig {
132
+ const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
133
+ if (!workspaceFolder) {
134
+ return DEFAULT_CONFIG.team;
135
+ }
136
+
137
+ const configPath = path.join(workspaceFolder.uri.fsPath, '.drift', 'config.json');
138
+
139
+ try {
140
+ if (fs.existsSync(configPath)) {
141
+ const content = fs.readFileSync(configPath, 'utf-8');
142
+ const parsed = JSON.parse(content);
143
+ return {
144
+ enforceApproved: parsed.enforceApproved ?? DEFAULT_CONFIG.team.enforceApproved,
145
+ requiredCategories: parsed.requiredCategories ?? DEFAULT_CONFIG.team.requiredCategories,
146
+ customRules: parsed.customRules ?? DEFAULT_CONFIG.team.customRules,
147
+ };
148
+ }
149
+ } catch (error) {
150
+ this.logger.warn('Failed to load team config:', error);
151
+ }
152
+
153
+ return DEFAULT_CONFIG.team;
154
+ }
155
+
156
+ private watchConfig(): void {
157
+ // Watch VS Code settings
158
+ this.disposables.push(
159
+ vscode.workspace.onDidChangeConfiguration((event) => {
160
+ if (event.affectsConfiguration('drift')) {
161
+ this.reload();
162
+ }
163
+ })
164
+ );
165
+
166
+ // Watch .drift/config.json
167
+ const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
168
+ if (workspaceFolder) {
169
+ const pattern = new vscode.RelativePattern(workspaceFolder, '.drift/config.json');
170
+ const watcher = vscode.workspace.createFileSystemWatcher(pattern);
171
+
172
+ watcher.onDidChange(() => { this.reloadTeamConfig(); });
173
+ watcher.onDidCreate(() => { this.reloadTeamConfig(); });
174
+ watcher.onDidDelete(() => { this.reloadTeamConfig(); });
175
+
176
+ this.disposables.push(watcher);
177
+ }
178
+ }
179
+
180
+ private reloadTeamConfig(): void {
181
+ const oldTeam = this.config.team;
182
+ this.config = { ...this.config, team: this.loadTeamConfig() };
183
+
184
+ if (JSON.stringify(oldTeam) !== JSON.stringify(this.config.team)) {
185
+ this.changeEmitter.fire({
186
+ section: 'team',
187
+ oldValue: oldTeam,
188
+ newValue: this.config.team,
189
+ });
190
+ }
191
+ }
192
+
193
+ private emitChanges(oldConfig: DriftConfig, newConfig: DriftConfig): void {
194
+ const sections: (keyof DriftConfig)[] = ['server', 'scan', 'display', 'ai', 'team'];
195
+
196
+ for (const section of sections) {
197
+ if (JSON.stringify(oldConfig[section]) !== JSON.stringify(newConfig[section])) {
198
+ this.changeEmitter.fire({
199
+ section,
200
+ oldValue: oldConfig[section],
201
+ newValue: newConfig[section],
202
+ });
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Factory function for creating config manager
210
+ */
211
+ export function createConfigManager(logger: Logger): ConfigManager {
212
+ return new ConfigManager(logger);
213
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Default configuration values
3
+ *
4
+ * Single responsibility: Provide default config values.
5
+ */
6
+
7
+ import type { DriftConfig } from '../types/index.js';
8
+
9
+ /**
10
+ * Default configuration
11
+ */
12
+ export const DEFAULT_CONFIG: DriftConfig = {
13
+ server: {
14
+ path: '',
15
+ args: [],
16
+ trace: 'off',
17
+ },
18
+ scan: {
19
+ onSave: true,
20
+ onOpen: true,
21
+ debounceMs: 200,
22
+ excludePatterns: [
23
+ '**/node_modules/**',
24
+ '**/dist/**',
25
+ '**/.git/**',
26
+ '**/coverage/**',
27
+ ],
28
+ },
29
+ display: {
30
+ showStatusBar: true,
31
+ showInlineHints: true,
32
+ showGutterIcons: true,
33
+ severityFilter: ['error', 'warning'],
34
+ },
35
+ ai: {
36
+ enabled: false,
37
+ provider: 'none',
38
+ model: '',
39
+ },
40
+ team: {
41
+ enforceApproved: false,
42
+ requiredCategories: [],
43
+ customRules: [],
44
+ },
45
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Configuration module exports
3
+ */
4
+
5
+ export { ConfigManager, createConfigManager } from './config-manager.js';
6
+ export { DEFAULT_CONFIG } from './defaults.js';
7
+ export { validateConfig } from './validator.js';
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Configuration validator
3
+ *
4
+ * Single responsibility: Validate configuration values.
5
+ */
6
+
7
+ import type { DriftConfig, Severity, AIProvider } from '../types/index.js';
8
+
9
+ /**
10
+ * Validation result
11
+ */
12
+ export interface ValidationResult {
13
+ valid: boolean;
14
+ errors: string[];
15
+ }
16
+
17
+ /**
18
+ * Valid severity values
19
+ */
20
+ const VALID_SEVERITIES: Severity[] = ['error', 'warning', 'info', 'hint'];
21
+
22
+ /**
23
+ * Valid AI providers
24
+ */
25
+ const VALID_PROVIDERS: AIProvider[] = ['openai', 'anthropic', 'ollama', 'none'];
26
+
27
+ /**
28
+ * Valid trace levels
29
+ */
30
+ const VALID_TRACE_LEVELS = ['off', 'messages', 'verbose'] as const;
31
+
32
+ /**
33
+ * Validate configuration
34
+ */
35
+ export function validateConfig(config: Partial<DriftConfig>): ValidationResult {
36
+ const errors: string[] = [];
37
+
38
+ // Validate server config
39
+ if (config.server) {
40
+ if (config.server.trace && !VALID_TRACE_LEVELS.includes(config.server.trace)) {
41
+ errors.push(`Invalid trace level: ${config.server.trace}`);
42
+ }
43
+ }
44
+
45
+ // Validate scan config
46
+ if (config.scan) {
47
+ if (typeof config.scan.debounceMs === 'number' && config.scan.debounceMs < 0) {
48
+ errors.push('debounceMs must be non-negative');
49
+ }
50
+ if (config.scan.excludePatterns && !Array.isArray(config.scan.excludePatterns)) {
51
+ errors.push('excludePatterns must be an array');
52
+ }
53
+ }
54
+
55
+ // Validate display config
56
+ if (config.display) {
57
+ if (config.display.severityFilter) {
58
+ for (const severity of config.display.severityFilter) {
59
+ if (!VALID_SEVERITIES.includes(severity)) {
60
+ errors.push(`Invalid severity: ${severity}`);
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ // Validate AI config
67
+ if (config.ai) {
68
+ if (config.ai.provider && !VALID_PROVIDERS.includes(config.ai.provider)) {
69
+ errors.push(`Invalid AI provider: ${config.ai.provider}`);
70
+ }
71
+ if (config.ai.enabled && config.ai.provider === 'none') {
72
+ errors.push('AI enabled but provider is "none"');
73
+ }
74
+ }
75
+
76
+ return {
77
+ valid: errors.length === 0,
78
+ errors,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Validate a single config value
84
+ */
85
+ export function validateConfigValue(
86
+ section: keyof DriftConfig,
87
+ key: string,
88
+ value: unknown
89
+ ): ValidationResult {
90
+ const errors: string[] = [];
91
+
92
+ switch (section) {
93
+ case 'scan':
94
+ if (key === 'debounceMs' && (typeof value !== 'number' || value < 0)) {
95
+ errors.push('debounceMs must be a non-negative number');
96
+ }
97
+ break;
98
+ case 'display':
99
+ if (key === 'severityFilter' && Array.isArray(value)) {
100
+ for (const v of value) {
101
+ if (!VALID_SEVERITIES.includes(v as Severity)) {
102
+ errors.push(`Invalid severity: ${v}`);
103
+ }
104
+ }
105
+ }
106
+ break;
107
+ case 'ai':
108
+ if (key === 'provider' && !VALID_PROVIDERS.includes(value as AIProvider)) {
109
+ errors.push(`Invalid AI provider: ${value}`);
110
+ }
111
+ break;
112
+ }
113
+
114
+ return {
115
+ valid: errors.length === 0,
116
+ errors,
117
+ };
118
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @drift/vscode - VS Code Extension Entry Point
3
+ *
4
+ * This is the main entry point for the Drift VS Code extension.
5
+ * It delegates all work to the ActivationController for proper
6
+ * lifecycle management and phased activation.
7
+ */
8
+
9
+ import * as vscode from 'vscode';
10
+
11
+ import { createActivationController, type ActivationController } from './activation/index.js';
12
+
13
+ /**
14
+ * Extension version - read from package.json at runtime
15
+ */
16
+ export function getVersion(): string {
17
+ const extension = vscode.extensions.getExtension('driftdetect.driftdetect-vscode');
18
+ return extension?.packageJSON?.version ?? '0.0.0';
19
+ }
20
+
21
+ // For backwards compatibility
22
+ export const VERSION = '0.4.4';
23
+
24
+ /**
25
+ * Activation controller instance
26
+ */
27
+ let controller: ActivationController | null = null;
28
+
29
+ /**
30
+ * Activates the Drift extension
31
+ *
32
+ * This function is called by VS Code when the extension is activated.
33
+ * Activation is phased for optimal startup performance:
34
+ *
35
+ * 1. Immediate phase (<100ms): Infrastructure, state, config, status bar
36
+ * 2. Deferred phase: LSP connection, commands, decorations
37
+ * 3. Lazy phase: Tree views, webviews (on-demand)
38
+ */
39
+ export async function activate(context: vscode.ExtensionContext): Promise<void> {
40
+ controller = createActivationController(context);
41
+ context.subscriptions.push(controller);
42
+
43
+ await controller.activate();
44
+ }
45
+
46
+ /**
47
+ * Deactivates the Drift extension
48
+ *
49
+ * This function is called by VS Code when the extension is deactivated.
50
+ * It ensures proper cleanup of all resources.
51
+ */
52
+ export async function deactivate(): Promise<void> {
53
+ if (controller) {
54
+ await controller.deactivate();
55
+ controller = null;
56
+ }
57
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * DisposableManager - Centralized resource cleanup
3
+ *
4
+ * Single responsibility: Track and dispose VS Code disposables.
5
+ */
6
+
7
+ import * as vscode from 'vscode';
8
+
9
+ import type { DisposableManager as IDisposableManager } from '../types/index.js';
10
+
11
+ /**
12
+ * Manages disposable resources for proper cleanup
13
+ */
14
+ export class DisposableManager implements IDisposableManager, vscode.Disposable {
15
+ private readonly disposables: vscode.Disposable[] = [];
16
+ private disposed = false;
17
+
18
+ /**
19
+ * Add a disposable to be managed
20
+ */
21
+ add(disposable: vscode.Disposable): void {
22
+ if (this.disposed) {
23
+ // If already disposed, dispose the new item immediately
24
+ disposable.dispose();
25
+ return;
26
+ }
27
+ this.disposables.push(disposable);
28
+ }
29
+
30
+ /**
31
+ * Add multiple disposables
32
+ */
33
+ addAll(...disposables: vscode.Disposable[]): void {
34
+ for (const d of disposables) {
35
+ this.add(d);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Create a disposable from a cleanup function
41
+ */
42
+ addCallback(cleanup: () => void): void {
43
+ this.add({ dispose: cleanup });
44
+ }
45
+
46
+ /**
47
+ * Dispose all managed resources
48
+ */
49
+ dispose(): void {
50
+ if (this.disposed) {
51
+ return;
52
+ }
53
+
54
+ this.disposed = true;
55
+
56
+ // Dispose in reverse order (LIFO)
57
+ while (this.disposables.length > 0) {
58
+ const disposable = this.disposables.pop();
59
+ try {
60
+ disposable?.dispose();
61
+ } catch (error) {
62
+ console.error('[Drift] Error disposing resource:', error);
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Check if already disposed
69
+ */
70
+ isDisposed(): boolean {
71
+ return this.disposed;
72
+ }
73
+
74
+ /**
75
+ * Get count of managed disposables
76
+ */
77
+ get count(): number {
78
+ return this.disposables.length;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Factory function for creating disposable managers
84
+ */
85
+ export function createDisposableManager(): DisposableManager {
86
+ return new DisposableManager();
87
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * EventBus - Decoupled event communication
3
+ *
4
+ * Single responsibility: Enable pub/sub communication between components.
5
+ */
6
+
7
+ import * as vscode from 'vscode';
8
+
9
+ /**
10
+ * Event types for the extension
11
+ */
12
+ export type ExtensionEventType =
13
+ | 'connection:changed'
14
+ | 'connection:error'
15
+ | 'patterns:updated'
16
+ | 'violations:updated'
17
+ | 'scan:started'
18
+ | 'scan:completed'
19
+ | 'config:changed'
20
+ | 'workspace:changed';
21
+
22
+ /**
23
+ * Event payload types
24
+ */
25
+ export interface ExtensionEvents {
26
+ 'connection:changed': { status: string; previousStatus: string };
27
+ 'connection:error': { error: Error; recoverable: boolean };
28
+ 'patterns:updated': { total: number; categories: string[] };
29
+ 'violations:updated': { total: number; file?: string };
30
+ 'scan:started': { files: number };
31
+ 'scan:completed': { duration: number; patterns: number; violations: number };
32
+ 'config:changed': { section: string; oldValue: unknown; newValue: unknown };
33
+ 'workspace:changed': { folders: string[] };
34
+ }
35
+
36
+ /**
37
+ * Event listener type
38
+ */
39
+ type EventListener<T> = (data: T) => void;
40
+
41
+ /**
42
+ * Type-safe event bus for extension-wide communication
43
+ */
44
+ export class EventBus implements vscode.Disposable {
45
+ private readonly emitters = new Map<string, vscode.EventEmitter<unknown>>();
46
+ private readonly disposables: vscode.Disposable[] = [];
47
+
48
+ /**
49
+ * Subscribe to an event
50
+ */
51
+ on<K extends ExtensionEventType>(
52
+ event: K,
53
+ listener: EventListener<ExtensionEvents[K]>
54
+ ): vscode.Disposable {
55
+ const emitter = this.getOrCreateEmitter(event);
56
+ return emitter.event(listener as EventListener<unknown>);
57
+ }
58
+
59
+ /**
60
+ * Subscribe to an event once
61
+ */
62
+ once<K extends ExtensionEventType>(
63
+ event: K,
64
+ listener: EventListener<ExtensionEvents[K]>
65
+ ): vscode.Disposable {
66
+ const disposable = this.on(event, (data) => {
67
+ disposable.dispose();
68
+ listener(data);
69
+ });
70
+ return disposable;
71
+ }
72
+
73
+ /**
74
+ * Emit an event
75
+ */
76
+ emit<K extends ExtensionEventType>(event: K, data: ExtensionEvents[K]): void {
77
+ const emitter = this.emitters.get(event);
78
+ if (emitter) {
79
+ emitter.fire(data);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Check if event has listeners
85
+ */
86
+ hasListeners(event: ExtensionEventType): boolean {
87
+ return this.emitters.has(event);
88
+ }
89
+
90
+ /**
91
+ * Dispose all emitters
92
+ */
93
+ dispose(): void {
94
+ for (const emitter of this.emitters.values()) {
95
+ emitter.dispose();
96
+ }
97
+ this.emitters.clear();
98
+
99
+ for (const d of this.disposables) {
100
+ d.dispose();
101
+ }
102
+ this.disposables.length = 0;
103
+ }
104
+
105
+ private getOrCreateEmitter(event: string): vscode.EventEmitter<unknown> {
106
+ let emitter = this.emitters.get(event);
107
+ if (!emitter) {
108
+ emitter = new vscode.EventEmitter<unknown>();
109
+ this.emitters.set(event, emitter);
110
+ this.disposables.push(emitter);
111
+ }
112
+ return emitter;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Factory function for creating event bus
118
+ */
119
+ export function createEventBus(): EventBus {
120
+ return new EventBus();
121
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Infrastructure module exports
3
+ *
4
+ * Core utilities and services used across the extension.
5
+ */
6
+
7
+ export { Logger, createLogger } from './logger.js';
8
+ export { DisposableManager, createDisposableManager } from './disposable-manager.js';
9
+ export { ServiceContainer, ServiceKeys, createServiceContainer } from './service-container.js';
10
+ export { EventBus, createEventBus } from './event-bus.js';