server-devtools 0.1.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 (109) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +544 -0
  3. package/dist/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  4. package/dist/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  5. package/dist/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  6. package/dist/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  7. package/dist/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  8. package/dist/dashboard/assets/index-DvYSUjO3.css +2 -0
  9. package/dist/dashboard/assets/index-vQGCvuXC.js +9 -0
  10. package/dist/dashboard/favicon.svg +9 -0
  11. package/dist/dashboard/icons.svg +24 -0
  12. package/dist/dashboard/index.html +14 -0
  13. package/dist/src/config/index.d.ts +21 -0
  14. package/dist/src/config/index.d.ts.map +1 -0
  15. package/dist/src/config/index.js +30 -0
  16. package/dist/src/config/index.js.map +1 -0
  17. package/dist/src/core/execution-tree-builder.d.ts +6 -0
  18. package/dist/src/core/execution-tree-builder.d.ts.map +1 -0
  19. package/dist/src/core/execution-tree-builder.js +44 -0
  20. package/dist/src/core/execution-tree-builder.js.map +1 -0
  21. package/dist/src/dashboard/dashboard-errors-mapper.d.ts +5 -0
  22. package/dist/src/dashboard/dashboard-errors-mapper.d.ts.map +1 -0
  23. package/dist/src/dashboard/dashboard-errors-mapper.js +19 -0
  24. package/dist/src/dashboard/dashboard-errors-mapper.js.map +1 -0
  25. package/dist/src/dashboard/dashboard-request-detail-mapper.d.ts +16 -0
  26. package/dist/src/dashboard/dashboard-request-detail-mapper.d.ts.map +1 -0
  27. package/dist/src/dashboard/dashboard-request-detail-mapper.js +79 -0
  28. package/dist/src/dashboard/dashboard-request-detail-mapper.js.map +1 -0
  29. package/dist/src/dashboard/dashboard-response-detail-mapper.d.ts +6 -0
  30. package/dist/src/dashboard/dashboard-response-detail-mapper.d.ts.map +1 -0
  31. package/dist/src/dashboard/dashboard-response-detail-mapper.js +39 -0
  32. package/dist/src/dashboard/dashboard-response-detail-mapper.js.map +1 -0
  33. package/dist/src/dashboard/dashboard-server.d.ts +21 -0
  34. package/dist/src/dashboard/dashboard-server.d.ts.map +1 -0
  35. package/dist/src/dashboard/dashboard-server.js +676 -0
  36. package/dist/src/dashboard/dashboard-server.js.map +1 -0
  37. package/dist/src/index.d.ts +16 -0
  38. package/dist/src/index.d.ts.map +1 -0
  39. package/dist/src/index.js +118 -0
  40. package/dist/src/index.js.map +1 -0
  41. package/dist/src/instrumentation/exporter.d.ts +11 -0
  42. package/dist/src/instrumentation/exporter.d.ts.map +1 -0
  43. package/dist/src/instrumentation/exporter.js +46 -0
  44. package/dist/src/instrumentation/exporter.js.map +1 -0
  45. package/dist/src/instrumentation/instrumentation.d.ts +11 -0
  46. package/dist/src/instrumentation/instrumentation.d.ts.map +1 -0
  47. package/dist/src/instrumentation/instrumentation.js +144 -0
  48. package/dist/src/instrumentation/instrumentation.js.map +1 -0
  49. package/dist/src/instrumentation/outbound-http/outbound-http-capture.d.ts +24 -0
  50. package/dist/src/instrumentation/outbound-http/outbound-http-capture.d.ts.map +1 -0
  51. package/dist/src/instrumentation/outbound-http/outbound-http-capture.js +245 -0
  52. package/dist/src/instrumentation/outbound-http/outbound-http-capture.js.map +1 -0
  53. package/dist/src/instrumentation/span-mapper.d.ts +8 -0
  54. package/dist/src/instrumentation/span-mapper.d.ts.map +1 -0
  55. package/dist/src/instrumentation/span-mapper.js +54 -0
  56. package/dist/src/instrumentation/span-mapper.js.map +1 -0
  57. package/dist/src/integrations/nestjs/server-devtools-nest.interceptor.d.ts +6 -0
  58. package/dist/src/integrations/nestjs/server-devtools-nest.interceptor.d.ts.map +1 -0
  59. package/dist/src/integrations/nestjs/server-devtools-nest.interceptor.js +23 -0
  60. package/dist/src/integrations/nestjs/server-devtools-nest.interceptor.js.map +1 -0
  61. package/dist/src/nestjs.d.ts +2 -0
  62. package/dist/src/nestjs.d.ts.map +1 -0
  63. package/dist/src/nestjs.js +9 -0
  64. package/dist/src/nestjs.js.map +1 -0
  65. package/dist/src/security/auth.service.d.ts +16 -0
  66. package/dist/src/security/auth.service.d.ts.map +1 -0
  67. package/dist/src/security/auth.service.js +96 -0
  68. package/dist/src/security/auth.service.js.map +1 -0
  69. package/dist/src/security/encrypt-decrypt.service.d.ts +10 -0
  70. package/dist/src/security/encrypt-decrypt.service.d.ts.map +1 -0
  71. package/dist/src/security/encrypt-decrypt.service.js +93 -0
  72. package/dist/src/security/encrypt-decrypt.service.js.map +1 -0
  73. package/dist/src/storage/sqlite-storage.d.ts +35 -0
  74. package/dist/src/storage/sqlite-storage.d.ts.map +1 -0
  75. package/dist/src/storage/sqlite-storage.js +583 -0
  76. package/dist/src/storage/sqlite-storage.js.map +1 -0
  77. package/dist/src/types/dashboard.d.ts +32 -0
  78. package/dist/src/types/dashboard.d.ts.map +1 -0
  79. package/dist/src/types/dashboard.js +3 -0
  80. package/dist/src/types/dashboard.js.map +1 -0
  81. package/dist/src/types/database.d.ts +16 -0
  82. package/dist/src/types/database.d.ts.map +1 -0
  83. package/dist/src/types/database.js +3 -0
  84. package/dist/src/types/database.js.map +1 -0
  85. package/dist/src/types/http-client.d.ts +8 -0
  86. package/dist/src/types/http-client.d.ts.map +1 -0
  87. package/dist/src/types/http-client.js +3 -0
  88. package/dist/src/types/http-client.js.map +1 -0
  89. package/dist/src/types/index.d.ts +7 -0
  90. package/dist/src/types/index.d.ts.map +1 -0
  91. package/dist/src/types/index.js +23 -0
  92. package/dist/src/types/index.js.map +1 -0
  93. package/dist/src/types/misc.d.ts +25 -0
  94. package/dist/src/types/misc.d.ts.map +1 -0
  95. package/dist/src/types/misc.js +3 -0
  96. package/dist/src/types/misc.js.map +1 -0
  97. package/dist/src/types/span.d.ts +25 -0
  98. package/dist/src/types/span.d.ts.map +1 -0
  99. package/dist/src/types/span.js +3 -0
  100. package/dist/src/types/span.js.map +1 -0
  101. package/dist/src/types/trace.d.ts +32 -0
  102. package/dist/src/types/trace.d.ts.map +1 -0
  103. package/dist/src/types/trace.js +4 -0
  104. package/dist/src/types/trace.js.map +1 -0
  105. package/dist/src/utils/logger.d.ts +2 -0
  106. package/dist/src/utils/logger.d.ts.map +1 -0
  107. package/dist/src/utils/logger.js +19 -0
  108. package/dist/src/utils/logger.js.map +1 -0
  109. package/package.json +82 -0
@@ -0,0 +1,676 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const config_1 = __importDefault(require("../config"));
9
+ const execution_tree_builder_1 = __importDefault(require("../core/execution-tree-builder"));
10
+ const dashboard_errors_mapper_1 = __importDefault(require("./dashboard-errors-mapper"));
11
+ const dashboard_request_detail_mapper_1 = __importDefault(require("./dashboard-request-detail-mapper"));
12
+ const dashboard_response_detail_mapper_1 = __importDefault(require("./dashboard-response-detail-mapper"));
13
+ const logger_1 = require("../utils/logger");
14
+ const sourceBuildDashboardDirectory = node_path_1.default.resolve(__dirname, "../../dist/dashboard");
15
+ const dashboardDirectory = (0, node_fs_1.existsSync)(node_path_1.default.join(sourceBuildDashboardDirectory, "index.html"))
16
+ ? sourceBuildDashboardDirectory
17
+ : node_path_1.default.resolve(__dirname, "../../dashboard");
18
+ const contentTypes = {
19
+ ".html": "text/html; charset=utf-8",
20
+ ".js": "text/javascript; charset=utf-8",
21
+ ".css": "text/css; charset=utf-8",
22
+ ".svg": "image/svg+xml",
23
+ ".woff2": "font/woff2",
24
+ ".json": "application/json; charset=utf-8",
25
+ };
26
+ class DashboardServer {
27
+ storage;
28
+ authService;
29
+ executionTreeBuilder = new execution_tree_builder_1.default();
30
+ requestDetailMapper = new dashboard_request_detail_mapper_1.default();
31
+ responseDetailMapper = new dashboard_response_detail_mapper_1.default();
32
+ errorMapper = new dashboard_errors_mapper_1.default();
33
+ // private server?: Server;
34
+ constructor(storage, authService) {
35
+ this.storage = storage;
36
+ this.authService = authService;
37
+ }
38
+ readJsonBody(req) {
39
+ return new Promise((resolve, reject) => {
40
+ let body = "";
41
+ req.on("data", (chunk) => {
42
+ body += chunk.toString();
43
+ });
44
+ req.on("end", () => {
45
+ try {
46
+ resolve(body ? JSON.parse(body) : {});
47
+ }
48
+ catch (error) {
49
+ reject(error);
50
+ }
51
+ });
52
+ req.on("error", reject);
53
+ });
54
+ }
55
+ getCookie(req, name) {
56
+ const cookieHeader = req.headers.cookie;
57
+ if (!cookieHeader)
58
+ return undefined;
59
+ const cookies = cookieHeader.split(";");
60
+ for (const cookie of cookies) {
61
+ const [key, ...valueParts] = cookie.trim().split("=");
62
+ if (key === name) {
63
+ return decodeURIComponent(valueParts.join("="));
64
+ }
65
+ }
66
+ return undefined;
67
+ }
68
+ authenticateRequest(req, res) {
69
+ const token = this.getCookie(req, "sdt_session");
70
+ if (!token) {
71
+ this.sendUnauthorized(res);
72
+ return undefined;
73
+ }
74
+ const session = this.authService.getSession(token);
75
+ if (!session) {
76
+ this.sendUnauthorized(res);
77
+ return undefined;
78
+ }
79
+ return session;
80
+ }
81
+ sendUnauthorized(res) {
82
+ res.statusCode = 401;
83
+ res.setHeader("Content-Type", "application/json");
84
+ res.setHeader("Cache-Control", "no-store");
85
+ res.end(JSON.stringify({
86
+ message: "Unauthorized",
87
+ }));
88
+ return;
89
+ }
90
+ async handleLogin(req, res) {
91
+ const body = await this.readJsonBody(req);
92
+ const username = body?.username || null;
93
+ const password = body?.password || null;
94
+ if (!username || !password) {
95
+ res.statusCode = 400;
96
+ res.setHeader("Content-Type", "application/json");
97
+ res.end(JSON.stringify({
98
+ message: "Password and Username are required!",
99
+ }));
100
+ return;
101
+ }
102
+ const valid = this.authService.validateCredentials(username, password);
103
+ if (!valid) {
104
+ res.statusCode = 401;
105
+ res.setHeader("Content-Type", "application/json");
106
+ res.end(JSON.stringify({
107
+ message: "Invalid Auth Credentials",
108
+ }));
109
+ return;
110
+ }
111
+ let token;
112
+ try {
113
+ token = this.authService.createSession(username);
114
+ }
115
+ catch (error) {
116
+ (0, logger_1.debugLog)("Session creation failed", {
117
+ errorName: error instanceof Error ? error.name : typeof error,
118
+ });
119
+ res.statusCode = 500;
120
+ res.setHeader("Content-Type", "application/json");
121
+ res.end(JSON.stringify({ message: "Unable to create session" }));
122
+ return;
123
+ }
124
+ res.statusCode = 200;
125
+ res.setHeader("Content-Type", "application/json");
126
+ res.setHeader("Set-Cookie", `sdt_session=${token}; HttpOnly; SameSite=Strict; Path=/_devtools`);
127
+ res.setHeader("Cache-Control", "no-store");
128
+ res.end(JSON.stringify({
129
+ username,
130
+ }));
131
+ return;
132
+ }
133
+ handleLogout(req, res) {
134
+ const token = this.getCookie(req, "sdt_session");
135
+ if (token) {
136
+ this.authService.deleteSession(token);
137
+ }
138
+ res.setHeader("Set-Cookie", "sdt_session=; HttpOnly; SameSite=Strict; Path=/_devtools; Max-Age=0");
139
+ res.setHeader("Cache-Control", "no-store");
140
+ res.statusCode = 200;
141
+ res.setHeader("Content-Type", "application/json");
142
+ res.end(JSON.stringify({
143
+ message: "Logout Successfully!",
144
+ }));
145
+ }
146
+ handleProfile(req, res) {
147
+ const token = this.getCookie(req, "sdt_session");
148
+ if (!token) {
149
+ res.statusCode = 401;
150
+ res.setHeader("Content-Type", "application/json");
151
+ res.end(JSON.stringify({
152
+ message: "Unauthorized",
153
+ }));
154
+ return;
155
+ }
156
+ const session = this.authService.getSession(token);
157
+ if (!session) {
158
+ res.statusCode = 401;
159
+ res.setHeader("Content-Type", "application/json");
160
+ res.end(JSON.stringify({
161
+ message: "Unauthorized",
162
+ }));
163
+ return;
164
+ }
165
+ res.statusCode = 200;
166
+ res.setHeader("Content-Type", "application/json");
167
+ res.setHeader("Cache-Control", "no-store");
168
+ res.end(JSON.stringify({
169
+ username: session.username,
170
+ }));
171
+ }
172
+ handle(req, res) {
173
+ const requestUrl = req.url ?? '';
174
+ const pathname = requestUrl.split('?')[0];
175
+ if (req.method === "POST" &&
176
+ pathname === `${config_1.default.DASHBOARD_API_ROUTES.AUTH}/login`) {
177
+ return this.handleLogin(req, res);
178
+ }
179
+ // Everything under /_devtools/api from here requires auth
180
+ if (pathname.startsWith("/_devtools/api/")) {
181
+ const session = this.authenticateRequest(req, res);
182
+ if (!session) {
183
+ return;
184
+ }
185
+ }
186
+ if (req.method === "POST" &&
187
+ pathname === `${config_1.default.DASHBOARD_API_ROUTES.AUTH}/logout`) {
188
+ return this.handleLogout(req, res);
189
+ }
190
+ if (req.method === "GET" &&
191
+ pathname === `${config_1.default.DASHBOARD_API_ROUTES.AUTH}/profile`) {
192
+ return this.handleProfile(req, res);
193
+ }
194
+ if (req.method === config_1.default.REQUEST_METHOD.DELETE &&
195
+ pathname === config_1.default.DASHBOARD_API_ROUTES.TRACES) {
196
+ try {
197
+ this.storage.clearHistory();
198
+ }
199
+ catch (error) {
200
+ (0, logger_1.debugLog)("History clear failed", {
201
+ errorName: error instanceof Error ? error.name : typeof error,
202
+ });
203
+ res.statusCode = 500;
204
+ res.setHeader("Content-Type", "application/json");
205
+ res.end(JSON.stringify({ message: "Unable to clear history" }));
206
+ return;
207
+ }
208
+ res.setHeader("Content-Type", "application/json");
209
+ res.end(JSON.stringify({
210
+ message: "Cleared history successfully!",
211
+ }));
212
+ return;
213
+ }
214
+ if (req.method === config_1.default.REQUEST_METHOD.DELETE &&
215
+ pathname.startsWith(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`)) {
216
+ const traceId = decodeURIComponent(pathname.slice(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`.length));
217
+ let traceExists;
218
+ try {
219
+ traceExists = this.storage.getTrace(traceId);
220
+ }
221
+ catch (error) {
222
+ (0, logger_1.debugLog)("Trace lookup failed", {
223
+ traceId,
224
+ errorName: error instanceof Error ? error.name : typeof error,
225
+ });
226
+ res.statusCode = 500;
227
+ res.setHeader("Content-Type", "application/json");
228
+ res.end(JSON.stringify({ message: "Unable to read trace" }));
229
+ return;
230
+ }
231
+ if (!traceExists) {
232
+ res.statusCode = 404;
233
+ res.setHeader("Content-Type", "application/json");
234
+ res.end(JSON.stringify({
235
+ message: "Trace not found",
236
+ }));
237
+ return;
238
+ }
239
+ try {
240
+ this.storage.deleteTrace(traceId);
241
+ }
242
+ catch (error) {
243
+ (0, logger_1.debugLog)("Trace deletion failed", {
244
+ traceId,
245
+ errorName: error instanceof Error ? error.name : typeof error,
246
+ });
247
+ res.statusCode = 500;
248
+ res.setHeader("Content-Type", "application/json");
249
+ res.end(JSON.stringify({ message: "Unable to delete trace" }));
250
+ return;
251
+ }
252
+ res.statusCode = 200;
253
+ res.setHeader("Content-Type", "application/json");
254
+ res.end(JSON.stringify({
255
+ message: "Deleted successfully!",
256
+ }));
257
+ return;
258
+ }
259
+ if (req.method === config_1.default.REQUEST_METHOD.GET &&
260
+ req.url?.startsWith(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`) &&
261
+ req.url.endsWith("/execution")) {
262
+ const prefix = `${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`;
263
+ const traceId = req.url
264
+ .slice(prefix.length)
265
+ .replace(/\/execution$/, "");
266
+ let trace;
267
+ try {
268
+ trace = this.storage.getTrace(traceId);
269
+ }
270
+ catch (error) {
271
+ (0, logger_1.debugLog)("Trace lookup failed", {
272
+ traceId,
273
+ errorName: error instanceof Error ? error.name : typeof error,
274
+ });
275
+ res.statusCode = 500;
276
+ res.setHeader("Content-Type", "application/json");
277
+ res.end(JSON.stringify({ message: "Unable to read trace" }));
278
+ return;
279
+ }
280
+ if (!trace) {
281
+ res.statusCode = 404;
282
+ res.setHeader("Content-Type", "application/json");
283
+ res.end(JSON.stringify({
284
+ message: "Trace not found",
285
+ }));
286
+ return;
287
+ }
288
+ const executionTree = this.executionTreeBuilder.build(trace);
289
+ res.statusCode = 200;
290
+ res.setHeader("Content-Type", "application/json");
291
+ res.end(JSON.stringify(executionTree));
292
+ return;
293
+ }
294
+ if (req.method === config_1.default.REQUEST_METHOD.GET &&
295
+ req.url?.startsWith(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`) &&
296
+ req.url.endsWith("/request")) {
297
+ const prefix = `${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`;
298
+ const traceId = req.url
299
+ .slice(prefix.length)
300
+ .replace(/\/request$/, "");
301
+ let trace;
302
+ try {
303
+ trace = this.storage.getTrace(traceId);
304
+ }
305
+ catch (error) {
306
+ (0, logger_1.debugLog)("Trace lookup failed", {
307
+ traceId,
308
+ errorName: error instanceof Error ? error.name : typeof error,
309
+ });
310
+ res.statusCode = 500;
311
+ res.setHeader("Content-Type", "application/json");
312
+ res.end(JSON.stringify({ message: "Unable to read trace" }));
313
+ return;
314
+ }
315
+ if (!trace) {
316
+ res.statusCode = 404;
317
+ res.setHeader("Content-Type", "application/json");
318
+ res.end(JSON.stringify({
319
+ message: "Trace not found",
320
+ }));
321
+ return;
322
+ }
323
+ let metadata;
324
+ try {
325
+ metadata = this.storage.getTraceMetadata(traceId);
326
+ }
327
+ catch (error) {
328
+ (0, logger_1.debugLog)("Trace metadata lookup failed", {
329
+ traceId,
330
+ errorName: error instanceof Error ? error.name : typeof error,
331
+ });
332
+ res.statusCode = 500;
333
+ res.setHeader("Content-Type", "application/json");
334
+ res.end(JSON.stringify({ message: "Unable to read trace metadata" }));
335
+ return;
336
+ }
337
+ const request = this.requestDetailMapper.map(trace, metadata);
338
+ const user = metadata?.user;
339
+ res.statusCode = 200;
340
+ res.setHeader("Content-Type", "application/json");
341
+ res.end(JSON.stringify({ ...request, user }));
342
+ return;
343
+ }
344
+ if (req.method === config_1.default.REQUEST_METHOD.GET &&
345
+ req.url?.startsWith(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`) &&
346
+ req.url.endsWith("/response")) {
347
+ const prefix = `${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`;
348
+ const traceId = req.url
349
+ .slice(prefix.length)
350
+ .replace(/\/response$/, "");
351
+ let trace;
352
+ try {
353
+ trace = this.storage.getTrace(traceId);
354
+ }
355
+ catch (error) {
356
+ (0, logger_1.debugLog)("Trace lookup failed", {
357
+ traceId,
358
+ errorName: error instanceof Error ? error.name : typeof error,
359
+ });
360
+ res.statusCode = 500;
361
+ res.setHeader("Content-Type", "application/json");
362
+ res.end(JSON.stringify({ message: "Unable to read trace" }));
363
+ return;
364
+ }
365
+ if (!trace) {
366
+ res.statusCode = 404;
367
+ res.setHeader("Content-Type", "application/json");
368
+ res.end(JSON.stringify({
369
+ message: "Trace not found",
370
+ }));
371
+ return;
372
+ }
373
+ let metadata;
374
+ try {
375
+ metadata = this.storage.getTraceMetadata(traceId);
376
+ }
377
+ catch (error) {
378
+ (0, logger_1.debugLog)("Trace metadata lookup failed", {
379
+ traceId,
380
+ errorName: error instanceof Error ? error.name : typeof error,
381
+ });
382
+ res.statusCode = 500;
383
+ res.setHeader("Content-Type", "application/json");
384
+ res.end(JSON.stringify({ message: "Unable to read trace metadata" }));
385
+ return;
386
+ }
387
+ const response = this.responseDetailMapper.map(trace, metadata);
388
+ const user = metadata?.user;
389
+ res.statusCode = 200;
390
+ res.setHeader("Content-Type", "application/json");
391
+ res.end(JSON.stringify({ ...response, user }));
392
+ return;
393
+ }
394
+ if (req.method === config_1.default.REQUEST_METHOD.GET &&
395
+ req.url?.startsWith(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`) &&
396
+ req.url.endsWith("/errors")) {
397
+ const prefix = `${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`;
398
+ const traceId = req.url
399
+ .slice(prefix.length)
400
+ .replace(/\/errors$/, "");
401
+ let trace;
402
+ try {
403
+ trace = this.storage.getTrace(traceId);
404
+ }
405
+ catch (error) {
406
+ (0, logger_1.debugLog)("Trace lookup failed", {
407
+ traceId,
408
+ errorName: error instanceof Error ? error.name : typeof error,
409
+ });
410
+ res.statusCode = 500;
411
+ res.setHeader("Content-Type", "application/json");
412
+ res.end(JSON.stringify({ message: "Unable to read trace" }));
413
+ return;
414
+ }
415
+ if (!trace) {
416
+ res.statusCode = 404;
417
+ res.setHeader("Content-Type", "application/json");
418
+ res.end(JSON.stringify({
419
+ message: "Trace not found",
420
+ }));
421
+ return;
422
+ }
423
+ const errors = this.errorMapper.map(trace);
424
+ res.statusCode = 200;
425
+ res.setHeader("Content-Type", "application/json");
426
+ res.end(JSON.stringify(errors));
427
+ return;
428
+ }
429
+ if (req.method === config_1.default.REQUEST_METHOD.GET &&
430
+ pathname.startsWith(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`)) {
431
+ const prefix = `${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`;
432
+ const segments = pathname.slice(prefix.length).split("/");
433
+ if (segments[1] === "http-client") {
434
+ let traceId;
435
+ let spanId;
436
+ if (segments.length !== 3) {
437
+ res.statusCode = 400;
438
+ res.setHeader("Content-Type", "application/json");
439
+ res.end(JSON.stringify({
440
+ message: "Trace and span identifiers are required",
441
+ }));
442
+ return;
443
+ }
444
+ try {
445
+ traceId = decodeURIComponent(segments[0]);
446
+ spanId = decodeURIComponent(segments[2]);
447
+ }
448
+ catch {
449
+ res.statusCode = 400;
450
+ res.setHeader("Content-Type", "application/json");
451
+ res.end(JSON.stringify({
452
+ message: "Invalid trace or span identifier",
453
+ }));
454
+ return;
455
+ }
456
+ if (!traceId || !spanId) {
457
+ res.statusCode = 400;
458
+ res.setHeader("Content-Type", "application/json");
459
+ res.end(JSON.stringify({
460
+ message: "Trace and span identifiers are required",
461
+ }));
462
+ return;
463
+ }
464
+ let trace;
465
+ try {
466
+ trace = this.storage.getTrace(traceId);
467
+ }
468
+ catch (error) {
469
+ (0, logger_1.debugLog)("Trace lookup failed", {
470
+ traceId,
471
+ errorName: error instanceof Error ? error.name : typeof error,
472
+ });
473
+ res.statusCode = 500;
474
+ res.setHeader("Content-Type", "application/json");
475
+ res.end(JSON.stringify({ message: "Unable to read trace" }));
476
+ return;
477
+ }
478
+ if (!trace) {
479
+ res.statusCode = 404;
480
+ res.setHeader("Content-Type", "application/json");
481
+ res.end(JSON.stringify({
482
+ message: "Trace not found",
483
+ }));
484
+ return;
485
+ }
486
+ const span = trace.spans.find(candidate => candidate.spanId === spanId);
487
+ if (!span) {
488
+ res.statusCode = 404;
489
+ res.setHeader("Content-Type", "application/json");
490
+ res.end(JSON.stringify({
491
+ message: "Span not found",
492
+ }));
493
+ return;
494
+ }
495
+ if (span.type !== "http.client") {
496
+ res.statusCode = 400;
497
+ res.setHeader("Content-Type", "application/json");
498
+ res.end(JSON.stringify({
499
+ message: "Span is not an HTTP client span",
500
+ }));
501
+ return;
502
+ }
503
+ let details;
504
+ try {
505
+ details = this.storage.getHttpClientDetails(spanId);
506
+ }
507
+ catch (error) {
508
+ (0, logger_1.debugLog)("HTTP client details lookup failed", {
509
+ spanId,
510
+ errorName: error instanceof Error ? error.name : typeof error,
511
+ });
512
+ res.statusCode = 500;
513
+ res.setHeader("Content-Type", "application/json");
514
+ res.end(JSON.stringify({ message: "Unable to read HTTP client details" }));
515
+ return;
516
+ }
517
+ res.statusCode = 200;
518
+ res.setHeader("Content-Type", "application/json");
519
+ res.end(JSON.stringify({
520
+ span,
521
+ ...(details ? { details } : {}),
522
+ }));
523
+ return;
524
+ }
525
+ }
526
+ if (req.method === config_1.default.REQUEST_METHOD.GET &&
527
+ pathname.startsWith(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`)) {
528
+ const traceId = decodeURIComponent(pathname.slice(`${config_1.default.DASHBOARD_API_ROUTES.TRACES}/`.length));
529
+ let trace;
530
+ try {
531
+ trace = this.storage.getTrace(traceId);
532
+ }
533
+ catch (error) {
534
+ (0, logger_1.debugLog)("Trace lookup failed", {
535
+ traceId,
536
+ errorName: error instanceof Error ? error.name : typeof error,
537
+ });
538
+ res.statusCode = 500;
539
+ res.setHeader("Content-Type", "application/json");
540
+ res.end(JSON.stringify({ message: "Unable to read trace" }));
541
+ return;
542
+ }
543
+ if (!trace) {
544
+ res.statusCode = 404;
545
+ res.setHeader("Content-Type", "application/json");
546
+ res.end(JSON.stringify({
547
+ message: "Trace not found",
548
+ }));
549
+ return;
550
+ }
551
+ let metadata;
552
+ try {
553
+ metadata = this.storage.getTraceMetadata(traceId);
554
+ }
555
+ catch (error) {
556
+ (0, logger_1.debugLog)("Trace metadata lookup failed", {
557
+ traceId,
558
+ errorName: error instanceof Error ? error.name : typeof error,
559
+ });
560
+ res.statusCode = 500;
561
+ res.setHeader("Content-Type", "application/json");
562
+ res.end(JSON.stringify({ message: "Unable to read trace metadata" }));
563
+ return;
564
+ }
565
+ res.statusCode = 200;
566
+ res.setHeader("Content-Type", "application/json");
567
+ res.end(JSON.stringify({ ...trace, user: metadata?.user }));
568
+ return;
569
+ }
570
+ if (req.method === config_1.default.REQUEST_METHOD.GET &&
571
+ pathname === config_1.default.DASHBOARD_API_ROUTES.REQUESTS) {
572
+ const searchParams = new URL(requestUrl, "http://localhost").searchParams;
573
+ const parsePositiveInteger = (name, fallback) => {
574
+ const value = searchParams.get(name);
575
+ if (value === null) {
576
+ return fallback;
577
+ }
578
+ if (!/^\d+$/.test(value)) {
579
+ return undefined;
580
+ }
581
+ const parsed = Number(value);
582
+ return Number.isSafeInteger(parsed) && parsed > 0
583
+ ? parsed
584
+ : undefined;
585
+ };
586
+ const page = parsePositiveInteger("page", 1);
587
+ const limit = parsePositiveInteger("limit", 20);
588
+ if (page === undefined || limit === undefined || limit > 100) {
589
+ res.statusCode = 400;
590
+ res.setHeader("Content-Type", "application/json");
591
+ res.end(JSON.stringify({
592
+ message: "Page must be a positive integer and limit must be between 1 and 100",
593
+ }));
594
+ return;
595
+ }
596
+ if (!Number.isSafeInteger((page - 1) * limit)) {
597
+ res.statusCode = 400;
598
+ res.setHeader("Content-Type", "application/json");
599
+ res.end(JSON.stringify({
600
+ message: "Page is out of range",
601
+ }));
602
+ return;
603
+ }
604
+ let summaries;
605
+ try {
606
+ summaries = this.storage.getPaginatedTraceSummaries(page, limit);
607
+ }
608
+ catch (error) {
609
+ (0, logger_1.debugLog)("Trace summary lookup failed", {
610
+ errorName: error instanceof Error ? error.name : typeof error,
611
+ });
612
+ res.statusCode = 500;
613
+ res.setHeader("Content-Type", "application/json");
614
+ res.end(JSON.stringify({ message: "Unable to read trace summaries" }));
615
+ return;
616
+ }
617
+ const { summaries: traceSummaries, total } = summaries;
618
+ const data = traceSummaries.map(summary => ({
619
+ id: summary.traceId,
620
+ method: summary.method ?? "UNKNOWN",
621
+ path: summary.path ?? "",
622
+ route: summary.route,
623
+ statusCode: summary.statusCode,
624
+ durationMs: summary.durationMs,
625
+ startedAt: summary.startedAt,
626
+ hasError: summary.hasError,
627
+ user: summary.user,
628
+ }));
629
+ const totalPages = Math.ceil(total / limit);
630
+ res.statusCode = 200;
631
+ res.setHeader("Content-Type", "application/json");
632
+ res.end(JSON.stringify({
633
+ data,
634
+ pagination: {
635
+ page,
636
+ limit,
637
+ total,
638
+ totalPages,
639
+ hasMore: page < totalPages,
640
+ },
641
+ }));
642
+ return;
643
+ }
644
+ if (req.method === "GET" &&
645
+ (pathname === "/_devtools" || pathname.startsWith("/_devtools/")) &&
646
+ !pathname.startsWith("/_devtools/api/") &&
647
+ pathname !== "/_devtools/api") {
648
+ const relativePath = pathname === "/_devtools" || pathname === "/_devtools/"
649
+ ? "index.html"
650
+ : pathname.slice("/_devtools/".length);
651
+ try {
652
+ const decodedPath = decodeURIComponent(relativePath);
653
+ const filePath = node_path_1.default.resolve(dashboardDirectory, decodedPath);
654
+ if (filePath !== dashboardDirectory && !filePath.startsWith(`${dashboardDirectory}${node_path_1.default.sep}`)) {
655
+ res.statusCode = 404;
656
+ res.end("Not Found");
657
+ return;
658
+ }
659
+ const content = (0, node_fs_1.readFileSync)(filePath);
660
+ res.statusCode = 200;
661
+ res.setHeader("Content-Type", contentTypes[node_path_1.default.extname(filePath)] ?? "application/octet-stream");
662
+ res.end(content);
663
+ return;
664
+ }
665
+ catch {
666
+ res.statusCode = 404;
667
+ res.end("Not Found");
668
+ return;
669
+ }
670
+ }
671
+ res.statusCode = 404;
672
+ res.end("Not Found");
673
+ }
674
+ }
675
+ exports.default = DashboardServer;
676
+ //# sourceMappingURL=dashboard-server.js.map