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,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,7 @@
1
+ /**
2
+ * State management module exports
3
+ */
4
+
5
+ export { StateManager, createStateManager } from './state-manager.js';
6
+ export { createInitialState } from './initial-state.js';
7
+ export * from './selectors.js';
@@ -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
+ }