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