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,263 @@
1
+ /**
2
+ * ConnectionManager - LSP connection lifecycle management
3
+ *
4
+ * Single responsibility: Manage LSP client connection with auto-recovery.
5
+ */
6
+
7
+ import * as vscode from 'vscode';
8
+
9
+ import { CONNECTION_CONFIG } from './connection-config.js';
10
+ import { createLanguageClient } from './language-client-factory.js';
11
+
12
+ import type { EventBus } from '../infrastructure/event-bus.js';
13
+ import type { Logger } from '../infrastructure/index.js';
14
+ import type { ConnectionState, ServerConfig } from '../types/index.js';
15
+ import type { LanguageClient, StateChangeEvent } from 'vscode-languageclient/node';
16
+
17
+
18
+ /**
19
+ * Connection manager for LSP client
20
+ */
21
+ export class ConnectionManager implements vscode.Disposable {
22
+ private client: LanguageClient | null = null;
23
+ private state: ConnectionState = 'disconnected';
24
+ private restartCount = 0;
25
+ private healthCheckTimer: NodeJS.Timeout | null = null;
26
+ private readonly disposables: vscode.Disposable[] = [];
27
+
28
+ private readonly stateEmitter = new vscode.EventEmitter<ConnectionState>();
29
+ readonly onStateChange = this.stateEmitter.event;
30
+
31
+ constructor(
32
+ private readonly context: vscode.ExtensionContext,
33
+ private readonly logger: Logger,
34
+ private readonly eventBus: EventBus,
35
+ private config: ServerConfig
36
+ ) {}
37
+
38
+ /**
39
+ * Get current connection state
40
+ */
41
+ getState(): ConnectionState {
42
+ return this.state;
43
+ }
44
+
45
+ /**
46
+ * Get the language client (if connected)
47
+ */
48
+ getClient(): LanguageClient | null {
49
+ return this.state === 'connected' ? this.client : null;
50
+ }
51
+
52
+ /**
53
+ * Connect to the LSP server
54
+ */
55
+ async connect(): Promise<void> {
56
+ if (this.state === 'connected' || this.state === 'connecting') {
57
+ this.logger.debug('Already connected or connecting');
58
+ return;
59
+ }
60
+
61
+ this.setState('connecting');
62
+
63
+ try {
64
+ this.client = createLanguageClient(this.context, this.logger, this.config);
65
+
66
+ // Set up client event handlers
67
+ this.setupClientHandlers();
68
+
69
+ // Start the client
70
+ await this.client.start();
71
+
72
+ // Wait for ready state
73
+ await this.waitForReady();
74
+
75
+ this.setState('connected');
76
+ this.restartCount = 0;
77
+ this.startHealthCheck();
78
+
79
+ this.logger.info('Connected to Drift LSP server');
80
+
81
+ } catch (error) {
82
+ await this.handleConnectionError(error);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Disconnect from the LSP server
88
+ */
89
+ async disconnect(): Promise<void> {
90
+ this.stopHealthCheck();
91
+
92
+ if (this.client) {
93
+ try {
94
+ await Promise.race([
95
+ this.client.stop(),
96
+ this.timeout(CONNECTION_CONFIG.shutdownTimeout),
97
+ ]);
98
+ } catch (error) {
99
+ this.logger.warn('Error during disconnect:', error);
100
+ }
101
+ this.client = null;
102
+ }
103
+
104
+ this.setState('disconnected');
105
+ this.logger.info('Disconnected from Drift LSP server');
106
+ }
107
+
108
+ /**
109
+ * Reconnect to the LSP server
110
+ */
111
+ async reconnect(): Promise<void> {
112
+ await this.disconnect();
113
+ await this.connect();
114
+ }
115
+
116
+ /**
117
+ * Update server configuration
118
+ */
119
+ updateConfig(config: ServerConfig): void {
120
+ this.config = config;
121
+ }
122
+
123
+ /**
124
+ * Dispose resources
125
+ */
126
+ dispose(): void {
127
+ this.stopHealthCheck();
128
+ this.stateEmitter.dispose();
129
+
130
+ if (this.client) {
131
+ this.client.stop().catch(() => {});
132
+ this.client = null;
133
+ }
134
+
135
+ for (const d of this.disposables) {
136
+ d.dispose();
137
+ }
138
+ }
139
+
140
+ private setState(state: ConnectionState): void {
141
+ const previousState = this.state;
142
+ this.state = state;
143
+ this.stateEmitter.fire(state);
144
+ this.eventBus.emit('connection:changed', { status: state, previousStatus: previousState });
145
+ }
146
+
147
+ private setupClientHandlers(): void {
148
+ if (!this.client) {return;}
149
+
150
+ // Handle client errors
151
+ this.client.onDidChangeState((event: StateChangeEvent) => {
152
+ this.logger.debug(`Client state changed: ${event.oldState} -> ${event.newState}`);
153
+ });
154
+ }
155
+
156
+ private async waitForReady(): Promise<void> {
157
+ if (!this.client) {
158
+ throw new Error('Client not initialized');
159
+ }
160
+
161
+ const timeout = CONNECTION_CONFIG.initializeTimeout;
162
+ const startTime = Date.now();
163
+
164
+ while (Date.now() - startTime < timeout) {
165
+ if (this.client.isRunning()) {
166
+ return;
167
+ }
168
+ await this.delay(100);
169
+ }
170
+
171
+ throw new Error(`Server initialization timed out after ${timeout}ms`);
172
+ }
173
+
174
+ private async handleConnectionError(error: unknown): Promise<void> {
175
+ const errorMessage = error instanceof Error ? error.message : String(error);
176
+ this.logger.error('Connection error:', errorMessage);
177
+
178
+ this.setState('error');
179
+ this.eventBus.emit('connection:error', {
180
+ error: error instanceof Error ? error : new Error(errorMessage),
181
+ recoverable: this.restartCount < CONNECTION_CONFIG.maxRestarts,
182
+ });
183
+
184
+ if (this.restartCount >= CONNECTION_CONFIG.maxRestarts) {
185
+ this.setState('failed');
186
+ this.logger.error(`Max restart attempts (${CONNECTION_CONFIG.maxRestarts}) reached`);
187
+ return;
188
+ }
189
+
190
+ const delay = this.calculateBackoff();
191
+ this.restartCount++;
192
+
193
+ this.logger.info(
194
+ `Reconnecting in ${delay}ms (attempt ${this.restartCount}/${CONNECTION_CONFIG.maxRestarts})`
195
+ );
196
+
197
+ this.setState('reconnecting');
198
+ await this.delay(delay);
199
+ await this.connect();
200
+ }
201
+
202
+ private calculateBackoff(): number {
203
+ const delay = CONNECTION_CONFIG.restartDelay *
204
+ Math.pow(CONNECTION_CONFIG.backoffMultiplier, this.restartCount);
205
+ return Math.min(delay, CONNECTION_CONFIG.maxBackoffDelay);
206
+ }
207
+
208
+ private startHealthCheck(): void {
209
+ this.stopHealthCheck();
210
+
211
+ this.healthCheckTimer = setInterval(async () => {
212
+ if (!await this.isHealthy()) {
213
+ this.logger.warn('Health check failed, reconnecting...');
214
+ await this.reconnect();
215
+ }
216
+ }, CONNECTION_CONFIG.healthCheckInterval);
217
+ }
218
+
219
+ private stopHealthCheck(): void {
220
+ if (this.healthCheckTimer) {
221
+ clearInterval(this.healthCheckTimer);
222
+ this.healthCheckTimer = null;
223
+ }
224
+ }
225
+
226
+ private async isHealthy(): Promise<boolean> {
227
+ if (!this.client?.isRunning()) {
228
+ return false;
229
+ }
230
+
231
+ try {
232
+ await Promise.race([
233
+ this.client.sendRequest('drift/health'),
234
+ this.timeout(5000),
235
+ ]);
236
+ return true;
237
+ } catch {
238
+ return false;
239
+ }
240
+ }
241
+
242
+ private timeout(ms: number): Promise<never> {
243
+ return new Promise((_, reject) => {
244
+ setTimeout(() => { reject(new Error('Timeout')); }, ms);
245
+ });
246
+ }
247
+
248
+ private delay(ms: number): Promise<void> {
249
+ return new Promise((resolve) => setTimeout(resolve, ms));
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Factory function for creating connection manager
255
+ */
256
+ export function createConnectionManager(
257
+ context: vscode.ExtensionContext,
258
+ logger: Logger,
259
+ eventBus: EventBus,
260
+ config: ServerConfig
261
+ ): ConnectionManager {
262
+ return new ConnectionManager(context, logger, eventBus, config);
263
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * LSP Client module exports
3
+ */
4
+
5
+ export { ConnectionManager, createConnectionManager } from './connection-manager.js';
6
+ export { LanguageClientFactory, createLanguageClient } from './language-client-factory.js';
7
+ export { RequestMiddleware, createRequestMiddleware } from './request-middleware.js';
8
+ export { CONNECTION_CONFIG } from './connection-config.js';
@@ -0,0 +1,111 @@
1
+ /**
2
+ * LanguageClientFactory - Creates configured LSP clients
3
+ *
4
+ * Single responsibility: Create and configure LanguageClient instances.
5
+ */
6
+
7
+ import * as path from 'node:path';
8
+
9
+ import * as vscode from 'vscode';
10
+ import {
11
+ LanguageClient,
12
+ LanguageClientOptions,
13
+ ServerOptions,
14
+ TransportKind,
15
+ } from 'vscode-languageclient/node';
16
+
17
+ import { DOCUMENT_SELECTORS } from './connection-config.js';
18
+
19
+ import type { Logger } from '../infrastructure/index.js';
20
+ import type { ServerConfig } from '../types/index.js';
21
+
22
+ /**
23
+ * Factory for creating language clients
24
+ */
25
+ export class LanguageClientFactory {
26
+ constructor(
27
+ private readonly context: vscode.ExtensionContext,
28
+ private readonly logger: Logger
29
+ ) {}
30
+
31
+ /**
32
+ * Create a new language client
33
+ */
34
+ create(config: ServerConfig): LanguageClient {
35
+ const serverOptions = this.createServerOptions(config);
36
+ const clientOptions = this.createClientOptions(config);
37
+
38
+ const client = new LanguageClient(
39
+ 'drift',
40
+ 'Drift Language Server',
41
+ serverOptions,
42
+ clientOptions
43
+ );
44
+
45
+ return client;
46
+ }
47
+
48
+ private createServerOptions(config: ServerConfig): ServerOptions {
49
+ // Use custom path if provided, otherwise use bundled server
50
+ const serverModule = config.path || this.getBundledServerPath();
51
+
52
+ this.logger.debug(`Server module: ${serverModule}`);
53
+
54
+ return {
55
+ run: {
56
+ module: serverModule,
57
+ transport: TransportKind.ipc,
58
+ args: config.args,
59
+ },
60
+ debug: {
61
+ module: serverModule,
62
+ transport: TransportKind.ipc,
63
+ args: [...config.args, '--debug'],
64
+ options: {
65
+ execArgv: ['--nolazy', '--inspect=6009'],
66
+ },
67
+ },
68
+ };
69
+ }
70
+
71
+ private createClientOptions(config: ServerConfig): LanguageClientOptions {
72
+ return {
73
+ documentSelector: DOCUMENT_SELECTORS,
74
+ synchronize: {
75
+ // Watch .drift directory for config changes
76
+ fileEvents: vscode.workspace.createFileSystemWatcher('**/.drift/**'),
77
+ },
78
+ outputChannel: vscode.window.createOutputChannel('Drift LSP'),
79
+ traceOutputChannel: vscode.window.createOutputChannel('Drift LSP Trace'),
80
+ initializationOptions: {
81
+ trace: config.trace,
82
+ },
83
+ };
84
+ }
85
+
86
+ private getBundledServerPath(): string {
87
+ // Look for the LSP server in the extension's dependencies
88
+ const serverPath = path.join(
89
+ this.context.extensionPath,
90
+ 'node_modules',
91
+ 'driftdetect-lsp',
92
+ 'dist',
93
+ 'bin',
94
+ 'server.js'
95
+ );
96
+
97
+ return serverPath;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Factory function for creating language client factory
103
+ */
104
+ export function createLanguageClient(
105
+ context: vscode.ExtensionContext,
106
+ logger: Logger,
107
+ config: ServerConfig
108
+ ): LanguageClient {
109
+ const factory = new LanguageClientFactory(context, logger);
110
+ return factory.create(config);
111
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * RequestMiddleware - Request/response interceptors
3
+ *
4
+ * Single responsibility: Add retry, timeout, and logging to LSP requests.
5
+ */
6
+
7
+ import { CONNECTION_CONFIG } from './connection-config.js';
8
+
9
+ import type { Logger } from '../infrastructure/index.js';
10
+ import type { LanguageClient } from 'vscode-languageclient/node';
11
+
12
+ /**
13
+ * Request options
14
+ */
15
+ export interface RequestOptions {
16
+ timeout?: number;
17
+ retries?: number;
18
+ retryDelay?: number;
19
+ }
20
+
21
+ /**
22
+ * Default request options
23
+ */
24
+ const DEFAULT_OPTIONS: Required<RequestOptions> = {
25
+ timeout: CONNECTION_CONFIG.requestTimeout,
26
+ retries: 2,
27
+ retryDelay: 500,
28
+ };
29
+
30
+ /**
31
+ * Middleware for LSP requests with retry and timeout
32
+ */
33
+ export class RequestMiddleware {
34
+ constructor(
35
+ private readonly client: LanguageClient,
36
+ private readonly logger: Logger
37
+ ) {}
38
+
39
+ /**
40
+ * Send a request with retry and timeout
41
+ */
42
+ async request<T>(
43
+ method: string,
44
+ params?: unknown,
45
+ options: RequestOptions = {}
46
+ ): Promise<T> {
47
+ const opts = { ...DEFAULT_OPTIONS, ...options };
48
+ let lastError: Error | undefined;
49
+
50
+ for (let attempt = 0; attempt <= opts.retries; attempt++) {
51
+ try {
52
+ const result = await this.sendWithTimeout<T>(method, params, opts.timeout);
53
+ return result;
54
+ } catch (error) {
55
+ lastError = error instanceof Error ? error : new Error(String(error));
56
+
57
+ if (attempt < opts.retries) {
58
+ this.logger.warn(
59
+ `Request ${method} failed (attempt ${attempt + 1}/${opts.retries + 1}): ${lastError.message}`
60
+ );
61
+ await this.delay(opts.retryDelay * (attempt + 1));
62
+ }
63
+ }
64
+ }
65
+
66
+ this.logger.error(`Request ${method} failed after ${opts.retries + 1} attempts`);
67
+ throw lastError;
68
+ }
69
+
70
+ /**
71
+ * Send a notification (fire and forget)
72
+ */
73
+ notify(method: string, params?: unknown): void {
74
+ try {
75
+ this.client.sendNotification(method, params);
76
+ } catch (error) {
77
+ this.logger.error(`Notification ${method} failed:`, error);
78
+ }
79
+ }
80
+
81
+ private async sendWithTimeout<T>(
82
+ method: string,
83
+ params: unknown,
84
+ timeout: number
85
+ ): Promise<T> {
86
+ return new Promise<T>((resolve, reject) => {
87
+ const timer = setTimeout(() => {
88
+ reject(new Error(`Request ${method} timed out after ${timeout}ms`));
89
+ }, timeout);
90
+
91
+ this.client
92
+ .sendRequest<T>(method, params)
93
+ .then((result) => {
94
+ clearTimeout(timer);
95
+ resolve(result);
96
+ })
97
+ .catch((error) => {
98
+ clearTimeout(timer);
99
+ reject(error);
100
+ });
101
+ });
102
+ }
103
+
104
+ private delay(ms: number): Promise<void> {
105
+ return new Promise((resolve) => setTimeout(resolve, ms));
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Factory function for creating request middleware
111
+ */
112
+ export function createRequestMiddleware(
113
+ client: LanguageClient,
114
+ logger: Logger
115
+ ): RequestMiddleware {
116
+ return new RequestMiddleware(client, logger);
117
+ }