@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,312 @@
1
+ /**
2
+ * Test Server Utility
3
+ * Provides standardized test server management for all SuperDangerous applications
4
+ */
5
+
6
+ import { spawn, ChildProcess } from "child_process";
7
+ import { execSync } from "child_process";
8
+ import { createLogger } from "../core/index.js";
9
+
10
+ let logger: any; // Will be initialized when needed
11
+
12
+ function ensureLogger() {
13
+ if (!logger) {
14
+ logger = createLogger("TestServer");
15
+ }
16
+ return logger;
17
+ }
18
+
19
+ export interface TestServerConfig {
20
+ /** Path to the application entry point */
21
+ entryPoint: string;
22
+ /** Port to run the server on */
23
+ port: number;
24
+ /** API base URL */
25
+ apiBase?: string;
26
+ /** Environment variables */
27
+ env?: Record<string, string>;
28
+ /** Timeout for server startup (ms) */
29
+ startupTimeout?: number;
30
+ /** Health check endpoint */
31
+ healthEndpoint?: string;
32
+ /** Whether to suppress server output */
33
+ silent?: boolean;
34
+ }
35
+
36
+ export class TestServer {
37
+ private process: ChildProcess | null = null;
38
+ private config: Required<TestServerConfig>;
39
+ private startPromise: Promise<void> | null = null;
40
+ public port: number;
41
+
42
+ constructor(config: TestServerConfig) {
43
+ this.port = config.port;
44
+ this.config = {
45
+ apiBase: `http://localhost:${config.port}`,
46
+ env: {},
47
+ startupTimeout: 30000,
48
+ healthEndpoint: "/api/health",
49
+ silent: true,
50
+ ...config,
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Clean up any existing processes on the port
56
+ */
57
+ private cleanupPort(): void {
58
+ try {
59
+ if (process.platform === "win32") {
60
+ // Windows: Find and kill process using the port
61
+ execSync(
62
+ `netstat -ano | findstr :${this.config.port} | findstr LISTENING`,
63
+ { stdio: "ignore" },
64
+ );
65
+ const result = execSync(
66
+ `netstat -ano | findstr :${this.config.port} | findstr LISTENING`,
67
+ ).toString();
68
+ const lines = result.split("\n");
69
+ for (const line of lines) {
70
+ const parts = line.trim().split(/\s+/);
71
+ const pid = parts[parts.length - 1];
72
+ if (pid && pid !== "0") {
73
+ execSync(`taskkill /F /PID ${pid}`, { stdio: "ignore" });
74
+ }
75
+ }
76
+ } else {
77
+ // Unix-like: Use lsof to find and kill process
78
+ execSync(
79
+ `lsof -ti:${this.config.port} | xargs kill -9 2>/dev/null || true`,
80
+ { stdio: "ignore" },
81
+ );
82
+ }
83
+ } catch (_error) {
84
+ // Port might already be free, ignore errors
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Wait for the server to be ready
90
+ */
91
+ private async waitForServer(): Promise<void> {
92
+ const startTime = Date.now();
93
+ const { apiBase, healthEndpoint, startupTimeout } = this.config;
94
+ const healthUrl = `${apiBase}${healthEndpoint}`;
95
+
96
+ if (!this.config.silent) {
97
+ console.log(`Waiting for server at ${healthUrl}...`);
98
+ }
99
+
100
+ while (Date.now() - startTime < startupTimeout) {
101
+ try {
102
+ const controller = new AbortController();
103
+ const timeoutId = setTimeout(() => controller.abort(), 1000);
104
+
105
+ const response = await fetch(healthUrl, {
106
+ signal: controller.signal,
107
+ });
108
+
109
+ clearTimeout(timeoutId);
110
+
111
+ if (response.ok) {
112
+ if (!this.config.silent) {
113
+ console.log(`Server is ready at ${healthUrl}`);
114
+ }
115
+ return; // Server is ready
116
+ }
117
+ } catch (_error) {
118
+ // Server not ready yet
119
+ }
120
+
121
+ // Check if process has exited
122
+ if (this.process && this.process.exitCode !== null) {
123
+ throw new Error(
124
+ `Server process exited with code ${this.process.exitCode}`,
125
+ );
126
+ }
127
+
128
+ await new Promise((resolve) => setTimeout(resolve, 500));
129
+ }
130
+
131
+ throw new Error(`Server failed to start within ${startupTimeout}ms`);
132
+ }
133
+
134
+ /**
135
+ * Start the test server
136
+ */
137
+ async start(): Promise<void> {
138
+ // Return existing promise if already starting
139
+ if (this.startPromise) {
140
+ return this.startPromise;
141
+ }
142
+
143
+ // Already running
144
+ if (this.process) {
145
+ return;
146
+ }
147
+
148
+ this.startPromise = this._start();
149
+ return this.startPromise;
150
+ }
151
+
152
+ private async _start(): Promise<void> {
153
+ // Clean up any existing processes
154
+ this.cleanupPort();
155
+
156
+ // Determine command based on entry point extension
157
+ const { entryPoint } = this.config;
158
+ const isTypeScript = entryPoint.endsWith(".ts");
159
+ const command = isTypeScript ? "npx" : "node";
160
+ const args = isTypeScript ? ["tsx", entryPoint] : [entryPoint];
161
+
162
+ // Start the server process
163
+ this.process = spawn(command, args, {
164
+ env: {
165
+ ...process.env,
166
+ ...this.config.env,
167
+ NODE_ENV: "test",
168
+ PORT: String(this.config.port),
169
+ API_PORT: String(this.config.port),
170
+ SILENT_STARTUP: "1",
171
+ },
172
+ stdio: this.config.silent ? ["ignore", "pipe", "pipe"] : "inherit",
173
+ });
174
+
175
+ // Handle process output if not silent
176
+ if (this.config.silent && this.process.stdout && this.process.stderr) {
177
+ this.process.stdout.on("data", (data) => {
178
+ const output = data.toString();
179
+ // Only log errors or important messages
180
+ if (output.includes("ERROR") || output.includes("WARN")) {
181
+ ensureLogger().debug(`Server output: ${output}`);
182
+ }
183
+ });
184
+
185
+ this.process.stderr.on("data", (data) => {
186
+ ensureLogger().error(`Server error: ${data}`);
187
+ });
188
+ }
189
+
190
+ // Handle process exit
191
+ this.process.on("exit", (code) => {
192
+ if (code !== 0 && code !== null) {
193
+ ensureLogger().error(`Server process exited with code ${code}`);
194
+ }
195
+ });
196
+
197
+ // Wait for server to be ready
198
+ await this.waitForServer();
199
+ ensureLogger().debug(`Test server started on port ${this.config.port}`);
200
+ }
201
+
202
+ /**
203
+ * Stop the test server
204
+ */
205
+ async stop(): Promise<void> {
206
+ if (!this.process) {
207
+ return;
208
+ }
209
+
210
+ return new Promise((resolve) => {
211
+ if (!this.process) {
212
+ resolve();
213
+ return;
214
+ }
215
+
216
+ const cleanup = () => {
217
+ this.process = null;
218
+ this.startPromise = null;
219
+ resolve();
220
+ };
221
+
222
+ // Set a timeout for graceful shutdown
223
+ const killTimeout = setTimeout(() => {
224
+ if (this.process) {
225
+ this.process.kill("SIGKILL");
226
+ }
227
+ cleanup();
228
+ }, 5000);
229
+
230
+ this.process.on("exit", () => {
231
+ clearTimeout(killTimeout);
232
+ cleanup();
233
+ });
234
+
235
+ // Try graceful shutdown first
236
+ this.process.kill("SIGTERM");
237
+ });
238
+ }
239
+
240
+ /**
241
+ * Get the API base URL
242
+ */
243
+ getApiBase(): string {
244
+ return this.config.apiBase;
245
+ }
246
+
247
+ /**
248
+ * Make a request to the test server
249
+ */
250
+ async request(endpoint: string, options: any = {}): Promise<any> {
251
+ const url = `${this.config.apiBase}${endpoint}`;
252
+ const response = await fetch(url, {
253
+ ...options,
254
+ headers: {
255
+ "Content-Type": "application/json",
256
+ ...options.headers,
257
+ },
258
+ });
259
+
260
+ const text = await response.text();
261
+ let data;
262
+ try {
263
+ data = JSON.parse(text);
264
+ } catch {
265
+ data = text;
266
+ }
267
+
268
+ return {
269
+ status: response.status,
270
+ ok: response.ok,
271
+ data,
272
+ response,
273
+ };
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Create a test server instance
279
+ */
280
+ export function createTestServer(config: TestServerConfig): TestServer {
281
+ return new TestServer(config);
282
+ }
283
+
284
+ /**
285
+ * Global test server instance for Jest
286
+ */
287
+ let globalTestServer: TestServer | null = null;
288
+
289
+ /**
290
+ * Setup function for Jest beforeAll
291
+ */
292
+ export async function setupTestServer(config: TestServerConfig): Promise<void> {
293
+ globalTestServer = new TestServer(config);
294
+ await globalTestServer.start();
295
+ }
296
+
297
+ /**
298
+ * Teardown function for Jest afterAll
299
+ */
300
+ export async function teardownTestServer(): Promise<void> {
301
+ if (globalTestServer) {
302
+ await globalTestServer.stop();
303
+ globalTestServer = null;
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Get the global test server instance
309
+ */
310
+ export function getTestServer(): TestServer | null {
311
+ return globalTestServer;
312
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Shared TypeScript Type Definitions
3
+ */
4
+
5
+ /**
6
+ * Field validation error detail
7
+ */
8
+ export interface FieldValidationError {
9
+ field: string;
10
+ message: string;
11
+ }
12
+
13
+ /**
14
+ * Standardized API response structure for all applications
15
+ * Supports both legacy string errors and new structured errors
16
+ * @template T - The type of the data payload
17
+ */
18
+ export interface ApiResponse<T = any> {
19
+ success: boolean;
20
+ data?: T;
21
+ error?:
22
+ | string
23
+ | {
24
+ code: string;
25
+ message: string;
26
+ details?: any;
27
+ stack?: string; // Only in development
28
+ };
29
+ message?: string; // Optional success message
30
+ errors?: FieldValidationError[]; // For validation errors
31
+ metadata?: {
32
+ timestamp: string;
33
+ version?: string;
34
+ requestId?: string;
35
+ pagination?: {
36
+ page: number;
37
+ limit: number;
38
+ total: number;
39
+ hasMore: boolean;
40
+ };
41
+ };
42
+ timestamp?: string; // Legacy field for backward compatibility
43
+ }
44
+
45
+ /**
46
+ * Legacy simple error format (deprecated - for backward compatibility)
47
+ * @deprecated Use structured error object instead
48
+ */
49
+ export interface ApiResponseLegacy<T = any> {
50
+ success: boolean;
51
+ data?: T;
52
+ error?: string;
53
+ message?: string;
54
+ timestamp?: string;
55
+ }
56
+
57
+ /**
58
+ * Configuration options
59
+ */
60
+ export interface ConfigOptions {
61
+ configPath?: string;
62
+ defaultConfig?: any;
63
+ watchChanges?: boolean;
64
+ }
65
+
66
+ /**
67
+ * Network interface information
68
+ */
69
+ export interface NetworkInterface {
70
+ name: string;
71
+ address: string;
72
+ family: "IPv4" | "IPv6";
73
+ internal: boolean;
74
+ mac?: string;
75
+ }
76
+
77
+ /**
78
+ * Update check result
79
+ */
80
+ export interface UpdateCheckResult {
81
+ hasUpdate: boolean;
82
+ currentVersion: string;
83
+ latestVersion?: string;
84
+ releaseUrl?: string;
85
+ releaseNotes?: string;
86
+ publishedAt?: string;
87
+ }
88
+
89
+ /**
90
+ * WebSocket client information
91
+ */
92
+ export interface ClientInfo {
93
+ id: string;
94
+ connectedAt: Date;
95
+ lastActivity: Date;
96
+ subscriptions: Set<string>;
97
+ }
98
+
99
+ /**
100
+ * Logger interface
101
+ */
102
+ export interface Logger {
103
+ info: (...args: any[]) => void;
104
+ error: (...args: any[]) => void;
105
+ warn: (...args: any[]) => void;
106
+ debug: (...args: any[]) => void;
107
+ }
108
+
109
+ /**
110
+ * WebSocket message types
111
+ */
112
+ export interface WebSocketMessage {
113
+ type: string;
114
+ data: any;
115
+ }
116
+
117
+ export interface SimulatorUpdateMessage extends WebSocketMessage {
118
+ type: "simulator:started" | "simulator:stopped" | "simulator:data";
119
+ data: Simulator;
120
+ }
121
+
122
+ export interface TemplateUpdateMessage extends WebSocketMessage {
123
+ type: "template:created" | "template:updated" | "template:deleted";
124
+ data: Template;
125
+ }
126
+
127
+ export interface DataUpdateMessage extends WebSocketMessage {
128
+ type: "data:update";
129
+ data: {
130
+ simulatorId: string;
131
+ values: Record<string, any>;
132
+ };
133
+ }
134
+
135
+ export interface Simulator {
136
+ id: string;
137
+ name: string;
138
+ status: string;
139
+ [key: string]: any;
140
+ }
141
+
142
+ export interface Template {
143
+ id: string;
144
+ name: string;
145
+ [key: string]: any;
146
+ }
147
+
148
+ export interface AppConfig {
149
+ [key: string]: any;
150
+ }
151
+
152
+ export interface ValidationError extends Error {
153
+ details?: any[];
154
+ }
@@ -0,0 +1,196 @@
1
+ import os from "os";
2
+ import path from "path";
3
+ import { mkdirSync } from "fs";
4
+
5
+ /**
6
+ * Get the appropriate data directory for the application.
7
+ *
8
+ * In desktop app mode (Electron):
9
+ * - macOS: ~/Library/Application Support/{appId}
10
+ * - Windows: %APPDATA%/{appName}
11
+ * - Linux: ~/.local/share/{appName}
12
+ *
13
+ * In development or web mode:
14
+ * - Uses project root ./data directory
15
+ *
16
+ * @param appId - The application identifier (e.g., 'com.superdangerous.app-name')
17
+ * @param appName - The application name (e.g., 'app-name')
18
+ * @returns The absolute path to the application data directory
19
+ */
20
+ export function getAppDataPath(appId: string, appName: string): string {
21
+ // Check if running in a desktop app environment
22
+ const isElectron =
23
+ process.versions?.electron || process.env.ELECTRON_RUNNING === "true";
24
+ const isDevelopment = process.env.NODE_ENV === "development";
25
+
26
+ // In development mode, always use local paths unless explicitly forced
27
+ const isDesktopApp = !isDevelopment && isElectron;
28
+
29
+ if (isDesktopApp) {
30
+ // Use platform-specific app data directories
31
+ const platform = process.platform;
32
+ const homeDir = os.homedir();
33
+ let appDataDir: string;
34
+
35
+ switch (platform) {
36
+ case "darwin": // macOS
37
+ appDataDir = path.join(
38
+ homeDir,
39
+ "Library",
40
+ "Application Support",
41
+ appId,
42
+ );
43
+ break;
44
+ case "win32": // Windows
45
+ appDataDir = path.join(
46
+ process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
47
+ appName,
48
+ );
49
+ break;
50
+ default: // Linux and others
51
+ appDataDir = path.join(
52
+ process.env.XDG_DATA_HOME || path.join(homeDir, ".local", "share"),
53
+ appName,
54
+ );
55
+ break;
56
+ }
57
+
58
+ // Ensure the directory exists
59
+ mkdirSync(appDataDir, { recursive: true });
60
+ return appDataDir;
61
+ }
62
+
63
+ // In development or web mode, use the project root ./data directory
64
+ const dataDir = path.join(process.cwd(), "data");
65
+ mkdirSync(dataDir, { recursive: true });
66
+ return dataDir;
67
+ }
68
+
69
+ /**
70
+ * Get the full path for a data file within the application data directory.
71
+ *
72
+ * @param filename - The name of the file
73
+ * @param appId - The application identifier
74
+ * @param appName - The application name
75
+ * @returns The absolute path to the file
76
+ */
77
+ export function getDataFilePath(
78
+ filename: string,
79
+ appId: string,
80
+ appName: string,
81
+ ): string {
82
+ return path.join(getAppDataPath(appId, appName), filename);
83
+ }
84
+
85
+ /**
86
+ * Get the logs directory path for the application.
87
+ *
88
+ * @param appId - The application identifier
89
+ * @param appName - The application name
90
+ * @returns The absolute path to the logs directory
91
+ */
92
+ export function getLogsPath(appId: string, appName: string): string {
93
+ const logsDir = path.join(getAppDataPath(appId, appName), "logs");
94
+ mkdirSync(logsDir, { recursive: true });
95
+ return logsDir;
96
+ }
97
+
98
+ /**
99
+ * Get the configuration directory path for the application.
100
+ *
101
+ * @param appId - The application identifier
102
+ * @param appName - The application name
103
+ * @returns The absolute path to the config directory
104
+ */
105
+ export function getConfigPath(appId: string, appName: string): string {
106
+ const configDir = path.join(getAppDataPath(appId, appName), "config");
107
+ mkdirSync(configDir, { recursive: true });
108
+ return configDir;
109
+ }
110
+
111
+ /**
112
+ * Get the cache directory path for the application.
113
+ *
114
+ * @param appId - The application identifier
115
+ * @param appName - The application name
116
+ * @returns The absolute path to the cache directory
117
+ */
118
+ export function getCachePath(appId: string, appName: string): string {
119
+ const cacheDir = path.join(getAppDataPath(appId, appName), "cache");
120
+ mkdirSync(cacheDir, { recursive: true });
121
+ return cacheDir;
122
+ }
123
+
124
+ /**
125
+ * Check if the application is running in desktop mode (Electron).
126
+ *
127
+ * @returns True if running as a desktop app, false otherwise
128
+ */
129
+ export function isDesktopApp(): boolean {
130
+ const isDevelopment = process.env.NODE_ENV === "development";
131
+ const isElectron =
132
+ !!process.versions?.electron || process.env.ELECTRON_RUNNING === "true";
133
+
134
+ // In development mode, consider it a desktop app only if explicitly set
135
+ return !isDevelopment && isElectron;
136
+ }
137
+
138
+ /**
139
+ * Get platform-specific user documents directory.
140
+ *
141
+ * @returns The path to the user's documents directory
142
+ */
143
+ export function getDocumentsPath(): string {
144
+ const platform = process.platform;
145
+ const homeDir = os.homedir();
146
+
147
+ switch (platform) {
148
+ case "darwin": // macOS
149
+ case "linux":
150
+ return path.join(homeDir, "Documents");
151
+ case "win32": // Windows
152
+ return path.join(homeDir, "Documents");
153
+ default:
154
+ return homeDir;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Get platform-specific user desktop directory.
160
+ *
161
+ * @returns The path to the user's desktop directory
162
+ */
163
+ export function getDesktopPath(): string {
164
+ const platform = process.platform;
165
+ const homeDir = os.homedir();
166
+
167
+ switch (platform) {
168
+ case "darwin": // macOS
169
+ case "linux":
170
+ return path.join(homeDir, "Desktop");
171
+ case "win32": // Windows
172
+ return path.join(homeDir, "Desktop");
173
+ default:
174
+ return homeDir;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Get platform-specific user downloads directory.
180
+ *
181
+ * @returns The path to the user's downloads directory
182
+ */
183
+ export function getDownloadsPath(): string {
184
+ const platform = process.platform;
185
+ const homeDir = os.homedir();
186
+
187
+ switch (platform) {
188
+ case "darwin": // macOS
189
+ case "linux":
190
+ return path.join(homeDir, "Downloads");
191
+ case "win32": // Windows
192
+ return path.join(homeDir, "Downloads");
193
+ default:
194
+ return homeDir;
195
+ }
196
+ }