@error-explorer/node 1.1.1

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.
package/dist/index.js ADDED
@@ -0,0 +1,1674 @@
1
+ import * as os2 from 'os';
2
+ import * as path from 'path';
3
+ import * as crypto from 'crypto';
4
+ import * as http from 'http';
5
+ import * as https from 'https';
6
+
7
+ // src/config/Config.ts
8
+ var DEFAULT_ENDPOINT = "https://error-explorer.com/api/v1/webhook";
9
+ function parseDsn(dsn) {
10
+ let url;
11
+ try {
12
+ url = new URL(dsn);
13
+ } catch {
14
+ throw new Error(`Invalid DSN format: ${dsn}. Expected format: https://token@host/path`);
15
+ }
16
+ const token = url.username;
17
+ if (!token) {
18
+ throw new Error("DSN must contain a token (username part)");
19
+ }
20
+ url.username = "";
21
+ url.password = "";
22
+ return {
23
+ token,
24
+ endpoint: url.toString().replace(/\/$/, "")
25
+ };
26
+ }
27
+ function detectEnvironment() {
28
+ return process.env["NODE_ENV"] || "development";
29
+ }
30
+ function getDefaultServerName() {
31
+ try {
32
+ return os2.hostname();
33
+ } catch {
34
+ return "unknown";
35
+ }
36
+ }
37
+ function resolveConfig(options) {
38
+ let token;
39
+ let endpoint;
40
+ if (options.dsn) {
41
+ const parsed = parseDsn(options.dsn);
42
+ token = parsed.token;
43
+ endpoint = options.endpoint || parsed.endpoint;
44
+ } else if (options.token) {
45
+ token = options.token;
46
+ endpoint = options.endpoint || DEFAULT_ENDPOINT;
47
+ } else {
48
+ throw new Error("Either token or dsn must be provided");
49
+ }
50
+ if (!token.startsWith("ee_")) {
51
+ console.warn('[ErrorExplorer] Token should start with "ee_" prefix');
52
+ }
53
+ return {
54
+ token,
55
+ endpoint,
56
+ environment: options.environment || detectEnvironment(),
57
+ release: options.release || "",
58
+ serverName: options.serverName || getDefaultServerName(),
59
+ autoCapture: {
60
+ uncaughtExceptions: options.autoCapture?.uncaughtExceptions ?? true,
61
+ unhandledRejections: options.autoCapture?.unhandledRejections ?? true,
62
+ console: options.autoCapture?.console ?? false
63
+ },
64
+ breadcrumbs: {
65
+ enabled: options.breadcrumbs?.enabled ?? true,
66
+ maxBreadcrumbs: options.breadcrumbs?.maxBreadcrumbs ?? 50,
67
+ http: options.breadcrumbs?.http ?? true,
68
+ console: options.breadcrumbs?.console ?? true
69
+ },
70
+ beforeSend: options.beforeSend,
71
+ ignoreErrors: options.ignoreErrors || [],
72
+ maxRetries: options.maxRetries ?? 3,
73
+ timeout: options.timeout ?? 5e3,
74
+ debug: options.debug ?? false,
75
+ hmacSecret: options.hmacSecret,
76
+ exitOnUncaughtException: options.exitOnUncaughtException ?? true
77
+ };
78
+ }
79
+ function matchesPattern(str, patterns) {
80
+ return patterns.some((pattern) => {
81
+ if (typeof pattern === "string") {
82
+ return str.includes(pattern);
83
+ }
84
+ return pattern.test(str);
85
+ });
86
+ }
87
+ function parseStackTrace(stack) {
88
+ if (!stack) {
89
+ return [];
90
+ }
91
+ const frames = [];
92
+ const lines = stack.split("\n");
93
+ for (const line of lines) {
94
+ const frame = parseStackLine(line);
95
+ if (frame) {
96
+ frames.push(frame);
97
+ }
98
+ }
99
+ return frames;
100
+ }
101
+ function parseStackLine(line) {
102
+ if (!line.trim().startsWith("at ")) {
103
+ return null;
104
+ }
105
+ const content = line.replace(/^\s*at\s+/, "").trim();
106
+ const withFunctionMatch = content.match(
107
+ /^(?:async\s+)?(.+?)\s+\((.+?):(\d+):(\d+)\)$/
108
+ );
109
+ if (withFunctionMatch) {
110
+ const [, fn, file, lineNo, colNo] = withFunctionMatch;
111
+ return createFrame(fn, file, lineNo, colNo);
112
+ }
113
+ const anonymousMatch = content.match(/^(.+?):(\d+):(\d+)$/);
114
+ if (anonymousMatch) {
115
+ const [, file, lineNo, colNo] = anonymousMatch;
116
+ return createFrame(void 0, file, lineNo, colNo);
117
+ }
118
+ return null;
119
+ }
120
+ function createFrame(fn, file, lineNo, colNo) {
121
+ const frame = {};
122
+ if (fn) {
123
+ frame.function = cleanFunctionName(fn);
124
+ }
125
+ if (file) {
126
+ frame.filename = file;
127
+ frame.abs_path = path.isAbsolute(file) ? file : void 0;
128
+ frame.in_app = !file.includes("node_modules") && !file.startsWith("node:");
129
+ if (frame.in_app && file.includes("/")) {
130
+ const parts = file.split("/");
131
+ frame.module = parts[parts.length - 1]?.replace(/\.[^/.]+$/, "");
132
+ }
133
+ }
134
+ if (lineNo) {
135
+ frame.lineno = parseInt(lineNo, 10);
136
+ }
137
+ if (colNo) {
138
+ frame.colno = parseInt(colNo, 10);
139
+ }
140
+ return frame;
141
+ }
142
+ function cleanFunctionName(name) {
143
+ name = name.replace(/^Object\./, "");
144
+ name = name.replace(/^Module\./, "");
145
+ if (name === "<anonymous>") {
146
+ return "(anonymous)";
147
+ }
148
+ return name;
149
+ }
150
+ function getErrorName(error) {
151
+ if (error instanceof Error) {
152
+ return error.name || error.constructor.name;
153
+ }
154
+ if (typeof error === "object" && error !== null) {
155
+ const obj = error;
156
+ if (typeof obj["name"] === "string") {
157
+ return obj["name"];
158
+ }
159
+ }
160
+ return "Error";
161
+ }
162
+ function getErrorMessage(error) {
163
+ if (error instanceof Error) {
164
+ return error.message;
165
+ }
166
+ if (typeof error === "string") {
167
+ return error;
168
+ }
169
+ if (typeof error === "object" && error !== null) {
170
+ const obj = error;
171
+ if (typeof obj["message"] === "string") {
172
+ return obj["message"];
173
+ }
174
+ try {
175
+ return JSON.stringify(error);
176
+ } catch {
177
+ return String(error);
178
+ }
179
+ }
180
+ return String(error);
181
+ }
182
+ function getErrorStack(error) {
183
+ if (error instanceof Error) {
184
+ return error.stack;
185
+ }
186
+ if (typeof error === "object" && error !== null) {
187
+ const obj = error;
188
+ if (typeof obj["stack"] === "string") {
189
+ return obj["stack"];
190
+ }
191
+ }
192
+ return void 0;
193
+ }
194
+ function generateUuid() {
195
+ return crypto.randomUUID();
196
+ }
197
+ function generateShortId() {
198
+ return crypto.randomBytes(4).toString("hex");
199
+ }
200
+ function generateTransactionId() {
201
+ return `txn_${crypto.randomBytes(12).toString("hex")}`;
202
+ }
203
+
204
+ // src/context/ProcessContext.ts
205
+ function collectProcessContext() {
206
+ const memoryUsage = process.memoryUsage();
207
+ const cpuUsage = process.cpuUsage();
208
+ return {
209
+ pid: process.pid,
210
+ ppid: process.ppid,
211
+ uptime: process.uptime(),
212
+ memory: {
213
+ rss: memoryUsage.rss,
214
+ heapTotal: memoryUsage.heapTotal,
215
+ heapUsed: memoryUsage.heapUsed,
216
+ external: memoryUsage.external,
217
+ arrayBuffers: memoryUsage.arrayBuffers
218
+ },
219
+ cpu: {
220
+ user: cpuUsage.user,
221
+ system: cpuUsage.system
222
+ }
223
+ };
224
+ }
225
+ var cachedOsContext = null;
226
+ function collectOsContext() {
227
+ if (cachedOsContext) {
228
+ return cachedOsContext;
229
+ }
230
+ cachedOsContext = {
231
+ name: os2.platform(),
232
+ version: os2.release(),
233
+ arch: os2.arch(),
234
+ kernel_version: os2.version()
235
+ };
236
+ return cachedOsContext;
237
+ }
238
+ function resetOsContext() {
239
+ cachedOsContext = null;
240
+ }
241
+
242
+ // src/context/RuntimeContext.ts
243
+ var cachedRuntimeContext = null;
244
+ function collectRuntimeContext() {
245
+ if (cachedRuntimeContext) {
246
+ return cachedRuntimeContext;
247
+ }
248
+ cachedRuntimeContext = {
249
+ name: "node",
250
+ version: process.version
251
+ };
252
+ return cachedRuntimeContext;
253
+ }
254
+ function resetRuntimeContext() {
255
+ cachedRuntimeContext = null;
256
+ }
257
+ var serverContext = {
258
+ name: void 0,
259
+ hostname: os2.hostname()
260
+ };
261
+ function getServerContext() {
262
+ return serverContext;
263
+ }
264
+ function setServerName(name) {
265
+ serverContext = {
266
+ ...serverContext,
267
+ name
268
+ };
269
+ }
270
+ function resetServerContext() {
271
+ serverContext = {
272
+ name: void 0,
273
+ hostname: os2.hostname()
274
+ };
275
+ }
276
+
277
+ // src/context/UserContext.ts
278
+ var UserContextManager = class {
279
+ user = null;
280
+ /**
281
+ * Set user context
282
+ */
283
+ setUser(user) {
284
+ this.user = user;
285
+ }
286
+ /**
287
+ * Get current user context
288
+ */
289
+ getUser() {
290
+ return this.user;
291
+ }
292
+ /**
293
+ * Clear user context
294
+ */
295
+ clearUser() {
296
+ this.user = null;
297
+ }
298
+ /**
299
+ * Reset manager state
300
+ */
301
+ reset() {
302
+ this.user = null;
303
+ }
304
+ };
305
+ var userContextManager = null;
306
+ function getUserContextManager() {
307
+ if (!userContextManager) {
308
+ userContextManager = new UserContextManager();
309
+ }
310
+ return userContextManager;
311
+ }
312
+ function resetUserContextManager() {
313
+ if (userContextManager) {
314
+ userContextManager.reset();
315
+ }
316
+ userContextManager = null;
317
+ }
318
+
319
+ // src/context/RequestContext.ts
320
+ function extractRequestContext(req) {
321
+ const headers = {};
322
+ const sensitiveHeaders = ["authorization", "cookie", "x-api-key", "x-auth-token"];
323
+ for (const [key, value] of Object.entries(req.headers)) {
324
+ const lowerKey = key.toLowerCase();
325
+ if (!sensitiveHeaders.includes(lowerKey)) {
326
+ headers[key] = value;
327
+ } else {
328
+ headers[key] = "[Filtered]";
329
+ }
330
+ }
331
+ const context = {
332
+ method: req.method,
333
+ headers,
334
+ url: getFullUrl(req)
335
+ };
336
+ const url = req.url || "";
337
+ const queryIndex = url.indexOf("?");
338
+ if (queryIndex !== -1) {
339
+ context.query_string = url.substring(queryIndex + 1);
340
+ }
341
+ const expressReq = req;
342
+ if (expressReq.body !== void 0) {
343
+ context.data = sanitizeBody(expressReq.body);
344
+ }
345
+ if (expressReq.cookies) {
346
+ context.cookies = sanitizeCookies(expressReq.cookies);
347
+ }
348
+ return context;
349
+ }
350
+ function getFullUrl(req) {
351
+ const expressReq = req;
352
+ const path2 = expressReq.originalUrl || req.url || "/";
353
+ const protocol = req.headers["x-forwarded-proto"] || "http";
354
+ const host = req.headers.host || "localhost";
355
+ return `${protocol}://${host}${path2}`;
356
+ }
357
+ function sanitizeBody(body) {
358
+ if (!body || typeof body !== "object") {
359
+ return body;
360
+ }
361
+ const sensitiveKeys = ["password", "secret", "token", "api_key", "apiKey", "credit_card", "creditCard", "cvv", "ssn"];
362
+ const sanitized = {};
363
+ for (const [key, value] of Object.entries(body)) {
364
+ const lowerKey = key.toLowerCase();
365
+ if (sensitiveKeys.some((sk) => lowerKey.includes(sk))) {
366
+ sanitized[key] = "[Filtered]";
367
+ } else if (typeof value === "object" && value !== null) {
368
+ sanitized[key] = sanitizeBody(value);
369
+ } else {
370
+ sanitized[key] = value;
371
+ }
372
+ }
373
+ return sanitized;
374
+ }
375
+ function sanitizeCookies(cookies) {
376
+ const sensitiveNames = ["session", "sess", "token", "auth", "jwt", "sid"];
377
+ const sanitized = {};
378
+ for (const [key, value] of Object.entries(cookies)) {
379
+ const lowerKey = key.toLowerCase();
380
+ if (sensitiveNames.some((sn) => lowerKey.includes(sn))) {
381
+ sanitized[key] = "[Filtered]";
382
+ } else {
383
+ sanitized[key] = value;
384
+ }
385
+ }
386
+ return sanitized;
387
+ }
388
+
389
+ // src/breadcrumbs/BreadcrumbManager.ts
390
+ var BreadcrumbManager = class {
391
+ breadcrumbs = [];
392
+ maxBreadcrumbs = 50;
393
+ /**
394
+ * Initialize with configuration
395
+ */
396
+ init(config) {
397
+ this.maxBreadcrumbs = config.breadcrumbs.maxBreadcrumbs;
398
+ this.breadcrumbs = [];
399
+ }
400
+ /**
401
+ * Add a breadcrumb
402
+ */
403
+ add(breadcrumb) {
404
+ const internal = {
405
+ ...breadcrumb,
406
+ timestamp: breadcrumb.timestamp ?? Date.now()
407
+ };
408
+ this.breadcrumbs.push(internal);
409
+ if (this.breadcrumbs.length > this.maxBreadcrumbs) {
410
+ this.breadcrumbs = this.breadcrumbs.slice(-this.maxBreadcrumbs);
411
+ }
412
+ }
413
+ /**
414
+ * Get all breadcrumbs (oldest first)
415
+ */
416
+ getAll() {
417
+ return [...this.breadcrumbs];
418
+ }
419
+ /**
420
+ * Get the last N breadcrumbs
421
+ */
422
+ getLast(count) {
423
+ return this.breadcrumbs.slice(-count);
424
+ }
425
+ /**
426
+ * Clear all breadcrumbs
427
+ */
428
+ clear() {
429
+ this.breadcrumbs = [];
430
+ }
431
+ /**
432
+ * Reset manager
433
+ */
434
+ reset() {
435
+ this.breadcrumbs = [];
436
+ this.maxBreadcrumbs = 50;
437
+ }
438
+ };
439
+ var breadcrumbManager = null;
440
+ function initBreadcrumbManager(config) {
441
+ if (!breadcrumbManager) {
442
+ breadcrumbManager = new BreadcrumbManager();
443
+ }
444
+ breadcrumbManager.init(config);
445
+ return breadcrumbManager;
446
+ }
447
+ function getBreadcrumbManager() {
448
+ return breadcrumbManager;
449
+ }
450
+ function resetBreadcrumbManager() {
451
+ if (breadcrumbManager) {
452
+ breadcrumbManager.reset();
453
+ }
454
+ breadcrumbManager = null;
455
+ }
456
+
457
+ // src/breadcrumbs/ConsoleTracker.ts
458
+ var ConsoleTracker = class {
459
+ originalMethods = null;
460
+ started = false;
461
+ /**
462
+ * Start tracking console calls
463
+ */
464
+ start() {
465
+ if (this.started) {
466
+ return;
467
+ }
468
+ this.originalMethods = {
469
+ log: console.log.bind(console),
470
+ info: console.info.bind(console),
471
+ warn: console.warn.bind(console),
472
+ error: console.error.bind(console),
473
+ debug: console.debug.bind(console)
474
+ };
475
+ const methods = ["log", "info", "warn", "error", "debug"];
476
+ for (const method of methods) {
477
+ this.wrapConsoleMethod(method);
478
+ }
479
+ this.started = true;
480
+ }
481
+ /**
482
+ * Stop tracking console calls
483
+ */
484
+ stop() {
485
+ if (!this.started || !this.originalMethods) {
486
+ return;
487
+ }
488
+ console.log = this.originalMethods.log;
489
+ console.info = this.originalMethods.info;
490
+ console.warn = this.originalMethods.warn;
491
+ console.error = this.originalMethods.error;
492
+ console.debug = this.originalMethods.debug;
493
+ this.originalMethods = null;
494
+ this.started = false;
495
+ }
496
+ /**
497
+ * Wrap a console method to add breadcrumbs
498
+ */
499
+ wrapConsoleMethod(method) {
500
+ const original = this.originalMethods[method];
501
+ const self = this;
502
+ console[method] = function(...args) {
503
+ self.addBreadcrumb(method, args);
504
+ original(...args);
505
+ };
506
+ }
507
+ /**
508
+ * Add a breadcrumb for a console call
509
+ */
510
+ addBreadcrumb(method, args) {
511
+ const manager = getBreadcrumbManager();
512
+ if (!manager) {
513
+ return;
514
+ }
515
+ const level = this.methodToLevel(method);
516
+ const message = this.formatArgs(args);
517
+ manager.add({
518
+ type: "console",
519
+ category: `console.${method}`,
520
+ message,
521
+ level,
522
+ data: {
523
+ arguments: this.serializeArgs(args)
524
+ }
525
+ });
526
+ }
527
+ /**
528
+ * Map console method to breadcrumb level
529
+ */
530
+ methodToLevel(method) {
531
+ switch (method) {
532
+ case "error":
533
+ return "error";
534
+ case "warn":
535
+ return "warning";
536
+ case "debug":
537
+ return "debug";
538
+ default:
539
+ return "info";
540
+ }
541
+ }
542
+ /**
543
+ * Format arguments into a string message
544
+ */
545
+ formatArgs(args) {
546
+ return args.map((arg) => {
547
+ if (typeof arg === "string") {
548
+ return arg;
549
+ }
550
+ try {
551
+ return JSON.stringify(arg);
552
+ } catch {
553
+ return String(arg);
554
+ }
555
+ }).join(" ").substring(0, 500);
556
+ }
557
+ /**
558
+ * Serialize arguments for data storage
559
+ */
560
+ serializeArgs(args) {
561
+ return args.map((arg) => {
562
+ if (typeof arg === "string" || typeof arg === "number" || typeof arg === "boolean") {
563
+ return arg;
564
+ }
565
+ if (arg === null || arg === void 0) {
566
+ return arg;
567
+ }
568
+ if (arg instanceof Error) {
569
+ return {
570
+ name: arg.name,
571
+ message: arg.message
572
+ };
573
+ }
574
+ try {
575
+ return JSON.parse(JSON.stringify(arg));
576
+ } catch {
577
+ return "[Unserializable]";
578
+ }
579
+ });
580
+ }
581
+ /**
582
+ * Reset tracker state
583
+ */
584
+ reset() {
585
+ this.stop();
586
+ }
587
+ };
588
+ var consoleTracker = null;
589
+ function getConsoleTracker() {
590
+ if (!consoleTracker) {
591
+ consoleTracker = new ConsoleTracker();
592
+ }
593
+ return consoleTracker;
594
+ }
595
+ function resetConsoleTracker() {
596
+ if (consoleTracker) {
597
+ consoleTracker.reset();
598
+ }
599
+ consoleTracker = null;
600
+ }
601
+ var HttpTracker = class {
602
+ originalHttpRequest = null;
603
+ originalHttpsRequest = null;
604
+ started = false;
605
+ patchFailed = false;
606
+ /**
607
+ * Start tracking HTTP requests
608
+ */
609
+ start() {
610
+ if (this.started || this.patchFailed) {
611
+ return;
612
+ }
613
+ try {
614
+ this.originalHttpRequest = http.request;
615
+ this.originalHttpsRequest = https.request;
616
+ this.wrapRequestMethod(http, "http");
617
+ this.wrapRequestMethod(https, "https");
618
+ this.started = true;
619
+ } catch {
620
+ this.patchFailed = true;
621
+ this.originalHttpRequest = null;
622
+ this.originalHttpsRequest = null;
623
+ }
624
+ }
625
+ /**
626
+ * Stop tracking HTTP requests
627
+ */
628
+ stop() {
629
+ if (!this.started) {
630
+ return;
631
+ }
632
+ try {
633
+ if (this.originalHttpRequest) {
634
+ Object.defineProperty(http, "request", {
635
+ value: this.originalHttpRequest,
636
+ writable: true,
637
+ configurable: true
638
+ });
639
+ }
640
+ if (this.originalHttpsRequest) {
641
+ Object.defineProperty(https, "request", {
642
+ value: this.originalHttpsRequest,
643
+ writable: true,
644
+ configurable: true
645
+ });
646
+ }
647
+ } catch {
648
+ }
649
+ this.originalHttpRequest = null;
650
+ this.originalHttpsRequest = null;
651
+ this.started = false;
652
+ }
653
+ /**
654
+ * Wrap a module's request method
655
+ */
656
+ wrapRequestMethod(mod, _protocol) {
657
+ const originalRequest = mod.request.bind(mod);
658
+ const self = this;
659
+ const wrappedRequest = function(...args) {
660
+ const [urlOrOptions, optionsOrCallback, maybeCallback] = args;
661
+ const { url, method, hostname: hostname3 } = self.parseRequestArgs(
662
+ urlOrOptions,
663
+ optionsOrCallback
664
+ );
665
+ const startTime = Date.now();
666
+ const req = originalRequest.apply(mod, args);
667
+ req.on("response", (res) => {
668
+ const duration = Date.now() - startTime;
669
+ self.addBreadcrumb(method, url, hostname3, res.statusCode, duration);
670
+ });
671
+ req.on("error", (error) => {
672
+ const duration = Date.now() - startTime;
673
+ self.addErrorBreadcrumb(method, url, hostname3, error, duration);
674
+ });
675
+ return req;
676
+ };
677
+ Object.defineProperty(mod, "request", {
678
+ value: wrappedRequest,
679
+ writable: true,
680
+ configurable: true
681
+ });
682
+ }
683
+ /**
684
+ * Parse request arguments to extract URL info
685
+ */
686
+ parseRequestArgs(urlOrOptions, optionsOrCallback) {
687
+ let url = "";
688
+ let method = "GET";
689
+ let hostname3 = "";
690
+ if (typeof urlOrOptions === "string") {
691
+ url = urlOrOptions;
692
+ try {
693
+ const parsed = new URL(urlOrOptions);
694
+ hostname3 = parsed.hostname;
695
+ } catch {
696
+ hostname3 = urlOrOptions;
697
+ }
698
+ } else if (urlOrOptions instanceof URL) {
699
+ url = urlOrOptions.toString();
700
+ hostname3 = urlOrOptions.hostname;
701
+ } else if (typeof urlOrOptions === "object") {
702
+ const opts = urlOrOptions;
703
+ hostname3 = opts.hostname || opts.host || "localhost";
704
+ const port = opts.port ? `:${opts.port}` : "";
705
+ const path2 = opts.path || "/";
706
+ const protocol = opts.protocol || "http:";
707
+ url = `${protocol}//${hostname3}${port}${path2}`;
708
+ method = opts.method || "GET";
709
+ }
710
+ if (optionsOrCallback && typeof optionsOrCallback === "object" && optionsOrCallback.method) {
711
+ method = optionsOrCallback.method;
712
+ }
713
+ return { url, method: method.toUpperCase(), hostname: hostname3 };
714
+ }
715
+ /**
716
+ * Add a breadcrumb for a successful HTTP request
717
+ */
718
+ addBreadcrumb(method, url, hostname3, statusCode, duration) {
719
+ const manager = getBreadcrumbManager();
720
+ if (!manager) {
721
+ return;
722
+ }
723
+ manager.add({
724
+ type: "http",
725
+ category: "http.client",
726
+ message: `${method} ${url}`,
727
+ level: statusCode && statusCode >= 400 ? "warning" : "info",
728
+ data: {
729
+ method,
730
+ url: this.truncateUrl(url),
731
+ hostname: hostname3,
732
+ status_code: statusCode,
733
+ duration_ms: duration
734
+ }
735
+ });
736
+ }
737
+ /**
738
+ * Add a breadcrumb for a failed HTTP request
739
+ */
740
+ addErrorBreadcrumb(method, url, hostname3, error, duration) {
741
+ const manager = getBreadcrumbManager();
742
+ if (!manager) {
743
+ return;
744
+ }
745
+ manager.add({
746
+ type: "http",
747
+ category: "http.client",
748
+ message: `${method} ${url} failed: ${error.message}`,
749
+ level: "error",
750
+ data: {
751
+ method,
752
+ url: this.truncateUrl(url),
753
+ hostname: hostname3,
754
+ error: error.message,
755
+ duration_ms: duration
756
+ }
757
+ });
758
+ }
759
+ /**
760
+ * Truncate URL for storage
761
+ */
762
+ truncateUrl(url) {
763
+ const maxLength = 200;
764
+ if (url.length <= maxLength) {
765
+ return url;
766
+ }
767
+ return url.substring(0, maxLength) + "...";
768
+ }
769
+ /**
770
+ * Reset tracker state
771
+ */
772
+ reset() {
773
+ this.stop();
774
+ this.patchFailed = false;
775
+ }
776
+ };
777
+ var httpTracker = null;
778
+ function getHttpTracker() {
779
+ if (!httpTracker) {
780
+ httpTracker = new HttpTracker();
781
+ }
782
+ return httpTracker;
783
+ }
784
+ function resetHttpTracker() {
785
+ if (httpTracker) {
786
+ httpTracker.reset();
787
+ }
788
+ httpTracker = null;
789
+ }
790
+
791
+ // src/capture/ProcessCapture.ts
792
+ var ProcessCapture = class {
793
+ handler = null;
794
+ uncaughtHandler = null;
795
+ rejectionHandler = null;
796
+ started = false;
797
+ exitOnUncaught = true;
798
+ /**
799
+ * Start capturing process errors
800
+ */
801
+ start(handler, exitOnUncaught = true) {
802
+ if (this.started) {
803
+ return;
804
+ }
805
+ this.handler = handler;
806
+ this.exitOnUncaught = exitOnUncaught;
807
+ this.uncaughtHandler = this.handleUncaughtException.bind(this);
808
+ this.rejectionHandler = this.handleUnhandledRejection.bind(this);
809
+ process.on("uncaughtException", this.uncaughtHandler);
810
+ process.on("unhandledRejection", this.rejectionHandler);
811
+ this.started = true;
812
+ }
813
+ /**
814
+ * Start only uncaughtException capture
815
+ */
816
+ startUncaughtException(handler, exitOnUncaught = true) {
817
+ if (this.uncaughtHandler) {
818
+ return;
819
+ }
820
+ this.handler = handler;
821
+ this.exitOnUncaught = exitOnUncaught;
822
+ this.uncaughtHandler = this.handleUncaughtException.bind(this);
823
+ process.on("uncaughtException", this.uncaughtHandler);
824
+ }
825
+ /**
826
+ * Start only unhandledRejection capture
827
+ */
828
+ startUnhandledRejection(handler) {
829
+ if (this.rejectionHandler) {
830
+ return;
831
+ }
832
+ this.handler = handler;
833
+ this.rejectionHandler = this.handleUnhandledRejection.bind(this);
834
+ process.on("unhandledRejection", this.rejectionHandler);
835
+ }
836
+ /**
837
+ * Stop capturing process errors
838
+ */
839
+ stop() {
840
+ if (this.uncaughtHandler) {
841
+ process.off("uncaughtException", this.uncaughtHandler);
842
+ this.uncaughtHandler = null;
843
+ }
844
+ if (this.rejectionHandler) {
845
+ process.off("unhandledRejection", this.rejectionHandler);
846
+ this.rejectionHandler = null;
847
+ }
848
+ this.handler = null;
849
+ this.started = false;
850
+ }
851
+ /**
852
+ * Handle uncaught exception
853
+ */
854
+ handleUncaughtException(error) {
855
+ if (!this.handler) {
856
+ return;
857
+ }
858
+ const captured = {
859
+ error,
860
+ message: error.message,
861
+ type: "uncaughtException",
862
+ severity: "critical"
863
+ };
864
+ try {
865
+ this.handler(captured);
866
+ } catch {
867
+ }
868
+ if (this.exitOnUncaught) {
869
+ setTimeout(() => {
870
+ process.exit(1);
871
+ }, 100);
872
+ }
873
+ }
874
+ /**
875
+ * Handle unhandled rejection
876
+ */
877
+ handleUnhandledRejection(reason, promise) {
878
+ if (!this.handler) {
879
+ return;
880
+ }
881
+ const error = reason instanceof Error ? reason : new Error(String(reason));
882
+ const captured = {
883
+ error,
884
+ message: error.message,
885
+ type: "unhandledRejection",
886
+ severity: "error",
887
+ promise
888
+ };
889
+ try {
890
+ this.handler(captured);
891
+ } catch {
892
+ }
893
+ }
894
+ /**
895
+ * Reset capture state
896
+ */
897
+ reset() {
898
+ this.stop();
899
+ }
900
+ };
901
+ var processCapture = null;
902
+ function getProcessCapture() {
903
+ if (!processCapture) {
904
+ processCapture = new ProcessCapture();
905
+ }
906
+ return processCapture;
907
+ }
908
+ function resetProcessCapture() {
909
+ if (processCapture) {
910
+ processCapture.reset();
911
+ }
912
+ processCapture = null;
913
+ }
914
+
915
+ // src/capture/ConsoleCapture.ts
916
+ var ConsoleCapture = class {
917
+ handler = null;
918
+ originalConsoleError = null;
919
+ started = false;
920
+ /**
921
+ * Start capturing console.error calls
922
+ */
923
+ start(handler) {
924
+ if (this.started) {
925
+ return;
926
+ }
927
+ this.handler = handler;
928
+ this.originalConsoleError = console.error.bind(console);
929
+ const self = this;
930
+ console.error = function(...args) {
931
+ self.originalConsoleError(...args);
932
+ self.handleConsoleError(args);
933
+ };
934
+ this.started = true;
935
+ }
936
+ /**
937
+ * Stop capturing console.error
938
+ */
939
+ stop() {
940
+ if (!this.started || !this.originalConsoleError) {
941
+ return;
942
+ }
943
+ console.error = this.originalConsoleError;
944
+ this.originalConsoleError = null;
945
+ this.handler = null;
946
+ this.started = false;
947
+ }
948
+ /**
949
+ * Handle console.error call
950
+ */
951
+ handleConsoleError(args) {
952
+ if (!this.handler) {
953
+ return;
954
+ }
955
+ let error;
956
+ let message;
957
+ const firstArg = args[0];
958
+ if (firstArg instanceof Error) {
959
+ error = firstArg;
960
+ message = error.message;
961
+ } else {
962
+ message = args.map((arg) => {
963
+ if (typeof arg === "string") return arg;
964
+ try {
965
+ return JSON.stringify(arg);
966
+ } catch {
967
+ return String(arg);
968
+ }
969
+ }).join(" ");
970
+ error = new Error(message);
971
+ }
972
+ const captured = {
973
+ error,
974
+ message,
975
+ type: "unhandledRejection",
976
+ // Use this type for console errors
977
+ severity: "error"
978
+ };
979
+ try {
980
+ this.handler(captured);
981
+ } catch {
982
+ }
983
+ }
984
+ /**
985
+ * Reset capture state
986
+ */
987
+ reset() {
988
+ this.stop();
989
+ }
990
+ };
991
+ var consoleCapture = null;
992
+ function getConsoleCapture() {
993
+ if (!consoleCapture) {
994
+ consoleCapture = new ConsoleCapture();
995
+ }
996
+ return consoleCapture;
997
+ }
998
+ function resetConsoleCapture() {
999
+ if (consoleCapture) {
1000
+ consoleCapture.reset();
1001
+ }
1002
+ consoleCapture = null;
1003
+ }
1004
+ var HmacSigner = class {
1005
+ secret;
1006
+ constructor(secret) {
1007
+ this.secret = secret;
1008
+ }
1009
+ /**
1010
+ * Sign a payload with HMAC-SHA256
1011
+ * @param payload - The JSON payload string to sign
1012
+ * @param timestamp - Unix timestamp (defaults to current time)
1013
+ * @returns Hex-encoded signature
1014
+ */
1015
+ sign(payload, timestamp) {
1016
+ const ts = timestamp ?? Math.floor(Date.now() / 1e3);
1017
+ const signedPayload = `${ts}.${payload}`;
1018
+ const hmac = crypto.createHmac("sha256", this.secret);
1019
+ hmac.update(signedPayload);
1020
+ return hmac.digest("hex");
1021
+ }
1022
+ /**
1023
+ * Build headers for a signed request
1024
+ * @param payload - The JSON payload string
1025
+ * @returns Headers object with signature and timestamp
1026
+ */
1027
+ buildHeaders(payload) {
1028
+ const timestamp = Math.floor(Date.now() / 1e3);
1029
+ const signature = this.sign(payload, timestamp);
1030
+ return {
1031
+ "X-Webhook-Signature": signature,
1032
+ "X-Webhook-Timestamp": String(timestamp)
1033
+ };
1034
+ }
1035
+ /**
1036
+ * Verify a signature (useful for testing)
1037
+ * @param payload - The payload that was signed
1038
+ * @param signature - The signature to verify
1039
+ * @param timestamp - The timestamp used in signing
1040
+ * @param maxAge - Maximum age in seconds (default: 5 minutes)
1041
+ */
1042
+ verify(payload, signature, timestamp, maxAge = 300) {
1043
+ const now = Math.floor(Date.now() / 1e3);
1044
+ if (Math.abs(now - timestamp) > maxAge) {
1045
+ return false;
1046
+ }
1047
+ const expected = this.sign(payload, timestamp);
1048
+ return crypto.timingSafeEqual(
1049
+ Buffer.from(signature, "hex"),
1050
+ Buffer.from(expected, "hex")
1051
+ );
1052
+ }
1053
+ };
1054
+
1055
+ // src/transport/HttpTransport.ts
1056
+ var HttpTransport = class {
1057
+ endpoint;
1058
+ token;
1059
+ timeout;
1060
+ maxRetries;
1061
+ hmacSigner = null;
1062
+ debug;
1063
+ constructor(options) {
1064
+ this.endpoint = options.endpoint;
1065
+ this.token = options.token;
1066
+ this.timeout = options.timeout;
1067
+ this.maxRetries = options.maxRetries;
1068
+ this.debug = options.debug ?? false;
1069
+ if (options.hmacSecret) {
1070
+ this.hmacSigner = new HmacSigner(options.hmacSecret);
1071
+ }
1072
+ }
1073
+ /**
1074
+ * Send an event to the Error Explorer API
1075
+ */
1076
+ async send(event) {
1077
+ const payload = JSON.stringify(event);
1078
+ let lastError = null;
1079
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
1080
+ try {
1081
+ const success = await this.doSend(payload);
1082
+ if (success) {
1083
+ if (this.debug) {
1084
+ console.log("[ErrorExplorer] Event sent successfully");
1085
+ }
1086
+ return true;
1087
+ }
1088
+ } catch (error) {
1089
+ lastError = error;
1090
+ if (this.debug) {
1091
+ console.error(`[ErrorExplorer] Send attempt ${attempt + 1} failed:`, error);
1092
+ }
1093
+ if (attempt < this.maxRetries) {
1094
+ await this.sleep(Math.pow(2, attempt) * 100);
1095
+ }
1096
+ }
1097
+ }
1098
+ if (this.debug && lastError) {
1099
+ console.error("[ErrorExplorer] All send attempts failed:", lastError);
1100
+ }
1101
+ return false;
1102
+ }
1103
+ /**
1104
+ * Perform the actual HTTP request
1105
+ */
1106
+ doSend(payload) {
1107
+ return new Promise((resolve, reject) => {
1108
+ const url = new URL(this.endpoint);
1109
+ const isHttps = url.protocol === "https:";
1110
+ const transport = isHttps ? https : http;
1111
+ const headers = {
1112
+ "Content-Type": "application/json",
1113
+ "Content-Length": String(Buffer.byteLength(payload)),
1114
+ "X-Webhook-Token": this.token,
1115
+ "User-Agent": "@error-explorer/node/1.0.0",
1116
+ // Include Host header for proper virtual host routing
1117
+ "Host": url.host
1118
+ };
1119
+ if (this.hmacSigner) {
1120
+ const hmacHeaders = this.hmacSigner.buildHeaders(payload);
1121
+ Object.assign(headers, hmacHeaders);
1122
+ }
1123
+ const options = {
1124
+ method: "POST",
1125
+ hostname: url.hostname,
1126
+ port: url.port || (isHttps ? 443 : 80),
1127
+ path: url.pathname,
1128
+ headers,
1129
+ timeout: this.timeout
1130
+ };
1131
+ const req = transport.request(options, (res) => {
1132
+ let data = "";
1133
+ res.on("data", (chunk) => {
1134
+ data += chunk;
1135
+ });
1136
+ res.on("end", () => {
1137
+ const statusCode = res.statusCode || 0;
1138
+ if (statusCode >= 200 && statusCode < 300) {
1139
+ resolve(true);
1140
+ } else if (statusCode === 429) {
1141
+ const retryAfter = res.headers["retry-after"];
1142
+ reject(new Error(`Rate limited. Retry after: ${retryAfter}`));
1143
+ } else if (statusCode >= 500) {
1144
+ reject(new Error(`Server error: ${statusCode}`));
1145
+ } else {
1146
+ if (this.debug) {
1147
+ console.error(`[ErrorExplorer] Client error ${statusCode}:`, data);
1148
+ }
1149
+ resolve(false);
1150
+ }
1151
+ });
1152
+ });
1153
+ req.on("error", (error) => {
1154
+ reject(error);
1155
+ });
1156
+ req.on("timeout", () => {
1157
+ req.destroy();
1158
+ reject(new Error("Request timeout"));
1159
+ });
1160
+ req.write(payload);
1161
+ req.end();
1162
+ });
1163
+ }
1164
+ /**
1165
+ * Send synchronously (blocking) - for use in exit handlers
1166
+ */
1167
+ sendSync(event) {
1168
+ this.send(event).catch((err) => {
1169
+ if (this.debug) {
1170
+ console.error("[ErrorExplorer] Sync send failed:", err);
1171
+ }
1172
+ });
1173
+ }
1174
+ /**
1175
+ * Sleep for a given duration
1176
+ */
1177
+ sleep(ms) {
1178
+ return new Promise((resolve) => setTimeout(resolve, ms));
1179
+ }
1180
+ };
1181
+
1182
+ // src/ErrorExplorer.ts
1183
+ var SDK_NAME = "@error-explorer/node";
1184
+ var SDK_VERSION = "1.0.0";
1185
+ var ErrorExplorerClient = class {
1186
+ config = null;
1187
+ transport = null;
1188
+ initialized = false;
1189
+ tags = {};
1190
+ extra = {};
1191
+ contexts = {};
1192
+ /**
1193
+ * Initialize the SDK
1194
+ */
1195
+ init(options) {
1196
+ if (this.initialized) {
1197
+ console.warn("[ErrorExplorer] Already initialized");
1198
+ return;
1199
+ }
1200
+ try {
1201
+ this.config = resolveConfig(options);
1202
+ if (this.config.serverName) {
1203
+ setServerName(this.config.serverName);
1204
+ }
1205
+ this.transport = new HttpTransport({
1206
+ endpoint: this.config.endpoint,
1207
+ token: this.config.token,
1208
+ timeout: this.config.timeout,
1209
+ maxRetries: this.config.maxRetries,
1210
+ hmacSecret: this.config.hmacSecret,
1211
+ debug: this.config.debug
1212
+ });
1213
+ if (this.config.breadcrumbs.enabled) {
1214
+ initBreadcrumbManager(this.config);
1215
+ this.startBreadcrumbTrackers();
1216
+ }
1217
+ if (this.config.autoCapture.uncaughtExceptions || this.config.autoCapture.unhandledRejections) {
1218
+ const processCapture2 = getProcessCapture();
1219
+ if (this.config.autoCapture.uncaughtExceptions) {
1220
+ processCapture2.startUncaughtException(
1221
+ this.handleCapturedError.bind(this),
1222
+ this.config.exitOnUncaughtException
1223
+ );
1224
+ }
1225
+ if (this.config.autoCapture.unhandledRejections) {
1226
+ processCapture2.startUnhandledRejection(this.handleCapturedError.bind(this));
1227
+ }
1228
+ }
1229
+ if (this.config.autoCapture.console) {
1230
+ getConsoleCapture().start(this.handleCapturedError.bind(this));
1231
+ }
1232
+ this.initialized = true;
1233
+ if (this.config.debug) {
1234
+ console.log("[ErrorExplorer] Initialized", {
1235
+ endpoint: this.config.endpoint,
1236
+ environment: this.config.environment,
1237
+ serverName: this.config.serverName
1238
+ });
1239
+ }
1240
+ } catch (error) {
1241
+ console.error("[ErrorExplorer] Initialization failed:", error);
1242
+ throw error;
1243
+ }
1244
+ }
1245
+ /**
1246
+ * Check if SDK is initialized
1247
+ */
1248
+ isInitialized() {
1249
+ return this.initialized;
1250
+ }
1251
+ /**
1252
+ * Set user context
1253
+ */
1254
+ setUser(user) {
1255
+ getUserContextManager().setUser(user);
1256
+ }
1257
+ /**
1258
+ * Clear user context
1259
+ */
1260
+ clearUser() {
1261
+ getUserContextManager().clearUser();
1262
+ }
1263
+ /**
1264
+ * Set a single tag
1265
+ */
1266
+ setTag(key, value) {
1267
+ this.tags[key] = value;
1268
+ }
1269
+ /**
1270
+ * Set multiple tags
1271
+ */
1272
+ setTags(tags) {
1273
+ this.tags = { ...this.tags, ...tags };
1274
+ }
1275
+ /**
1276
+ * Set extra data
1277
+ */
1278
+ setExtra(key, value) {
1279
+ this.extra[key] = value;
1280
+ }
1281
+ /**
1282
+ * Set a named context
1283
+ */
1284
+ setContext(name, context) {
1285
+ this.contexts[name] = context;
1286
+ }
1287
+ /**
1288
+ * Add a manual breadcrumb
1289
+ */
1290
+ addBreadcrumb(breadcrumb) {
1291
+ const manager = getBreadcrumbManager();
1292
+ if (manager) {
1293
+ manager.add(breadcrumb);
1294
+ }
1295
+ }
1296
+ /**
1297
+ * Capture an exception manually
1298
+ */
1299
+ captureException(error, context) {
1300
+ if (!this.initialized || !this.config) {
1301
+ console.warn("[ErrorExplorer] Not initialized");
1302
+ return "";
1303
+ }
1304
+ const eventId = generateUuid();
1305
+ const event = this.buildEvent(error, context, "error");
1306
+ this.processAndSend(event);
1307
+ return eventId;
1308
+ }
1309
+ /**
1310
+ * Capture a message manually
1311
+ */
1312
+ captureMessage(message, level = "info") {
1313
+ if (!this.initialized || !this.config) {
1314
+ console.warn("[ErrorExplorer] Not initialized");
1315
+ return "";
1316
+ }
1317
+ const eventId = generateUuid();
1318
+ const event = this.buildEvent(new Error(message), void 0, level);
1319
+ event.exception_class = "Message";
1320
+ this.processAndSend(event);
1321
+ return eventId;
1322
+ }
1323
+ /**
1324
+ * Flush all pending events (best effort)
1325
+ */
1326
+ async flush(timeout = 5e3) {
1327
+ if (!this.initialized) {
1328
+ return false;
1329
+ }
1330
+ await new Promise((resolve) => setTimeout(resolve, Math.min(timeout, 100)));
1331
+ return true;
1332
+ }
1333
+ /**
1334
+ * Close the SDK and cleanup
1335
+ */
1336
+ async close(timeout = 5e3) {
1337
+ if (!this.initialized) {
1338
+ return true;
1339
+ }
1340
+ await this.flush(timeout);
1341
+ this.stopAllTrackers();
1342
+ resetBreadcrumbManager();
1343
+ resetProcessCapture();
1344
+ resetConsoleCapture();
1345
+ resetConsoleTracker();
1346
+ resetHttpTracker();
1347
+ resetUserContextManager();
1348
+ resetServerContext();
1349
+ resetOsContext();
1350
+ resetRuntimeContext();
1351
+ this.config = null;
1352
+ this.transport = null;
1353
+ this.initialized = false;
1354
+ this.tags = {};
1355
+ this.extra = {};
1356
+ this.contexts = {};
1357
+ return true;
1358
+ }
1359
+ /**
1360
+ * Handle a captured error from auto-capture
1361
+ */
1362
+ handleCapturedError(captured) {
1363
+ if (!this.config) {
1364
+ return;
1365
+ }
1366
+ if (this.shouldIgnoreError(captured.message)) {
1367
+ if (this.config.debug) {
1368
+ console.log("[ErrorExplorer] Ignoring error:", captured.message);
1369
+ }
1370
+ return;
1371
+ }
1372
+ const event = this.buildEvent(captured.error, void 0, captured.severity);
1373
+ event.tags = {
1374
+ ...event.tags,
1375
+ capture_type: captured.type
1376
+ };
1377
+ this.processAndSend(event);
1378
+ }
1379
+ /**
1380
+ * Check if an error should be ignored
1381
+ */
1382
+ shouldIgnoreError(message) {
1383
+ if (!this.config) {
1384
+ return true;
1385
+ }
1386
+ return matchesPattern(message, this.config.ignoreErrors);
1387
+ }
1388
+ /**
1389
+ * Build a complete error event
1390
+ */
1391
+ buildEvent(error, context, severity = "error") {
1392
+ const config = this.config;
1393
+ const message = getErrorMessage(error);
1394
+ const name = getErrorName(error);
1395
+ const stack = getErrorStack(error);
1396
+ const frames = parseStackTrace(stack);
1397
+ const topFrame = frames[0];
1398
+ const event = {
1399
+ message,
1400
+ exception_class: name,
1401
+ file: topFrame?.filename,
1402
+ line: topFrame?.lineno,
1403
+ column: topFrame?.colno,
1404
+ stack_trace: stack,
1405
+ frames,
1406
+ severity: context?.level ?? severity,
1407
+ environment: config.environment,
1408
+ release: config.release || void 0,
1409
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1410
+ // User context
1411
+ user: context?.user ?? getUserContextManager().getUser() ?? void 0,
1412
+ // Server context
1413
+ server: getServerContext(),
1414
+ // Runtime context
1415
+ runtime: collectRuntimeContext(),
1416
+ // OS context
1417
+ os: collectOsContext(),
1418
+ // Process context
1419
+ process: collectProcessContext(),
1420
+ // Request context (if provided)
1421
+ request: context?.request ? extractRequestContext(context.request) : void 0,
1422
+ // Breadcrumbs
1423
+ breadcrumbs: getBreadcrumbManager()?.getAll(),
1424
+ // Tags (merge global + context)
1425
+ tags: { ...this.tags, ...context?.tags },
1426
+ // Extra data
1427
+ extra: { ...this.extra, ...context?.extra },
1428
+ // Custom contexts
1429
+ contexts: this.contexts,
1430
+ // SDK info
1431
+ sdk: {
1432
+ name: SDK_NAME,
1433
+ version: SDK_VERSION
1434
+ },
1435
+ // Fingerprint for grouping
1436
+ fingerprint: context?.fingerprint
1437
+ };
1438
+ return event;
1439
+ }
1440
+ /**
1441
+ * Process event through beforeSend and send
1442
+ */
1443
+ async processAndSend(event) {
1444
+ if (!this.config || !this.transport) {
1445
+ return;
1446
+ }
1447
+ let processedEvent = event;
1448
+ if (this.config.beforeSend) {
1449
+ try {
1450
+ const result = this.config.beforeSend(event);
1451
+ processedEvent = result instanceof Promise ? await result : result;
1452
+ } catch (e) {
1453
+ console.error("[ErrorExplorer] beforeSend threw an error:", e);
1454
+ processedEvent = event;
1455
+ }
1456
+ }
1457
+ if (!processedEvent) {
1458
+ if (this.config.debug) {
1459
+ console.log("[ErrorExplorer] Event dropped by beforeSend");
1460
+ }
1461
+ return;
1462
+ }
1463
+ await this.transport.send(processedEvent);
1464
+ }
1465
+ /**
1466
+ * Start all breadcrumb trackers
1467
+ */
1468
+ startBreadcrumbTrackers() {
1469
+ if (!this.config) {
1470
+ return;
1471
+ }
1472
+ const bc = this.config.breadcrumbs;
1473
+ if (bc.console) {
1474
+ getConsoleTracker().start();
1475
+ }
1476
+ if (bc.http) {
1477
+ getHttpTracker().start();
1478
+ }
1479
+ }
1480
+ /**
1481
+ * Stop all breadcrumb trackers
1482
+ */
1483
+ stopAllTrackers() {
1484
+ resetConsoleTracker();
1485
+ resetHttpTracker();
1486
+ }
1487
+ };
1488
+ var ErrorExplorer = new ErrorExplorerClient();
1489
+
1490
+ // src/middleware/express.ts
1491
+ function requestHandler(options = {}) {
1492
+ const { extractUser, breadcrumbs = true } = options;
1493
+ return (req, res, next) => {
1494
+ const transaction = generateTransactionId();
1495
+ req.errorExplorer = {
1496
+ transaction,
1497
+ startTime: Date.now()
1498
+ };
1499
+ if (extractUser) {
1500
+ try {
1501
+ const user = extractUser(req);
1502
+ if (user) {
1503
+ req.errorExplorer.user = user;
1504
+ }
1505
+ } catch {
1506
+ }
1507
+ }
1508
+ if (breadcrumbs) {
1509
+ const manager = getBreadcrumbManager();
1510
+ if (manager) {
1511
+ manager.add({
1512
+ type: "http",
1513
+ category: "http.request",
1514
+ message: `${req.method} ${req.path || req.url}`,
1515
+ level: "info",
1516
+ data: {
1517
+ method: req.method,
1518
+ url: req.originalUrl || req.url,
1519
+ transaction
1520
+ }
1521
+ });
1522
+ }
1523
+ }
1524
+ if (breadcrumbs) {
1525
+ const originalEnd = res.end;
1526
+ let ended = false;
1527
+ res.end = function(...args) {
1528
+ if (!ended) {
1529
+ ended = true;
1530
+ const duration = req.errorExplorer?.startTime ? Date.now() - req.errorExplorer.startTime : void 0;
1531
+ const manager = getBreadcrumbManager();
1532
+ if (manager) {
1533
+ manager.add({
1534
+ type: "http",
1535
+ category: "http.response",
1536
+ message: `${req.method} ${req.path || req.url} - ${res.statusCode}`,
1537
+ level: res.statusCode >= 400 ? "warning" : "info",
1538
+ data: {
1539
+ method: req.method,
1540
+ url: req.originalUrl || req.url,
1541
+ status_code: res.statusCode,
1542
+ duration_ms: duration,
1543
+ transaction
1544
+ }
1545
+ });
1546
+ }
1547
+ }
1548
+ return originalEnd.apply(res, args);
1549
+ };
1550
+ }
1551
+ next();
1552
+ };
1553
+ }
1554
+ function errorHandler(options = {}) {
1555
+ const { callNext = true, onError } = options;
1556
+ return (error, req, res, next) => {
1557
+ if (res.headersSent) {
1558
+ if (callNext) {
1559
+ next(error);
1560
+ }
1561
+ return;
1562
+ }
1563
+ const requestContext = extractRequestContext(req);
1564
+ const user = req.errorExplorer?.user;
1565
+ const transaction = req.errorExplorer?.transaction;
1566
+ ErrorExplorer.captureException(error, {
1567
+ request: req,
1568
+ user,
1569
+ tags: {
1570
+ transaction: transaction || "unknown",
1571
+ route: req.path || req.url || "unknown",
1572
+ method: req.method || "unknown"
1573
+ },
1574
+ extra: {
1575
+ requestContext,
1576
+ query: req.query,
1577
+ params: req.params
1578
+ }
1579
+ });
1580
+ if (onError) {
1581
+ try {
1582
+ onError(error, req, res);
1583
+ } catch {
1584
+ }
1585
+ }
1586
+ if (callNext) {
1587
+ next(error);
1588
+ }
1589
+ };
1590
+ }
1591
+ function setupExpress(requestOptions = {}, errorOptions = {}) {
1592
+ return {
1593
+ requestHandler: requestHandler(requestOptions),
1594
+ errorHandler: errorHandler(errorOptions)
1595
+ };
1596
+ }
1597
+
1598
+ // src/middleware/http.ts
1599
+ function wrapHandler(handler, options = {}) {
1600
+ const { extractUser, breadcrumbs = true } = options;
1601
+ return async (req, res) => {
1602
+ const transaction = generateTransactionId();
1603
+ const startTime = Date.now();
1604
+ if (breadcrumbs) {
1605
+ const manager = getBreadcrumbManager();
1606
+ if (manager) {
1607
+ manager.add({
1608
+ type: "http",
1609
+ category: "http.request",
1610
+ message: `${req.method} ${req.url}`,
1611
+ level: "info",
1612
+ data: {
1613
+ method: req.method,
1614
+ url: req.url,
1615
+ transaction
1616
+ }
1617
+ });
1618
+ }
1619
+ }
1620
+ let user;
1621
+ if (extractUser) {
1622
+ try {
1623
+ user = extractUser(req);
1624
+ } catch {
1625
+ }
1626
+ }
1627
+ try {
1628
+ await handler(req, res);
1629
+ } catch (error) {
1630
+ ErrorExplorer.captureException(error, {
1631
+ request: req,
1632
+ user: user || void 0,
1633
+ tags: {
1634
+ transaction,
1635
+ method: req.method || "unknown"
1636
+ },
1637
+ extra: {
1638
+ requestContext: extractRequestContext(req)
1639
+ }
1640
+ });
1641
+ throw error;
1642
+ } finally {
1643
+ if (breadcrumbs) {
1644
+ const duration = Date.now() - startTime;
1645
+ const manager = getBreadcrumbManager();
1646
+ if (manager) {
1647
+ manager.add({
1648
+ type: "http",
1649
+ category: "http.response",
1650
+ message: `${req.method} ${req.url} - ${res.statusCode}`,
1651
+ level: res.statusCode >= 400 ? "warning" : "info",
1652
+ data: {
1653
+ method: req.method,
1654
+ url: req.url,
1655
+ status_code: res.statusCode,
1656
+ duration_ms: duration,
1657
+ transaction
1658
+ }
1659
+ });
1660
+ }
1661
+ }
1662
+ }
1663
+ };
1664
+ }
1665
+ function createTrackedServer(handler, options = {}) {
1666
+ return wrapHandler(handler, options);
1667
+ }
1668
+
1669
+ // src/index.ts
1670
+ var src_default = ErrorExplorer;
1671
+
1672
+ export { ErrorExplorer, HmacSigner, collectOsContext, collectProcessContext, collectRuntimeContext, createTrackedServer, src_default as default, errorHandler, extractRequestContext, generateShortId, generateTransactionId, generateUuid, getBreadcrumbManager, getServerContext, parseStackTrace, requestHandler, setServerName, setupExpress, wrapHandler };
1673
+ //# sourceMappingURL=index.js.map
1674
+ //# sourceMappingURL=index.js.map