@superdangerous/app-framework 4.9.0

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 (239) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +652 -0
  3. package/dist/api/logsRouter.d.ts +20 -0
  4. package/dist/api/logsRouter.d.ts.map +1 -0
  5. package/dist/api/logsRouter.js +515 -0
  6. package/dist/api/logsRouter.js.map +1 -0
  7. package/dist/cli/dev-server.d.ts +7 -0
  8. package/dist/cli/dev-server.d.ts.map +1 -0
  9. package/dist/cli/dev-server.js +640 -0
  10. package/dist/cli/dev-server.js.map +1 -0
  11. package/dist/cli/index.d.ts +7 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +26 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/core/StandardServer.d.ts +129 -0
  16. package/dist/core/StandardServer.d.ts.map +1 -0
  17. package/dist/core/StandardServer.js +453 -0
  18. package/dist/core/StandardServer.js.map +1 -0
  19. package/dist/core/apiResponse.d.ts +69 -0
  20. package/dist/core/apiResponse.d.ts.map +1 -0
  21. package/dist/core/apiResponse.js +127 -0
  22. package/dist/core/apiResponse.js.map +1 -0
  23. package/dist/core/healthCheck.d.ts +160 -0
  24. package/dist/core/healthCheck.d.ts.map +1 -0
  25. package/dist/core/healthCheck.js +398 -0
  26. package/dist/core/healthCheck.js.map +1 -0
  27. package/dist/core/index.d.ts +40 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +40 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/logger.d.ts +117 -0
  32. package/dist/core/logger.d.ts.map +1 -0
  33. package/dist/core/logger.js +826 -0
  34. package/dist/core/logger.js.map +1 -0
  35. package/dist/core/portUtils.d.ts +71 -0
  36. package/dist/core/portUtils.d.ts.map +1 -0
  37. package/dist/core/portUtils.js +240 -0
  38. package/dist/core/portUtils.js.map +1 -0
  39. package/dist/core/storageService.d.ts +119 -0
  40. package/dist/core/storageService.d.ts.map +1 -0
  41. package/dist/core/storageService.js +405 -0
  42. package/dist/core/storageService.js.map +1 -0
  43. package/dist/desktop/bundler.d.ts +40 -0
  44. package/dist/desktop/bundler.d.ts.map +1 -0
  45. package/dist/desktop/bundler.js +176 -0
  46. package/dist/desktop/bundler.js.map +1 -0
  47. package/dist/desktop/index.d.ts +25 -0
  48. package/dist/desktop/index.d.ts.map +1 -0
  49. package/dist/desktop/index.js +15 -0
  50. package/dist/desktop/index.js.map +1 -0
  51. package/dist/desktop/native-modules.d.ts +66 -0
  52. package/dist/desktop/native-modules.d.ts.map +1 -0
  53. package/dist/desktop/native-modules.js +200 -0
  54. package/dist/desktop/native-modules.js.map +1 -0
  55. package/dist/index.d.ts +29 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +39 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/logging/LogCategories.d.ts +87 -0
  60. package/dist/logging/LogCategories.d.ts.map +1 -0
  61. package/dist/logging/LogCategories.js +205 -0
  62. package/dist/logging/LogCategories.js.map +1 -0
  63. package/dist/middleware/aiErrorHandler.d.ts +31 -0
  64. package/dist/middleware/aiErrorHandler.d.ts.map +1 -0
  65. package/dist/middleware/aiErrorHandler.js +181 -0
  66. package/dist/middleware/aiErrorHandler.js.map +1 -0
  67. package/dist/middleware/auth.d.ts +101 -0
  68. package/dist/middleware/auth.d.ts.map +1 -0
  69. package/dist/middleware/auth.js +230 -0
  70. package/dist/middleware/auth.js.map +1 -0
  71. package/dist/middleware/cors.d.ts +56 -0
  72. package/dist/middleware/cors.d.ts.map +1 -0
  73. package/dist/middleware/cors.js +123 -0
  74. package/dist/middleware/cors.js.map +1 -0
  75. package/dist/middleware/errorHandler.d.ts +13 -0
  76. package/dist/middleware/errorHandler.d.ts.map +1 -0
  77. package/dist/middleware/errorHandler.js +85 -0
  78. package/dist/middleware/errorHandler.js.map +1 -0
  79. package/dist/middleware/fileUpload.d.ts +62 -0
  80. package/dist/middleware/fileUpload.d.ts.map +1 -0
  81. package/dist/middleware/fileUpload.js +175 -0
  82. package/dist/middleware/fileUpload.js.map +1 -0
  83. package/dist/middleware/health.d.ts +48 -0
  84. package/dist/middleware/health.d.ts.map +1 -0
  85. package/dist/middleware/health.js +143 -0
  86. package/dist/middleware/health.js.map +1 -0
  87. package/dist/middleware/index.d.ts +20 -0
  88. package/dist/middleware/index.d.ts.map +1 -0
  89. package/dist/middleware/index.js +18 -0
  90. package/dist/middleware/index.js.map +1 -0
  91. package/dist/middleware/openapi.d.ts +64 -0
  92. package/dist/middleware/openapi.d.ts.map +1 -0
  93. package/dist/middleware/openapi.js +258 -0
  94. package/dist/middleware/openapi.js.map +1 -0
  95. package/dist/middleware/requestLogging.d.ts +22 -0
  96. package/dist/middleware/requestLogging.d.ts.map +1 -0
  97. package/dist/middleware/requestLogging.js +61 -0
  98. package/dist/middleware/requestLogging.js.map +1 -0
  99. package/dist/middleware/session.d.ts +84 -0
  100. package/dist/middleware/session.d.ts.map +1 -0
  101. package/dist/middleware/session.js +189 -0
  102. package/dist/middleware/session.js.map +1 -0
  103. package/dist/middleware/validation.d.ts +1337 -0
  104. package/dist/middleware/validation.d.ts.map +1 -0
  105. package/dist/middleware/validation.js +483 -0
  106. package/dist/middleware/validation.js.map +1 -0
  107. package/dist/services/aiService.d.ts +180 -0
  108. package/dist/services/aiService.d.ts.map +1 -0
  109. package/dist/services/aiService.js +547 -0
  110. package/dist/services/aiService.js.map +1 -0
  111. package/dist/services/conversationStorage.d.ts +38 -0
  112. package/dist/services/conversationStorage.d.ts.map +1 -0
  113. package/dist/services/conversationStorage.js +158 -0
  114. package/dist/services/conversationStorage.js.map +1 -0
  115. package/dist/services/crossPlatformBuffer.d.ts +84 -0
  116. package/dist/services/crossPlatformBuffer.d.ts.map +1 -0
  117. package/dist/services/crossPlatformBuffer.js +246 -0
  118. package/dist/services/crossPlatformBuffer.js.map +1 -0
  119. package/dist/services/index.d.ts +17 -0
  120. package/dist/services/index.d.ts.map +1 -0
  121. package/dist/services/index.js +18 -0
  122. package/dist/services/index.js.map +1 -0
  123. package/dist/services/networkService.d.ts +81 -0
  124. package/dist/services/networkService.d.ts.map +1 -0
  125. package/dist/services/networkService.js +268 -0
  126. package/dist/services/networkService.js.map +1 -0
  127. package/dist/services/queueService.d.ts +112 -0
  128. package/dist/services/queueService.d.ts.map +1 -0
  129. package/dist/services/queueService.js +338 -0
  130. package/dist/services/queueService.js.map +1 -0
  131. package/dist/services/settingsService.d.ts +135 -0
  132. package/dist/services/settingsService.d.ts.map +1 -0
  133. package/dist/services/settingsService.js +425 -0
  134. package/dist/services/settingsService.js.map +1 -0
  135. package/dist/services/systemMonitor.d.ts +208 -0
  136. package/dist/services/systemMonitor.d.ts.map +1 -0
  137. package/dist/services/systemMonitor.js +693 -0
  138. package/dist/services/systemMonitor.js.map +1 -0
  139. package/dist/services/updateService.d.ts +78 -0
  140. package/dist/services/updateService.d.ts.map +1 -0
  141. package/dist/services/updateService.js +252 -0
  142. package/dist/services/updateService.js.map +1 -0
  143. package/dist/services/websocketEvents.d.ts +372 -0
  144. package/dist/services/websocketEvents.d.ts.map +1 -0
  145. package/dist/services/websocketEvents.js +338 -0
  146. package/dist/services/websocketEvents.js.map +1 -0
  147. package/dist/services/websocketServer.d.ts +80 -0
  148. package/dist/services/websocketServer.d.ts.map +1 -0
  149. package/dist/services/websocketServer.js +299 -0
  150. package/dist/services/websocketServer.js.map +1 -0
  151. package/dist/settings/SettingsSchema.d.ts +151 -0
  152. package/dist/settings/SettingsSchema.d.ts.map +1 -0
  153. package/dist/settings/SettingsSchema.js +424 -0
  154. package/dist/settings/SettingsSchema.js.map +1 -0
  155. package/dist/testing/TestServer.d.ts +69 -0
  156. package/dist/testing/TestServer.d.ts.map +1 -0
  157. package/dist/testing/TestServer.js +250 -0
  158. package/dist/testing/TestServer.js.map +1 -0
  159. package/dist/types/index.d.ts +137 -0
  160. package/dist/types/index.d.ts.map +1 -0
  161. package/dist/types/index.js +5 -0
  162. package/dist/types/index.js.map +1 -0
  163. package/dist/utils/appPaths.d.ts +74 -0
  164. package/dist/utils/appPaths.d.ts.map +1 -0
  165. package/dist/utils/appPaths.js +162 -0
  166. package/dist/utils/appPaths.js.map +1 -0
  167. package/dist/utils/fs-utils.d.ts +50 -0
  168. package/dist/utils/fs-utils.d.ts.map +1 -0
  169. package/dist/utils/fs-utils.js +114 -0
  170. package/dist/utils/fs-utils.js.map +1 -0
  171. package/dist/utils/index.d.ts +12 -0
  172. package/dist/utils/index.d.ts.map +1 -0
  173. package/dist/utils/index.js +10 -0
  174. package/dist/utils/index.js.map +1 -0
  175. package/dist/utils/standardConfig.d.ts +61 -0
  176. package/dist/utils/standardConfig.d.ts.map +1 -0
  177. package/dist/utils/standardConfig.js +109 -0
  178. package/dist/utils/standardConfig.js.map +1 -0
  179. package/dist/utils/startupBanner.d.ts +34 -0
  180. package/dist/utils/startupBanner.d.ts.map +1 -0
  181. package/dist/utils/startupBanner.js +169 -0
  182. package/dist/utils/startupBanner.js.map +1 -0
  183. package/dist/utils/startupLogger.d.ts +45 -0
  184. package/dist/utils/startupLogger.d.ts.map +1 -0
  185. package/dist/utils/startupLogger.js +200 -0
  186. package/dist/utils/startupLogger.js.map +1 -0
  187. package/package.json +151 -0
  188. package/src/api/logsRouter.ts +600 -0
  189. package/src/cli/dev-server.ts +803 -0
  190. package/src/cli/index.ts +31 -0
  191. package/src/core/StandardServer.ts +587 -0
  192. package/src/core/apiResponse.ts +202 -0
  193. package/src/core/healthCheck.ts +565 -0
  194. package/src/core/index.ts +80 -0
  195. package/src/core/logger.ts +1092 -0
  196. package/src/core/portUtils.ts +319 -0
  197. package/src/core/storageService.ts +595 -0
  198. package/src/desktop/bundler.ts +271 -0
  199. package/src/desktop/index.ts +18 -0
  200. package/src/desktop/native-modules.ts +289 -0
  201. package/src/index.ts +142 -0
  202. package/src/logging/LogCategories.ts +302 -0
  203. package/src/middleware/aiErrorHandler.ts +278 -0
  204. package/src/middleware/auth.ts +329 -0
  205. package/src/middleware/cors.ts +187 -0
  206. package/src/middleware/errorHandler.ts +103 -0
  207. package/src/middleware/fileUpload.ts +252 -0
  208. package/src/middleware/health.ts +206 -0
  209. package/src/middleware/index.ts +71 -0
  210. package/src/middleware/openapi.ts +305 -0
  211. package/src/middleware/requestLogging.ts +92 -0
  212. package/src/middleware/session.ts +238 -0
  213. package/src/middleware/validation.ts +603 -0
  214. package/src/services/aiService.ts +789 -0
  215. package/src/services/conversationStorage.ts +232 -0
  216. package/src/services/crossPlatformBuffer.ts +341 -0
  217. package/src/services/index.ts +47 -0
  218. package/src/services/networkService.ts +351 -0
  219. package/src/services/queueService.ts +446 -0
  220. package/src/services/settingsService.ts +549 -0
  221. package/src/services/systemMonitor.ts +936 -0
  222. package/src/services/updateService.ts +334 -0
  223. package/src/services/websocketEvents.ts +409 -0
  224. package/src/services/websocketServer.ts +394 -0
  225. package/src/settings/SettingsSchema.ts +664 -0
  226. package/src/testing/TestServer.ts +312 -0
  227. package/src/types/index.ts +154 -0
  228. package/src/utils/appPaths.ts +196 -0
  229. package/src/utils/fs-utils.ts +130 -0
  230. package/src/utils/index.ts +15 -0
  231. package/src/utils/standardConfig.ts +178 -0
  232. package/src/utils/startupBanner.ts +287 -0
  233. package/src/utils/startupLogger.ts +268 -0
  234. package/ui/dist/index.d.mts +1221 -0
  235. package/ui/dist/index.d.ts +1221 -0
  236. package/ui/dist/index.js +73 -0
  237. package/ui/dist/index.js.map +1 -0
  238. package/ui/dist/index.mjs +73 -0
  239. package/ui/dist/index.mjs.map +1 -0
@@ -0,0 +1,394 @@
1
+ /**
2
+ * WebSocket Server Implementation
3
+ * Provides real-time updates for simulator data and events
4
+ */
5
+
6
+ import { Server, Socket } from "socket.io";
7
+ import { Server as HTTPServer } from "http";
8
+ import { createLogger } from "../core/index.js";
9
+ import {
10
+ WebSocketMessage,
11
+ SimulatorUpdateMessage,
12
+ TemplateUpdateMessage,
13
+ DataUpdateMessage,
14
+ Simulator,
15
+ Template,
16
+ } from "../types/index.js";
17
+
18
+ let logger: any; // Will be initialized when needed
19
+
20
+ interface ClientInfo {
21
+ id: string;
22
+ connectedAt: Date;
23
+ subscriptions: Set<string>;
24
+ }
25
+
26
+ interface BroadcastData {
27
+ simulatorId?: string;
28
+ templateId?: string;
29
+ [key: string]: any;
30
+ }
31
+
32
+ class WebSocketServer {
33
+ private io: Server | null = null;
34
+ private httpServer: HTTPServer;
35
+ private clients: Map<string, ClientInfo>;
36
+ private simulatorSubscriptions: Map<string, Set<string>>; // simulatorId -> Set of socket IDs
37
+ private broadcastHook?: (event: string, data: any) => void;
38
+
39
+ constructor(httpServer: HTTPServer) {
40
+ // Initialize logger if not already done
41
+ if (!logger) {
42
+ logger = createLogger("WebSocket");
43
+ }
44
+
45
+ this.httpServer = httpServer;
46
+ this.clients = new Map();
47
+ this.simulatorSubscriptions = new Map();
48
+ }
49
+
50
+ initialize(opts?: {
51
+ broadcastHook?: (event: string, data: any) => void;
52
+ }): void {
53
+ if (this.io) {
54
+ if (opts?.broadcastHook) {
55
+ this.broadcastHook = opts.broadcastHook;
56
+ logger.debug("WebSocket broadcast hook updated");
57
+ }
58
+ return;
59
+ }
60
+
61
+ this.io = new Server(this.httpServer, {
62
+ cors: {
63
+ origin: "*", // Allow all origins for local development
64
+ methods: ["GET", "POST"],
65
+ },
66
+ transports: ["websocket", "polling"],
67
+ });
68
+
69
+ if (opts?.broadcastHook) {
70
+ this.broadcastHook = opts.broadcastHook;
71
+ }
72
+
73
+ this.setupEventHandlers();
74
+ logger.info("WebSocket server initialized");
75
+ }
76
+
77
+ /**
78
+ * Get the underlying Socket.IO server instance
79
+ */
80
+ getIO(): Server | null {
81
+ return this.io;
82
+ }
83
+
84
+ private setupEventHandlers(): void {
85
+ if (!this.io) return;
86
+
87
+ this.io.on("connection", (socket: Socket) => {
88
+ // Only log in debug mode to avoid spam
89
+ if (process.env.LOG_LEVEL?.toLowerCase() === "debug") {
90
+ logger.debug(`Client connected: ${socket.id}`);
91
+ }
92
+ this.clients.set(socket.id, {
93
+ id: socket.id,
94
+ connectedAt: new Date(),
95
+ subscriptions: new Set(),
96
+ });
97
+
98
+ // Send initial connection confirmation
99
+ socket.emit("connected", {
100
+ id: socket.id,
101
+ timestamp: Date.now(),
102
+ });
103
+
104
+ // Handle simulator subscriptions
105
+ socket.on("subscribe:simulator", (simulatorId: string) => {
106
+ this.subscribeToSimulator(socket, simulatorId);
107
+ });
108
+
109
+ socket.on("unsubscribe:simulator", (simulatorId: string) => {
110
+ this.unsubscribeFromSimulator(socket, simulatorId);
111
+ });
112
+
113
+ // Handle disconnection
114
+ socket.on("disconnect", () => {
115
+ // Only log in debug mode to avoid spam
116
+ if (process.env.LOG_LEVEL?.toLowerCase() === "debug") {
117
+ logger.debug(`Client disconnected: ${socket.id}`);
118
+ }
119
+ this.handleDisconnect(socket);
120
+ });
121
+
122
+ // Handle errors
123
+ socket.on("error", (_error: Error) => {
124
+ logger.error(`Socket error for ${socket.id}:`, _error);
125
+ });
126
+ });
127
+ }
128
+
129
+ private subscribeToSimulator(socket: Socket, simulatorId: string): void {
130
+ const client = this.clients.get(socket.id);
131
+ if (!client) return;
132
+
133
+ // Add to client's subscriptions
134
+ client.subscriptions.add(simulatorId);
135
+
136
+ // Add to simulator's subscriber list
137
+ if (!this.simulatorSubscriptions.has(simulatorId)) {
138
+ this.simulatorSubscriptions.set(simulatorId, new Set());
139
+ }
140
+ const subscribers = this.simulatorSubscriptions.get(simulatorId);
141
+ if (subscribers) {
142
+ subscribers.add(socket.id);
143
+ }
144
+
145
+ // Join socket.io room for efficient broadcasting
146
+ socket.join(`simulator:${simulatorId}`);
147
+
148
+ logger.debug(`Client ${socket.id} subscribed to simulator ${simulatorId}`);
149
+ }
150
+
151
+ private unsubscribeFromSimulator(socket: Socket, simulatorId: string): void {
152
+ const client = this.clients.get(socket.id);
153
+ if (!client) return;
154
+
155
+ // Remove from client's subscriptions
156
+ client.subscriptions.delete(simulatorId);
157
+
158
+ // Remove from simulator's subscriber list
159
+ const subscribers = this.simulatorSubscriptions.get(simulatorId);
160
+ if (subscribers) {
161
+ subscribers.delete(socket.id);
162
+ if (subscribers.size === 0) {
163
+ this.simulatorSubscriptions.delete(simulatorId);
164
+ }
165
+ }
166
+
167
+ // Leave socket.io room
168
+ socket.leave(`simulator:${simulatorId}`);
169
+
170
+ logger.debug(
171
+ `Client ${socket.id} unsubscribed from simulator ${simulatorId}`,
172
+ );
173
+ }
174
+
175
+ private handleDisconnect(socket: Socket): void {
176
+ const client = this.clients.get(socket.id);
177
+ if (!client) return;
178
+
179
+ // Clean up all subscriptions
180
+ client.subscriptions.forEach((simulatorId) => {
181
+ const subscribers = this.simulatorSubscriptions.get(simulatorId);
182
+ if (subscribers) {
183
+ subscribers.delete(socket.id);
184
+ if (subscribers.size === 0) {
185
+ this.simulatorSubscriptions.delete(simulatorId);
186
+ }
187
+ }
188
+ });
189
+
190
+ // Remove client
191
+ this.clients.delete(socket.id);
192
+ }
193
+
194
+ /**
195
+ * Broadcast an event to all connected clients
196
+ */
197
+ broadcast(event: string, data: BroadcastData): void {
198
+ if (!this.io) return;
199
+
200
+ // Allow tests/consumers to observe outbound broadcasts
201
+ if (this.broadcastHook) {
202
+ try {
203
+ this.broadcastHook(event, data);
204
+ } catch (_err) {
205
+ // ignore hook errors
206
+ }
207
+ }
208
+
209
+ // Create typed message based on event
210
+ let message: WebSocketMessage;
211
+ // Timestamp removed - not needed in simplified message structure
212
+
213
+ switch (event) {
214
+ case "simulator:started":
215
+ case "simulator:stopped":
216
+ case "simulator:update":
217
+ message = {
218
+ type: event as
219
+ | "simulator:started"
220
+ | "simulator:stopped"
221
+ | "simulator:data",
222
+ data: data as Simulator,
223
+ } as SimulatorUpdateMessage;
224
+ break;
225
+
226
+ case "template:created":
227
+ case "template:updated":
228
+ case "template:deleted":
229
+ message = {
230
+ type: event as
231
+ | "template:created"
232
+ | "template:updated"
233
+ | "template:deleted",
234
+ data: data as Template,
235
+ } as TemplateUpdateMessage;
236
+ break;
237
+
238
+ case "simulator:data":
239
+ message = {
240
+ type: "data:update",
241
+ data: {
242
+ simulatorId: data.simulatorId!,
243
+ values: data.values || { [data.address]: data.value },
244
+ },
245
+ } as DataUpdateMessage;
246
+ break;
247
+
248
+ default:
249
+ message = {
250
+ type: event,
251
+ data: data,
252
+ } as WebSocketMessage;
253
+ }
254
+
255
+ // Broadcast to all clients
256
+ this.io.emit(event, message.data);
257
+
258
+ // If simulator-specific, also send to room
259
+ if (data.simulatorId) {
260
+ this.io.to(`simulator:${data.simulatorId}`).emit(event, message.data);
261
+ }
262
+
263
+ logger.debug(`Broadcast ${event} to ${this.clients.size} clients`);
264
+ }
265
+
266
+ /**
267
+ * Send event to specific simulator subscribers
268
+ */
269
+ broadcastToSimulator(simulatorId: string, event: string, data: any): void {
270
+ if (!this.io) return;
271
+
272
+ const message: DataUpdateMessage = {
273
+ type: "data:update",
274
+ data: {
275
+ simulatorId,
276
+ values: data,
277
+ },
278
+ };
279
+
280
+ this.io.to(`simulator:${simulatorId}`).emit(event, message.data);
281
+
282
+ const subscriberCount =
283
+ this.simulatorSubscriptions.get(simulatorId)?.size || 0;
284
+ logger.debug(
285
+ `Broadcast ${event} to ${subscriberCount} subscribers of simulator ${simulatorId}`,
286
+ );
287
+ }
288
+
289
+ /**
290
+ * Get statistics about connected clients
291
+ */
292
+ getStats(): {
293
+ totalClients: number;
294
+ totalSubscriptions: number;
295
+ simulatorSubscriptions: Record<string, number>;
296
+ } {
297
+ const stats = {
298
+ totalClients: this.clients.size,
299
+ totalSubscriptions: 0,
300
+ simulatorSubscriptions: {} as Record<string, number>,
301
+ };
302
+
303
+ // Count total subscriptions
304
+ this.clients.forEach((client) => {
305
+ stats.totalSubscriptions += client.subscriptions.size;
306
+ });
307
+
308
+ // Count per-simulator subscriptions
309
+ this.simulatorSubscriptions.forEach((subscribers, simulatorId) => {
310
+ stats.simulatorSubscriptions[simulatorId] = subscribers.size;
311
+ });
312
+
313
+ return stats;
314
+ }
315
+
316
+ /**
317
+ * Get list of connected clients
318
+ */
319
+ getClients(): ClientInfo[] {
320
+ return Array.from(this.clients.values());
321
+ }
322
+
323
+ /**
324
+ * Check if server is initialized
325
+ */
326
+ isInitialized(): boolean {
327
+ return this.io !== null;
328
+ }
329
+
330
+ /**
331
+ * Shutdown the WebSocket server
332
+ */
333
+ async shutdown(): Promise<void> {
334
+ if (this.io) {
335
+ // Disconnect all clients
336
+ this.io.disconnectSockets();
337
+
338
+ // Close the server
339
+ await new Promise<void>((resolve) => {
340
+ this.io!.close(() => {
341
+ logger.info("WebSocket server shut down");
342
+ resolve();
343
+ });
344
+ });
345
+
346
+ this.io = null;
347
+ }
348
+
349
+ // Clear all data
350
+ this.clients.clear();
351
+ this.simulatorSubscriptions.clear();
352
+ }
353
+ }
354
+
355
+ // Singleton instance
356
+ let wsServer: WebSocketServer | null = null;
357
+
358
+ /**
359
+ * Create WebSocket server instance
360
+ */
361
+ export function createWebSocketServer(
362
+ httpServer: HTTPServer,
363
+ opts?: {
364
+ reset?: boolean;
365
+ broadcastHook?: (event: string, data: any) => void;
366
+ },
367
+ ): WebSocketServer {
368
+ // Initialize logger if not already done
369
+ if (!logger) {
370
+ logger = createLogger("WebSocket");
371
+ }
372
+
373
+ if (!wsServer || opts?.reset) {
374
+ wsServer = new WebSocketServer(httpServer);
375
+ wsServer.initialize({ broadcastHook: opts?.broadcastHook });
376
+ } else if (opts?.broadcastHook) {
377
+ // Reconfigure hook without rebuilding the IO server
378
+ wsServer.initialize({ broadcastHook: opts.broadcastHook });
379
+ }
380
+ return wsServer;
381
+ }
382
+
383
+ /**
384
+ * Get existing WebSocket server instance
385
+ */
386
+ export function getWebSocketServer(): WebSocketServer | null {
387
+ return wsServer;
388
+ }
389
+
390
+ /**
391
+ * Export the class for type usage
392
+ */
393
+ export { WebSocketServer };
394
+ export type { ClientInfo, BroadcastData };