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