@honeybadger-io/js 6.15.2 → 6.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,8 @@ var require$$1$1 = require('domain');
5
5
  var require$$1 = require('fs');
6
6
  var require$$2 = require('path');
7
7
  var require$$3 = require('util');
8
- var require$$0$3 = require('url');
8
+ var require$$0$3 = require('crypto');
9
+ var require$$0$4 = require('url');
9
10
  var require$$3$1 = require('http');
10
11
  var require$$4 = require('https');
11
12
 
@@ -17,6 +18,7 @@ var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1);
17
18
  var require$$2__default = /*#__PURE__*/_interopDefaultLegacy(require$$2);
18
19
  var require$$3__default = /*#__PURE__*/_interopDefaultLegacy(require$$3);
19
20
  var require$$0__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$0$3);
21
+ var require$$0__default$2 = /*#__PURE__*/_interopDefaultLegacy(require$$0$4);
20
22
  var require$$3__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$3$1);
21
23
  var require$$4__default = /*#__PURE__*/_interopDefaultLegacy(require$$4);
22
24
 
@@ -51,7 +53,7 @@ var server$1 = {};
51
53
 
52
54
  var src = {};
53
55
 
54
- var events = {};
56
+ var console_events = {};
55
57
 
56
58
  var util$1 = {};
57
59
 
@@ -258,7 +260,7 @@ var require$$0$1 = /*@__PURE__*/getAugmentedNamespace(stackTraceParser_esm);
258
260
  }
259
261
  };
260
262
  Object.defineProperty(exports, "__esModule", { value: true });
261
- exports.logDeprecatedMethod = exports.globalThisOrWindow = exports.isBrowserConfig = exports.clone = exports.formatCGIData = exports.filterUrl = exports.filter = exports.generateStackTrace = exports.endpoint = exports.instrumentConsole = exports.instrument = exports.isErrorObject = exports.makeNotice = exports.logger = exports.sanitize = exports.shallowClone = exports.runAfterNotifyHandlers = exports.runBeforeNotifyHandlers = exports.getSourceForBacktrace = exports.getCauses = exports.calculateBacktraceShift = exports.DEFAULT_BACKTRACE_SHIFT = exports.makeBacktrace = exports.objectIsExtensible = exports.objectIsEmpty = exports.mergeNotice = exports.merge = void 0;
263
+ exports.logDeprecatedMethod = exports.globalThisOrWindow = exports.isBrowserConfig = exports.clone = exports.formatCGIData = exports.filterUrl = exports.filter = exports.generateStackTrace = exports.endpoint = exports.instrumentConsole = exports.instrument = exports.isErrorObject = exports.makeNotice = exports.logger = exports.sanitize = exports.shallowClone = exports.runAfterNotifyHandlers = exports.shouldSampleEvent = exports.resolveInsights = exports.runBeforeEventHandlers = exports.runBeforeNotifyHandlers = exports.getSourceForBacktrace = exports.getCauses = exports.calculateBacktraceShift = exports.DEFAULT_BACKTRACE_SHIFT = exports.makeBacktrace = exports.objectIsExtensible = exports.objectIsEmpty = exports.mergeNotice = exports.merge = void 0;
262
264
  /* eslint-disable prefer-rest-params */
263
265
  var stackTraceParser = __importStar(require$$0$1);
264
266
  function merge(obj1, obj2) {
@@ -434,6 +436,88 @@ var require$$0$1 = /*@__PURE__*/getAugmentedNamespace(stackTraceParser_esm);
434
436
  };
435
437
  }
436
438
  exports.runBeforeNotifyHandlers = runBeforeNotifyHandlers;
439
+ function runBeforeEventHandlers(payload, handlers, logger) {
440
+ return __awaiter(this, void 0, void 0, function () {
441
+ var i, len, result, err_1;
442
+ return __generator(this, function (_a) {
443
+ switch (_a.label) {
444
+ case 0:
445
+ i = 0, len = handlers.length;
446
+ _a.label = 1;
447
+ case 1:
448
+ if (!(i < len)) return [3 /*break*/, 7];
449
+ result = void 0;
450
+ _a.label = 2;
451
+ case 2:
452
+ _a.trys.push([2, 4, , 5]);
453
+ return [4 /*yield*/, handlers[i](payload)];
454
+ case 3:
455
+ result = _a.sent();
456
+ return [3 /*break*/, 5];
457
+ case 4:
458
+ err_1 = _a.sent();
459
+ // A buggy handler should not suppress unrelated events. Log and treat as no-op.
460
+ logger === null || logger === void 0 ? void 0 : logger.error('beforeEvent handler threw; continuing', err_1);
461
+ return [3 /*break*/, 6];
462
+ case 5:
463
+ if (result === false) {
464
+ return [2 /*return*/, false];
465
+ }
466
+ _a.label = 6;
467
+ case 6:
468
+ i++;
469
+ return [3 /*break*/, 1];
470
+ case 7: return [2 /*return*/, true];
471
+ }
472
+ });
473
+ });
474
+ }
475
+ exports.runBeforeEventHandlers = runBeforeEventHandlers;
476
+ function resolveInsights(config) {
477
+ var _a, _b;
478
+ var insights = config.insights;
479
+ if (!insights || insights.enabled !== true) {
480
+ return { console: false, http: false };
481
+ }
482
+ return {
483
+ console: (_a = insights.console) !== null && _a !== void 0 ? _a : false,
484
+ http: (_b = insights.http) !== null && _b !== void 0 ? _b : false,
485
+ };
486
+ }
487
+ exports.resolveInsights = resolveInsights;
488
+ function fnv1a(str) {
489
+ var h = 0x811c9dc5;
490
+ for (var i = 0, len = str.length; i < len; i++) {
491
+ h ^= str.charCodeAt(i);
492
+ h = Math.imul(h, 0x01000193);
493
+ }
494
+ return h >>> 0;
495
+ }
496
+ function clampSampleRate(rate) {
497
+ if (rate < 0)
498
+ return 0;
499
+ if (rate > 100)
500
+ return 100;
501
+ return rate;
502
+ }
503
+ function shouldSampleEvent(event, configRate) {
504
+ var meta = event._hb;
505
+ var override = meta && Number.isFinite(meta.sampleRate) ? meta.sampleRate : undefined;
506
+ var rawRate = override !== undefined ? override : configRate;
507
+ // Guard against non-finite rates (e.g. NaN from Number(invalidConfig)); an
508
+ // unusable rate must not silently drop every event, so fall back to send-all.
509
+ var rate = clampSampleRate(Number.isFinite(rawRate) ? rawRate : 100);
510
+ if (rate <= 0)
511
+ return false;
512
+ if (rate >= 100)
513
+ return true;
514
+ var requestId = event.request_id;
515
+ if (typeof requestId === 'string' && requestId.length > 0) {
516
+ return fnv1a(requestId) % 100 < rate;
517
+ }
518
+ return Math.random() * 100 < rate;
519
+ }
520
+ exports.shouldSampleEvent = shouldSampleEvent;
437
521
  function runAfterNotifyHandlers(notice, handlers, error) {
438
522
  if (notice && notice.afterNotify) {
439
523
  notice.afterNotify(error, notice);
@@ -854,7 +938,7 @@ var require$$0$1 = /*@__PURE__*/getAugmentedNamespace(stackTraceParser_esm);
854
938
 
855
939
  } (util$1));
856
940
 
857
- Object.defineProperty(events, "__esModule", { value: true });
941
+ Object.defineProperty(console_events, "__esModule", { value: true });
858
942
  var util_1$8 = util$1;
859
943
  function default_1$3(_window) {
860
944
  if (_window === void 0) { _window = (0, util_1$8.globalThisOrWindow)(); }
@@ -862,7 +946,7 @@ function default_1$3(_window) {
862
946
  shouldReloadOnConfigure: false,
863
947
  load: function (client) {
864
948
  function sendEventsToInsights() {
865
- return client.config.eventsEnabled;
949
+ return (0, util_1$8.resolveInsights)(client.config).console;
866
950
  }
867
951
  if (!sendEventsToInsights()) {
868
952
  return;
@@ -889,7 +973,7 @@ function default_1$3(_window) {
889
973
  }
890
974
  };
891
975
  }
892
- events.default = default_1$3;
976
+ console_events.default = default_1$3;
893
977
 
894
978
  var client$1 = {};
895
979
 
@@ -916,6 +1000,12 @@ var GlobalStore = /** @class */ (function () {
916
1000
  GlobalStore.prototype.setContext = function (context) {
917
1001
  this.contents.context = (0, util_1$7.merge)(this.contents.context, context || {});
918
1002
  };
1003
+ GlobalStore.prototype.setEventContext = function (eventContext) {
1004
+ this.contents.eventContext = (0, util_1$7.merge)(this.contents.eventContext, eventContext || {});
1005
+ };
1006
+ GlobalStore.prototype.clearEventContext = function () {
1007
+ this.contents.eventContext = {};
1008
+ };
919
1009
  GlobalStore.prototype.addBreadcrumb = function (breadcrumb) {
920
1010
  if (this.contents.breadcrumbs.length == this.breadcrumbsLimit) {
921
1011
  this.contents.breadcrumbs.shift();
@@ -924,6 +1014,7 @@ var GlobalStore = /** @class */ (function () {
924
1014
  };
925
1015
  GlobalStore.prototype.clear = function () {
926
1016
  this.contents.context = {};
1017
+ this.contents.eventContext = {};
927
1018
  this.contents.breadcrumbs = [];
928
1019
  };
929
1020
  GlobalStore.prototype.run = function (callback) {
@@ -933,7 +1024,7 @@ var GlobalStore = /** @class */ (function () {
933
1024
  }());
934
1025
  store.GlobalStore = GlobalStore;
935
1026
 
936
- var throttled_events_logger = {};
1027
+ var throttled_events_worker = {};
937
1028
 
938
1029
  class NdJson {
939
1030
  static parse(data) {
@@ -968,8 +1059,18 @@ defaults.CONFIG = {
968
1059
  revision: null,
969
1060
  reportData: null,
970
1061
  breadcrumbsEnabled: true,
971
- // we could decide the value of eventsEnabled based on `env` and `developmentEnvironments`
1062
+ // deprecated: `eventsEnabled: true` auto-enables insights.enabled + insights.console
972
1063
  eventsEnabled: false,
1064
+ insights: {
1065
+ enabled: false,
1066
+ console: false,
1067
+ http: false,
1068
+ },
1069
+ events: {
1070
+ dispatchIntervalSeconds: 10,
1071
+ bulkThreshold: 500,
1072
+ sampleRatePercentage: 100,
1073
+ },
973
1074
  maxBreadcrumbs: 40,
974
1075
  maxObjectDepth: 8,
975
1076
  logger: console,
@@ -1032,72 +1133,179 @@ var __generator$4 = (commonjsGlobal && commonjsGlobal.__generator) || function (
1032
1133
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
1033
1134
  }
1034
1135
  };
1035
- Object.defineProperty(throttled_events_logger, "__esModule", { value: true });
1036
- throttled_events_logger.ThrottledEventsLogger = void 0;
1136
+ Object.defineProperty(throttled_events_worker, "__esModule", { value: true });
1137
+ throttled_events_worker.ThrottledEventsWorker = void 0;
1037
1138
  var json_nd_1 = require$$0;
1038
1139
  var util_1$6 = util$1;
1039
1140
  var defaults_1$1 = defaults;
1040
- var ThrottledEventsLogger = /** @class */ (function () {
1041
- function ThrottledEventsLogger(config, transport) {
1141
+ var ThrottledEventsWorker = /** @class */ (function () {
1142
+ function ThrottledEventsWorker(config, transport) {
1042
1143
  this.config = config;
1043
1144
  this.transport = transport;
1044
1145
  this.queue = [];
1045
- this.isProcessing = false;
1146
+ this.cooldownTimer = null;
1147
+ // Timestamp (ms) until which the post-send cooldown window is active. A timer
1148
+ // is only armed while there are queued events waiting for this window; when the
1149
+ // queue drains empty we keep the timestamp but drop the timer, so an idle worker
1150
+ // never holds a pending timer (which would otherwise keep a Node process alive
1151
+ // for the full interval.
1152
+ this.cooldownUntil = 0;
1153
+ this.inFlight = null;
1046
1154
  this.config = __assign$2(__assign$2({}, defaults_1$1.CONFIG), config);
1047
1155
  this.logger = this.originalLogger();
1048
1156
  }
1049
- ThrottledEventsLogger.prototype.configure = function (opts) {
1157
+ ThrottledEventsWorker.prototype.configure = function (opts) {
1050
1158
  for (var k in opts) {
1051
1159
  this.config[k] = opts[k];
1052
1160
  }
1053
1161
  };
1054
- ThrottledEventsLogger.prototype.log = function (payload) {
1162
+ ThrottledEventsWorker.prototype.log = function (payload) {
1055
1163
  this.queue.push(payload);
1056
- if (!this.isProcessing) {
1164
+ if (this.inFlight) {
1165
+ return;
1166
+ }
1167
+ // Crossing the bulk threshold preempts any pending cooldown and dispatches now.
1168
+ if (this.queue.length >= this.bulkThreshold()) {
1169
+ this.clearCooldownTimer();
1057
1170
  this.processQueue();
1171
+ return;
1058
1172
  }
1173
+ // A timer is already counting down the cooldown window; the queued event
1174
+ // will be picked up when it fires.
1175
+ if (this.cooldownTimer) {
1176
+ return;
1177
+ }
1178
+ // We're inside a cooldown window but no timer is armed (the queue had drained
1179
+ // empty after the previous send). Arm one for the remaining time so this event
1180
+ // still respects the interval.
1181
+ var remaining = this.cooldownRemainingMs();
1182
+ if (remaining > 0) {
1183
+ this.scheduleCooldownTimer(remaining);
1184
+ return;
1185
+ }
1186
+ this.processQueue();
1059
1187
  };
1060
- ThrottledEventsLogger.prototype.flushAsync = function () {
1188
+ ThrottledEventsWorker.prototype.flushAsync = function () {
1189
+ var _this = this;
1190
+ var _a;
1061
1191
  this.logger.debug('[Honeybadger] Flushing events');
1062
- return this.send();
1192
+ this.clearCooldownTimer();
1193
+ var previous = (_a = this.inFlight) !== null && _a !== void 0 ? _a : Promise.resolve();
1194
+ var flush = previous.then(function () { return _this.drainAll(); });
1195
+ // Mark the flush as in-flight so a concurrent log() doesn't start an
1196
+ // overlapping processQueue() drain while we're draining here.
1197
+ this.inFlight = flush;
1198
+ var clear = function () {
1199
+ if (_this.inFlight === flush) {
1200
+ _this.inFlight = null;
1201
+ }
1202
+ };
1203
+ flush.then(clear, clear);
1204
+ return flush;
1063
1205
  };
1064
- ThrottledEventsLogger.prototype.processQueue = function () {
1206
+ ThrottledEventsWorker.prototype.drainAll = function () {
1207
+ return __awaiter$4(this, void 0, void 0, function () {
1208
+ return __generator$4(this, function (_a) {
1209
+ switch (_a.label) {
1210
+ case 0:
1211
+ if (!(this.queue.length > 0)) return [3 /*break*/, 2];
1212
+ return [4 /*yield*/, this.send()];
1213
+ case 1:
1214
+ _a.sent();
1215
+ return [3 /*break*/, 0];
1216
+ case 2: return [2 /*return*/];
1217
+ }
1218
+ });
1219
+ });
1220
+ };
1221
+ ThrottledEventsWorker.prototype.processQueue = function () {
1065
1222
  var _this = this;
1066
- if (this.queue.length === 0 || this.isProcessing) {
1223
+ if (this.queue.length === 0 || this.inFlight) {
1067
1224
  return;
1068
1225
  }
1069
- this.isProcessing = true;
1070
- this.send()
1071
- .then(function () {
1072
- setTimeout(function () {
1073
- _this.isProcessing = false;
1074
- _this.processQueue();
1075
- }, 50);
1076
- })
1226
+ var inFlight = this.send()
1077
1227
  .catch(function (error) {
1078
1228
  _this.logger.error('[Honeybadger] Error making HTTP request:', error);
1079
- // Continue processing the queue even if there's an error
1080
- setTimeout(function () {
1081
- _this.isProcessing = false;
1082
- _this.processQueue();
1083
- }, 50);
1229
+ })
1230
+ .then(function () {
1231
+ // Only reset if we're still the current operation; a flushAsync() may
1232
+ // have taken ownership of inFlight while this send was resolving.
1233
+ if (_this.inFlight === inFlight) {
1234
+ _this.inFlight = null;
1235
+ _this.scheduleNextDispatch();
1236
+ }
1084
1237
  });
1238
+ this.inFlight = inFlight;
1239
+ };
1240
+ ThrottledEventsWorker.prototype.scheduleNextDispatch = function () {
1241
+ var intervalMs = this.dispatchIntervalMs();
1242
+ // Capture the cooldown deadline at dispatch time so a later config change to
1243
+ // the interval doesn't retroactively shorten a window already in progress.
1244
+ this.cooldownUntil = Date.now() + intervalMs;
1245
+ if (this.queue.length >= this.bulkThreshold()) {
1246
+ this.processQueue();
1247
+ return;
1248
+ }
1249
+ // Only arm a timer when there's queued work waiting for it. If the queue is
1250
+ // empty, `cooldownUntil` alone enforces the window: the next log() will arm a
1251
+ // timer for whatever time remains, and an idle worker holds no timer at all.
1252
+ if (this.queue.length > 0) {
1253
+ this.scheduleCooldownTimer(intervalMs);
1254
+ }
1255
+ };
1256
+ ThrottledEventsWorker.prototype.cooldownRemainingMs = function () {
1257
+ if (!this.cooldownUntil) {
1258
+ return 0;
1259
+ }
1260
+ return Math.max(0, this.cooldownUntil - Date.now());
1261
+ };
1262
+ ThrottledEventsWorker.prototype.scheduleCooldownTimer = function (ms) {
1263
+ var _this = this;
1264
+ this.cooldownTimer = setTimeout(function () {
1265
+ _this.cooldownTimer = null;
1266
+ _this.processQueue();
1267
+ }, ms);
1268
+ // In Node, don't let a pending cooldown timer keep the process alive for the
1269
+ // whole batching interval. `unref` lets the event loop go idle so the process
1270
+ // can exit (or a shutdown / `beforeExit` handler can run) promptly instead of
1271
+ // blocking for `dispatchIntervalSeconds`. Queued events aren't lost: a
1272
+ // `flushAsync()` on the way out still drains them before exit. (`unref` is
1273
+ // Node-only; the browser's numeric setTimeout handle has no such method.)
1274
+ if (typeof this.cooldownTimer === 'object' && this.cooldownTimer !== null && typeof this.cooldownTimer.unref === 'function') {
1275
+ this.cooldownTimer.unref();
1276
+ }
1277
+ };
1278
+ ThrottledEventsWorker.prototype.clearCooldownTimer = function () {
1279
+ if (this.cooldownTimer) {
1280
+ clearTimeout(this.cooldownTimer);
1281
+ this.cooldownTimer = null;
1282
+ }
1085
1283
  };
1086
- ThrottledEventsLogger.prototype.send = function () {
1284
+ ThrottledEventsWorker.prototype.send = function () {
1087
1285
  return __awaiter$4(this, void 0, void 0, function () {
1088
1286
  var eventsData, data;
1089
1287
  return __generator$4(this, function (_a) {
1090
1288
  if (this.queue.length === 0) {
1091
1289
  return [2 /*return*/];
1092
1290
  }
1093
- eventsData = this.queue.slice();
1094
- this.queue = [];
1291
+ eventsData = this.queue.splice(0, this.bulkThreshold());
1095
1292
  data = json_nd_1.NdJson.stringify(eventsData);
1096
1293
  return [2 /*return*/, this.makeHttpRequest(data)];
1097
1294
  });
1098
1295
  });
1099
1296
  };
1100
- ThrottledEventsLogger.prototype.makeHttpRequest = function (data) {
1297
+ ThrottledEventsWorker.prototype.bulkThreshold = function () {
1298
+ var _a;
1299
+ var v = (_a = this.config.events) === null || _a === void 0 ? void 0 : _a.bulkThreshold;
1300
+ return typeof v === 'number' && v > 0 ? v : defaults_1$1.CONFIG.events.bulkThreshold;
1301
+ };
1302
+ ThrottledEventsWorker.prototype.dispatchIntervalMs = function () {
1303
+ var _a;
1304
+ var v = (_a = this.config.events) === null || _a === void 0 ? void 0 : _a.dispatchIntervalSeconds;
1305
+ var seconds = typeof v === 'number' && v >= 0 ? v : defaults_1$1.CONFIG.events.dispatchIntervalSeconds;
1306
+ return seconds * 1000;
1307
+ };
1308
+ ThrottledEventsWorker.prototype.makeHttpRequest = function (data) {
1101
1309
  return __awaiter$4(this, void 0, void 0, function () {
1102
1310
  var _this = this;
1103
1311
  return __generator$4(this, function (_a) {
@@ -1112,10 +1320,13 @@ var ThrottledEventsLogger = /** @class */ (function () {
1112
1320
  maxObjectDepth: this.config.maxObjectDepth,
1113
1321
  logger: this.logger,
1114
1322
  }, data)
1115
- .then(function () {
1116
- if (_this.config.debug) {
1323
+ .then(function (resp) {
1324
+ if ([200, 201].includes(resp.statusCode)) {
1117
1325
  _this.logger.debug('[Honeybadger] Events sent successfully');
1118
1326
  }
1327
+ else {
1328
+ _this.logger.debug("[Honeybadger] Events failed[".concat(resp.statusCode, "]: ").concat(resp.body));
1329
+ }
1119
1330
  })
1120
1331
  .catch(function (err) {
1121
1332
  _this.logger.error("[Honeybadger] Error sending events: ".concat(err.message));
@@ -1126,28 +1337,40 @@ var ThrottledEventsLogger = /** @class */ (function () {
1126
1337
  /**
1127
1338
  * todo: improve this
1128
1339
  *
1129
- * The EventsLogger overrides the console methods to enable automatic instrumentation
1340
+ * The events plugin overrides the console methods to enable automatic instrumentation
1130
1341
  * of console logs to the Honeybadger API.
1131
1342
  * So if we want to log something in here we need to use the original methods.
1132
1343
  */
1133
- ThrottledEventsLogger.prototype.originalLogger = function () {
1134
- var _a, _b, _c, _d, _e;
1344
+ ThrottledEventsWorker.prototype.originalLogger = function () {
1345
+ var _this = this;
1346
+ var _a, _b, _c, _d;
1135
1347
  return {
1136
1348
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1137
1349
  log: (_a = console.log.__hb_original) !== null && _a !== void 0 ? _a : console.log,
1138
1350
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1139
1351
  info: (_b = console.info.__hb_original) !== null && _b !== void 0 ? _b : console.info,
1352
+ debug: function () {
1353
+ var _a;
1354
+ var args = [];
1355
+ for (var _i = 0; _i < arguments.length; _i++) {
1356
+ args[_i] = arguments[_i];
1357
+ }
1358
+ if (!_this.config.debug) {
1359
+ return;
1360
+ }
1361
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1362
+ var func = (_a = console.debug.__hb_original) !== null && _a !== void 0 ? _a : console.debug;
1363
+ return func.apply(void 0, args);
1364
+ },
1140
1365
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1141
- debug: (_c = console.debug.__hb_original) !== null && _c !== void 0 ? _c : console.debug,
1142
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1143
- warn: (_d = console.warn.__hb_original) !== null && _d !== void 0 ? _d : console.warn,
1366
+ warn: (_c = console.warn.__hb_original) !== null && _c !== void 0 ? _c : console.warn,
1144
1367
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1145
- error: (_e = console.error.__hb_original) !== null && _e !== void 0 ? _e : console.error,
1368
+ error: (_d = console.error.__hb_original) !== null && _d !== void 0 ? _d : console.error,
1146
1369
  };
1147
1370
  };
1148
- return ThrottledEventsLogger;
1371
+ return ThrottledEventsWorker;
1149
1372
  }());
1150
- throttled_events_logger.ThrottledEventsLogger = ThrottledEventsLogger;
1373
+ throttled_events_worker.ThrottledEventsWorker = ThrottledEventsWorker;
1151
1374
 
1152
1375
  var __assign$1 = (commonjsGlobal && commonjsGlobal.__assign) || function () {
1153
1376
  __assign$1 = Object.assign || function(t) {
@@ -1200,7 +1423,7 @@ Object.defineProperty(client$1, "__esModule", { value: true });
1200
1423
  client$1.Client = void 0;
1201
1424
  var util_1$5 = util$1;
1202
1425
  var store_1 = store;
1203
- var throttled_events_logger_1 = throttled_events_logger;
1426
+ var throttled_events_worker_1 = throttled_events_worker;
1204
1427
  var defaults_1 = defaults;
1205
1428
  // Split at commas and spaces
1206
1429
  var TAG_SEPARATOR = /,|\s+/;
@@ -1212,17 +1435,20 @@ var Client = /** @class */ (function () {
1212
1435
  this.__pluginsLoaded = false;
1213
1436
  this.__store = null;
1214
1437
  this.__beforeNotifyHandlers = [];
1438
+ this.__beforeEventHandlers = [];
1215
1439
  this.__afterNotifyHandlers = [];
1440
+ this.__pendingEvents = new Set();
1216
1441
  this.__notifier = {
1217
1442
  name: '@honeybadger-io/core',
1218
1443
  url: 'https://github.com/honeybadger-io/honeybadger-js/tree/master/packages/core',
1219
- version: '6.15.2'
1444
+ version: '6.16.0'
1220
1445
  };
1221
1446
  this.config = __assign$1(__assign$1({}, defaults_1.CONFIG), opts);
1222
1447
  this.__initStore();
1223
1448
  this.__transport = transport;
1224
- this.__eventsLogger = new throttled_events_logger_1.ThrottledEventsLogger(this.config, this.__transport);
1449
+ this.__eventsWorker = new throttled_events_worker_1.ThrottledEventsWorker(this.config, this.__transport);
1225
1450
  this.logger = (0, util_1$5.logger)(this);
1451
+ this.__applyEventsEnabledShim(opts);
1226
1452
  }
1227
1453
  Client.prototype.getVersion = function () {
1228
1454
  return this.__notifier.version;
@@ -1245,10 +1471,24 @@ var Client = /** @class */ (function () {
1245
1471
  for (var k in opts) {
1246
1472
  this.config[k] = opts[k];
1247
1473
  }
1248
- this.__eventsLogger.configure(this.config);
1474
+ this.__applyEventsEnabledShim(opts);
1475
+ this.__eventsWorker.configure(this.config);
1249
1476
  this.loadPlugins();
1250
1477
  return this;
1251
1478
  };
1479
+ /**
1480
+ * Backwards compatibility: `eventsEnabled: true` used to opt into console events.
1481
+ * The deprecated flag auto-enables `insights.enabled` and `insights.console`;
1482
+ * explicit user-provided insights values win over the shim.
1483
+ */
1484
+ Client.prototype.__applyEventsEnabledShim = function (opts) {
1485
+ var _a;
1486
+ if (opts.eventsEnabled !== true) {
1487
+ return;
1488
+ }
1489
+ this.logger.warn('Deprecation warning: `eventsEnabled` has been deprecated; please use `insights.enabled` and `insights.console` instead.');
1490
+ this.config.insights = __assign$1(__assign$1(__assign$1({}, ((_a = this.config.insights) !== null && _a !== void 0 ? _a : {})), { enabled: true, console: true }), opts.insights);
1491
+ };
1252
1492
  Client.prototype.loadPlugins = function () {
1253
1493
  var _this = this;
1254
1494
  var pluginsToLoad = this.__pluginsLoaded
@@ -1258,12 +1498,16 @@ var Client = /** @class */ (function () {
1258
1498
  this.__pluginsLoaded = true;
1259
1499
  };
1260
1500
  Client.prototype.__initStore = function () {
1261
- this.__store = new store_1.GlobalStore({ context: {}, breadcrumbs: [] }, this.config.maxBreadcrumbs);
1501
+ this.__store = new store_1.GlobalStore({ context: {}, eventContext: {}, breadcrumbs: [] }, this.config.maxBreadcrumbs);
1262
1502
  };
1263
1503
  Client.prototype.beforeNotify = function (handler) {
1264
1504
  this.__beforeNotifyHandlers.push(handler);
1265
1505
  return this;
1266
1506
  };
1507
+ Client.prototype.beforeEvent = function (handler) {
1508
+ this.__beforeEventHandlers.push(handler);
1509
+ return this;
1510
+ };
1267
1511
  Client.prototype.afterNotify = function (handler) {
1268
1512
  this.__afterNotifyHandlers.push(handler);
1269
1513
  return this;
@@ -1274,6 +1518,16 @@ var Client = /** @class */ (function () {
1274
1518
  }
1275
1519
  return this;
1276
1520
  };
1521
+ Client.prototype.setEventContext = function (eventContext) {
1522
+ if (typeof eventContext === 'object' && eventContext != null) {
1523
+ this.__store.setEventContext(eventContext);
1524
+ }
1525
+ return this;
1526
+ };
1527
+ Client.prototype.clearEventContext = function () {
1528
+ this.__store.clearEventContext();
1529
+ return this;
1530
+ };
1277
1531
  Client.prototype.resetContext = function (context) {
1278
1532
  this.logger.warn('Deprecation warning: `Honeybadger.resetContext()` has been deprecated; please use `Honeybadger.clear()` instead.');
1279
1533
  this.__store.clear();
@@ -1428,19 +1682,59 @@ var Client = /** @class */ (function () {
1428
1682
  this.event('log', data);
1429
1683
  };
1430
1684
  Client.prototype.event = function (type, data) {
1685
+ var _this = this;
1431
1686
  var _a;
1432
1687
  if (typeof type === 'object') {
1433
1688
  data = type;
1434
1689
  type = (_a = type['event_type']) !== null && _a !== void 0 ? _a : undefined;
1435
1690
  }
1436
- this.__eventsLogger.log(__assign$1({ event_type: type, ts: new Date().toISOString() }, data));
1691
+ var eventContext = this.__store.getContents('eventContext') || {};
1692
+ var payload = __assign$1(__assign$1({ event_type: type, ts: new Date().toISOString() }, eventContext), data);
1693
+ // Track in-flight handler chains so flushAsync() can await them before
1694
+ // delegating to the events logger; otherwise a caller that awaits flushAsync()
1695
+ // immediately after event() may flush before the payload enters the queue.
1696
+ var inFlight = (0, util_1$5.runBeforeEventHandlers)(payload, this.__beforeEventHandlers, this.logger)
1697
+ .then(function (shouldSend) {
1698
+ var _a, _b;
1699
+ if (!shouldSend) {
1700
+ _this.logger.debug('skipping event: beforeEvent handler returned false');
1701
+ return;
1702
+ }
1703
+ var sampleRate = (_b = (_a = _this.config.events) === null || _a === void 0 ? void 0 : _a.sampleRatePercentage) !== null && _b !== void 0 ? _b : 100;
1704
+ if (!(0, util_1$5.shouldSampleEvent)(payload, sampleRate)) {
1705
+ _this.logger.debug('skipping event: dropped by sampleRatePercentage');
1706
+ return;
1707
+ }
1708
+ delete payload._hb;
1709
+ _this.__eventsWorker.log(payload);
1710
+ })
1711
+ .catch(function (err) {
1712
+ // should not happen, because each handler is wrapped with a try/catch
1713
+ _this.logger.error('beforeEvent handler chain failed; dropping event', err);
1714
+ })
1715
+ .finally(function () {
1716
+ _this.__pendingEvents.delete(inFlight);
1717
+ });
1718
+ this.__pendingEvents.add(inFlight);
1437
1719
  };
1438
1720
  /**
1439
1721
  * This method currently flushes the event (Insights) queue.
1440
1722
  * In the future, it should also flush the error queue (assuming an error throttler is implemented).
1441
1723
  */
1442
1724
  Client.prototype.flushAsync = function () {
1443
- return this.__eventsLogger.flushAsync();
1725
+ return __awaiter$3(this, void 0, void 0, function () {
1726
+ return __generator$3(this, function (_a) {
1727
+ switch (_a.label) {
1728
+ case 0:
1729
+ if (!(this.__pendingEvents.size > 0)) return [3 /*break*/, 2];
1730
+ return [4 /*yield*/, Promise.allSettled(Array.from(this.__pendingEvents))];
1731
+ case 1:
1732
+ _a.sent();
1733
+ _a.label = 2;
1734
+ case 2: return [2 /*return*/, this.__eventsWorker.flushAsync()];
1735
+ }
1736
+ });
1737
+ });
1444
1738
  };
1445
1739
  Client.prototype.__getBreadcrumbs = function () {
1446
1740
  return this.__store.getContents('breadcrumbs').slice();
@@ -1632,7 +1926,7 @@ Object.defineProperty(types, "__esModule", { value: true });
1632
1926
  };
1633
1927
  Object.defineProperty(exports, "__esModule", { value: true });
1634
1928
  exports.Plugins = exports.Defaults = exports.Util = exports.Types = exports.Client = void 0;
1635
- var events_1 = __importDefault(events);
1929
+ var console_events_1 = __importDefault(console_events);
1636
1930
  var client_1 = client$1;
1637
1931
  Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return client_1.Client; } });
1638
1932
  __exportStar(store, exports);
@@ -1640,7 +1934,12 @@ Object.defineProperty(types, "__esModule", { value: true });
1640
1934
  exports.Util = __importStar(util$1);
1641
1935
  exports.Defaults = __importStar(defaults);
1642
1936
  exports.Plugins = {
1643
- events: events_1.default
1937
+ consoleEvents: console_events_1.default,
1938
+ /**
1939
+ * @deprecated Use `consoleEvents` instead. Kept as an alias for backwards
1940
+ * compatibility and will be removed in a future major version.
1941
+ */
1942
+ events: console_events_1.default,
1644
1943
  };
1645
1944
 
1646
1945
  } (src));
@@ -1802,8 +2101,145 @@ var uncaught_exception_monitor = {};
1802
2101
 
1803
2102
  var aws_lambda = {};
1804
2103
 
2104
+ var http_event = {};
2105
+
2106
+ /**
2107
+ * Shared helpers for HTTP-event instrumentation.
2108
+ *
2109
+ * Used by inbound (Express, Fastify, Lambda, Next.js) and — in a later PR —
2110
+ * outbound (`http`, `https`, `fetch`) integrations.
2111
+ */
2112
+ Object.defineProperty(http_event, "__esModule", { value: true });
2113
+ http_event.buildRequestEventPayload = http_event.durationMs = http_event.startTimer = http_event.seedRequestEventContext = http_event.getOrCreateCorrelationId = http_event.getOrCreateRequestId = void 0;
2114
+ var crypto_1 = require$$0__default$1["default"];
2115
+ /**
2116
+ * Reads a header value from a Node-style headers bag in a case-insensitive
2117
+ * manner. When the value is an array (e.g. `Set-Cookie`), returns the first
2118
+ * element. Returns `undefined` when the header is missing or empty.
2119
+ */
2120
+ function readHeader(headers, name) {
2121
+ if (!headers)
2122
+ return undefined;
2123
+ var lower = name.toLowerCase();
2124
+ var value = headers[lower];
2125
+ if (value === undefined) {
2126
+ for (var _i = 0, _a = Object.keys(headers); _i < _a.length; _i++) {
2127
+ var key = _a[_i];
2128
+ if (key.toLowerCase() === lower) {
2129
+ value = headers[key];
2130
+ break;
2131
+ }
2132
+ }
2133
+ }
2134
+ if (Array.isArray(value))
2135
+ value = value[0];
2136
+ if (typeof value !== 'string')
2137
+ return undefined;
2138
+ var trimmed = value.trim();
2139
+ return trimmed.length ? trimmed : undefined;
2140
+ }
2141
+ /**
2142
+ * Generates a UUID-like identifier. Prefers Node's `crypto.randomUUID()` when
2143
+ * available (Node 14.17+); falls back to a Math.random-based v4-shaped string
2144
+ * for older Node versions.
2145
+ */
2146
+ function generateId() {
2147
+ if (typeof crypto_1.randomUUID === 'function') {
2148
+ try {
2149
+ return (0, crypto_1.randomUUID)();
2150
+ }
2151
+ catch (_e) {
2152
+ // fall through to manual generation
2153
+ }
2154
+ }
2155
+ // v4-shaped, not crypto-quality. Acceptable since this is a correlation id,
2156
+ // not a security token.
2157
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (ch) {
2158
+ var r = (Math.random() * 16) | 0;
2159
+ var v = ch === 'x' ? r : (r & 0x3) | 0x8;
2160
+ return v.toString(16);
2161
+ });
2162
+ }
2163
+ /**
2164
+ * Returns a unique-per-request id. Reads `x-request-id`, then `request-id`
2165
+ * headers; falls back to a generated id when neither is present.
2166
+ */
2167
+ function getOrCreateRequestId(headers) {
2168
+ var _a, _b;
2169
+ return ((_b = (_a = readHeader(headers, 'x-request-id')) !== null && _a !== void 0 ? _a : readHeader(headers, 'request-id')) !== null && _b !== void 0 ? _b : generateId());
2170
+ }
2171
+ http_event.getOrCreateRequestId = getOrCreateRequestId;
2172
+ /**
2173
+ * Returns a correlation id that may span multiple requests in a logical trace.
2174
+ * Reads `x-correlation-id`, then `x-amzn-trace-id`; falls back to the supplied
2175
+ * `requestId` so callers always have both fields populated.
2176
+ */
2177
+ function getOrCreateCorrelationId(headers, requestId) {
2178
+ var _a, _b;
2179
+ return ((_b = (_a = readHeader(headers, 'x-correlation-id')) !== null && _a !== void 0 ? _a : readHeader(headers, 'x-amzn-trace-id')) !== null && _b !== void 0 ? _b : requestId);
2180
+ }
2181
+ http_event.getOrCreateCorrelationId = getOrCreateCorrelationId;
2182
+ /**
2183
+ * Convenience helper for framework integrations: read request/correlation ids
2184
+ * from the request headers and return both. When no headers contain either
2185
+ * value, both ids are generated and `requestId === correlationId`.
2186
+ */
2187
+ function seedRequestEventContext(headers) {
2188
+ var requestId = getOrCreateRequestId(headers);
2189
+ var correlationId = getOrCreateCorrelationId(headers, requestId);
2190
+ return { request_id: requestId, correlation_id: correlationId };
2191
+ }
2192
+ http_event.seedRequestEventContext = seedRequestEventContext;
2193
+ /**
2194
+ * Starts a high-resolution timer.
2195
+ */
2196
+ function startTimer() {
2197
+ return process.hrtime.bigint();
2198
+ }
2199
+ http_event.startTimer = startTimer;
2200
+ var NS_PER_MS = BigInt(1000000);
2201
+ /**
2202
+ * Returns the integer number of milliseconds elapsed since `start`.
2203
+ */
2204
+ function durationMs(start) {
2205
+ var diff = process.hrtime.bigint() - start;
2206
+ return Number(diff / NS_PER_MS);
2207
+ }
2208
+ http_event.durationMs = durationMs;
2209
+ /**
2210
+ * Builds the per-request event payload. `request_id` and `correlation_id` are
2211
+ * NOT added here — they live on `eventContext` and are merged onto every event
2212
+ * by the client.
2213
+ */
2214
+ function buildRequestEventPayload(input) {
2215
+ var payload = {};
2216
+ if (input.method !== undefined)
2217
+ payload.method = input.method;
2218
+ if (input.path !== undefined)
2219
+ payload.path = input.path;
2220
+ if (input.route !== undefined)
2221
+ payload.route = input.route;
2222
+ if (input.status !== undefined)
2223
+ payload.status = input.status;
2224
+ if (input.duration !== undefined)
2225
+ payload.duration = input.duration;
2226
+ if (input.extra) {
2227
+ for (var _i = 0, _a = Object.keys(input.extra); _i < _a.length; _i++) {
2228
+ var k = _a[_i];
2229
+ if (!Object.prototype.hasOwnProperty.call(payload, k)) {
2230
+ payload[k] = input.extra[k];
2231
+ }
2232
+ }
2233
+ }
2234
+ return payload;
2235
+ }
2236
+ http_event.buildRequestEventPayload = buildRequestEventPayload;
2237
+
1805
2238
  Object.defineProperty(aws_lambda, "__esModule", { value: true });
1806
2239
  aws_lambda.removeAwsDefaultUncaughtExceptionListener = aws_lambda.lambdaHandler = void 0;
2240
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2241
+ var core_1$4 = src;
2242
+ var http_event_1$1 = http_event;
1807
2243
  function isHandlerSync(handler) {
1808
2244
  return handler.length > 2;
1809
2245
  }
@@ -1815,28 +2251,132 @@ function reportToHoneybadger(hb, err, callback) {
1815
2251
  }
1816
2252
  });
1817
2253
  }
2254
+ // Detects inbound HTTP Lambda events (API Gateway v1 REST, API Gateway v2 HTTP, and ALB).
2255
+ // API Gateway v1 and ALB carry `httpMethod` at the top level; API Gateway v2 nests
2256
+ // method under `requestContext.http`.
2257
+ function isHttpLambdaEvent(event) {
2258
+ if (!event || typeof event !== 'object') {
2259
+ return false;
2260
+ }
2261
+ var e = event;
2262
+ if (typeof e.httpMethod === 'string') {
2263
+ return true;
2264
+ }
2265
+ var rc = e.requestContext;
2266
+ var http = rc && rc.http;
2267
+ return !!(http && typeof http.method === 'string');
2268
+ }
2269
+ function getLambdaMethod(event) {
2270
+ if (!event || typeof event !== 'object') {
2271
+ return undefined;
2272
+ }
2273
+ var e = event;
2274
+ if (typeof e.httpMethod === 'string') {
2275
+ return e.httpMethod;
2276
+ }
2277
+ var rc = e.requestContext;
2278
+ var http = rc && rc.http;
2279
+ if (http && typeof http.method === 'string') {
2280
+ return http.method;
2281
+ }
2282
+ return undefined;
2283
+ }
2284
+ function getLambdaPath(event) {
2285
+ if (!event || typeof event !== 'object') {
2286
+ return undefined;
2287
+ }
2288
+ var e = event;
2289
+ var rc = e.requestContext;
2290
+ var http = rc && rc.http;
2291
+ if (http && typeof http.path === 'string') {
2292
+ return http.path;
2293
+ }
2294
+ if (typeof e.rawPath === 'string') {
2295
+ return e.rawPath;
2296
+ }
2297
+ if (typeof e.path === 'string') {
2298
+ return e.path;
2299
+ }
2300
+ return undefined;
2301
+ }
2302
+ function lambdaStatus(result) {
2303
+ if (result && typeof result === 'object') {
2304
+ var status_1 = result.statusCode;
2305
+ if (typeof status_1 === 'number')
2306
+ return status_1;
2307
+ }
2308
+ return 200;
2309
+ }
2310
+ function getLambdaHeaders(event) {
2311
+ if (!event || typeof event !== 'object')
2312
+ return {};
2313
+ var headers = event.headers;
2314
+ if (!headers || typeof headers !== 'object')
2315
+ return {};
2316
+ return headers;
2317
+ }
2318
+ function seedLambdaEventContext(hb, event, context) {
2319
+ if (isHttpLambdaEvent(event)) {
2320
+ hb.setEventContext((0, http_event_1$1.seedRequestEventContext)(getLambdaHeaders(event)));
2321
+ return;
2322
+ }
2323
+ // Non-HTTP triggers (SQS, S3, EventBridge, scheduled) have no inbound headers.
2324
+ // Use the AWS request id as the request_id, and X-Ray's trace id (if set) as
2325
+ // the correlation_id, otherwise mirror request_id.
2326
+ var requestId = context.awsRequestId;
2327
+ var correlationId = process.env._X_AMZN_TRACE_ID || requestId;
2328
+ hb.setEventContext({ request_id: requestId, correlation_id: correlationId });
2329
+ }
2330
+ // Returns an emitter for the `request.handled` event, or null when HTTP
2331
+ // instrumentation is off (nothing to emit). The emitter also owns delivery:
2332
+ // Lambda may freeze the execution environment the moment the handler completes,
2333
+ // so after recording the event it flushes the queue and returns that promise.
2334
+ // Callers must await it before resolving/invoking the callback. Delivery failures
2335
+ // are logged by the events worker and must not break the handler.
2336
+ function createLambdaRequestEmitter(hb, event) {
2337
+ if (!isHttpLambdaEvent(event))
2338
+ return null;
2339
+ if (!core_1$4.Util.resolveInsights(hb.config).http)
2340
+ return null;
2341
+ var start = (0, http_event_1$1.startTimer)();
2342
+ var emitted = false;
2343
+ return function emit(status) {
2344
+ if (emitted)
2345
+ return Promise.resolve();
2346
+ emitted = true;
2347
+ hb.event('request.handled', (0, http_event_1$1.buildRequestEventPayload)({
2348
+ method: getLambdaMethod(event),
2349
+ path: getLambdaPath(event),
2350
+ status: status,
2351
+ duration: (0, http_event_1$1.durationMs)(start),
2352
+ }));
2353
+ return hb.flushAsync().catch(function () { });
2354
+ };
2355
+ }
1818
2356
  function asyncHandler(handler, hb) {
1819
2357
  return function wrappedLambdaHandler(event, context) {
1820
2358
  return new Promise(function (resolve, reject) {
1821
2359
  hb.run(function () {
2360
+ seedLambdaEventContext(hb, event, context);
2361
+ var emit = createLambdaRequestEmitter(hb, event);
1822
2362
  var timeoutHandler = setupTimeoutWarning(hb, context);
1823
2363
  try {
1824
- var result = handler(event, context);
2364
+ var result_1 = handler(event, context);
1825
2365
  // check if handler returns a promise
1826
- if (result && result.then) {
1827
- result
1828
- .then(resolve)
1829
- .catch(function (err) { return reportToHoneybadger(hb, err, reject); })
2366
+ if (result_1 && result_1.then) {
2367
+ result_1
2368
+ .then(function (value) { return Promise.resolve(emit === null || emit === void 0 ? void 0 : emit(lambdaStatus(value))).then(function () { return resolve(value); }); })
2369
+ .catch(function (err) { return Promise.resolve(emit === null || emit === void 0 ? void 0 : emit(500)).then(function () { return reportToHoneybadger(hb, err, reject); }); })
1830
2370
  .finally(function () { return clearTimeout(timeoutHandler); });
1831
2371
  }
1832
2372
  else {
1833
2373
  clearTimeout(timeoutHandler);
1834
- resolve(result);
2374
+ Promise.resolve(emit === null || emit === void 0 ? void 0 : emit(lambdaStatus(result_1))).then(function () { return resolve(result_1); });
1835
2375
  }
1836
2376
  }
1837
2377
  catch (err) {
1838
2378
  clearTimeout(timeoutHandler);
1839
- reportToHoneybadger(hb, err, reject);
2379
+ Promise.resolve(emit === null || emit === void 0 ? void 0 : emit(500)).then(function () { return reportToHoneybadger(hb, err, reject); });
1840
2380
  }
1841
2381
  });
1842
2382
  });
@@ -1845,19 +2385,22 @@ function asyncHandler(handler, hb) {
1845
2385
  function syncHandler(handler, hb) {
1846
2386
  return function wrappedLambdaHandler(event, context, cb) {
1847
2387
  hb.run(function () {
2388
+ seedLambdaEventContext(hb, event, context);
2389
+ var emit = createLambdaRequestEmitter(hb, event);
1848
2390
  var timeoutHandler = setupTimeoutWarning(hb, context);
1849
2391
  try {
1850
2392
  handler(event, context, function (error, result) {
1851
2393
  clearTimeout(timeoutHandler);
1852
2394
  if (error) {
1853
- return reportToHoneybadger(hb, error, cb);
2395
+ Promise.resolve(emit === null || emit === void 0 ? void 0 : emit(500)).then(function () { return reportToHoneybadger(hb, error, cb); });
2396
+ return;
1854
2397
  }
1855
- cb(null, result);
2398
+ Promise.resolve(emit === null || emit === void 0 ? void 0 : emit(lambdaStatus(result))).then(function () { return cb(null, result); });
1856
2399
  });
1857
2400
  }
1858
2401
  catch (err) {
1859
2402
  clearTimeout(timeoutHandler);
1860
- reportToHoneybadger(hb, err, cb);
2403
+ Promise.resolve(emit === null || emit === void 0 ? void 0 : emit(500)).then(function () { return reportToHoneybadger(hb, err, cb); });
1861
2404
  }
1862
2405
  });
1863
2406
  };
@@ -2130,6 +2673,7 @@ var ShutdownMonitor = /** @class */ (function () {
2130
2673
  function ShutdownMonitor() {
2131
2674
  this.__isReporting = false;
2132
2675
  this.__listener = this.makeListener();
2676
+ this.__beforeExitListener = this.makeBeforeExitListener();
2133
2677
  }
2134
2678
  ShutdownMonitor.prototype.setClient = function (client) {
2135
2679
  this.__client = client;
@@ -2158,6 +2702,35 @@ var ShutdownMonitor = /** @class */ (function () {
2158
2702
  }); };
2159
2703
  return honeybadgerShutdownListener;
2160
2704
  };
2705
+ // `beforeExit` fires when the event loop drains and the process is about to
2706
+ // exit on its own — a natural exit, which the SIGTERM/SIGINT listeners above do
2707
+ // *not* cover. We flush any queued events (e.g. batched Insights events whose
2708
+ // cooldown timer is unref'd and therefore won't hold the process open) so they
2709
+ // aren't dropped on the way out. Unlike the signal path we don't force-exit;
2710
+ // flushing schedules real I/O, which keeps the process alive until delivery, and
2711
+ // then Node exits naturally. The `__isReporting` guard stops the flush's own I/O
2712
+ // from re-triggering us.
2713
+ ShutdownMonitor.prototype.makeBeforeExitListener = function () {
2714
+ var _this = this;
2715
+ // noinspection UnnecessaryLocalVariableJS
2716
+ var honeybadgerBeforeExitListener = function () { return __awaiter$1(_this, void 0, void 0, function () {
2717
+ return __generator$1(this, function (_a) {
2718
+ switch (_a.label) {
2719
+ case 0:
2720
+ if (this.__isReporting || !this.__client) {
2721
+ return [2 /*return*/];
2722
+ }
2723
+ this.__isReporting = true;
2724
+ return [4 /*yield*/, this.__client.flushAsync()];
2725
+ case 1:
2726
+ _a.sent();
2727
+ this.__isReporting = false;
2728
+ return [2 /*return*/];
2729
+ }
2730
+ });
2731
+ }); };
2732
+ return honeybadgerBeforeExitListener;
2733
+ };
2161
2734
  ShutdownMonitor.prototype.maybeAddListener = function () {
2162
2735
  for (var _i = 0, _a = ShutdownMonitor.KILL_SIGNALS; _i < _a.length; _i++) {
2163
2736
  var signal = _a[_i];
@@ -2166,6 +2739,10 @@ var ShutdownMonitor = /** @class */ (function () {
2166
2739
  process.on(signal, this.__listener);
2167
2740
  }
2168
2741
  }
2742
+ var beforeExitListeners = process.listeners('beforeExit') || [];
2743
+ if (!beforeExitListeners.includes(this.__beforeExitListener)) {
2744
+ process.on('beforeExit', this.__beforeExitListener);
2745
+ }
2169
2746
  };
2170
2747
  ShutdownMonitor.prototype.maybeRemoveListener = function () {
2171
2748
  for (var _i = 0, _a = ShutdownMonitor.KILL_SIGNALS; _i < _a.length; _i++) {
@@ -2175,6 +2752,10 @@ var ShutdownMonitor = /** @class */ (function () {
2175
2752
  process.removeListener(signal, this.__listener);
2176
2753
  }
2177
2754
  }
2755
+ var beforeExitListeners = process.listeners('beforeExit') || [];
2756
+ if (beforeExitListeners.includes(this.__beforeExitListener)) {
2757
+ process.removeListener('beforeExit', this.__beforeExitListener);
2758
+ }
2178
2759
  };
2179
2760
  ShutdownMonitor.prototype.hasOtherShutdownListeners = function (signal) {
2180
2761
  var _this = this;
@@ -2199,14 +2780,9 @@ function default_1() {
2199
2780
  return {
2200
2781
  load: function (client) {
2201
2782
  shutdownMonitor.setClient(client);
2202
- // at the moment, the shutdown monitor only sends events from the queue
2203
- // if we implement a queue for throttling errors, we won't have to check for `config.eventsEnabled`
2204
- if (client.config.eventsEnabled) {
2205
- shutdownMonitor.maybeAddListener();
2206
- }
2207
- else {
2208
- shutdownMonitor.maybeRemoveListener();
2209
- }
2783
+ // programmatic events can be queued at any time, so the flush listener
2784
+ // is always registered
2785
+ shutdownMonitor.maybeAddListener();
2210
2786
  },
2211
2787
  shouldReloadOnConfigure: true,
2212
2788
  };
@@ -2220,7 +2796,9 @@ var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || func
2220
2796
  };
2221
2797
  Object.defineProperty(middleware, "__esModule", { value: true });
2222
2798
  middleware.errorHandler = middleware.requestHandler = void 0;
2223
- var url_1$1 = __importDefault(require$$0__default$1["default"]);
2799
+ var url_1$1 = __importDefault(require$$0__default$2["default"]);
2800
+ var core_1$3 = src;
2801
+ var http_event_1 = http_event;
2224
2802
  function fullUrl(req) {
2225
2803
  var connection = req.connection;
2226
2804
  var address = connection && connection.address();
@@ -2235,8 +2813,43 @@ function fullUrl(req) {
2235
2813
  query: req.query
2236
2814
  });
2237
2815
  }
2816
+ function instrumentInboundRequest(client, req, res) {
2817
+ // Completion is observed via response events, which requires a Node
2818
+ // ServerResponse. A non-EventEmitter response (e.g. a FastifyReply when this
2819
+ // middleware is mistakenly registered as a fastify preHandler) would break
2820
+ // every request — degrade to "no request event" instead. Fastify users
2821
+ // should register fastifyPlugin() from server/fastify.
2822
+ if (typeof res.once !== 'function') {
2823
+ client.logger.debug('skipping insights instrumentation for http requests: response object is not an EventEmitter and does not have a .once() hook');
2824
+ return;
2825
+ }
2826
+ var start = (0, http_event_1.startTimer)();
2827
+ var emitted = false;
2828
+ var emit = function () {
2829
+ var _a;
2830
+ if (emitted)
2831
+ return;
2832
+ emitted = true;
2833
+ client.event('request.handled', (0, http_event_1.buildRequestEventPayload)({
2834
+ method: req.method,
2835
+ path: req.path,
2836
+ route: (_a = req.route) === null || _a === void 0 ? void 0 : _a.path,
2837
+ status: res.statusCode,
2838
+ duration: (0, http_event_1.durationMs)(start),
2839
+ }));
2840
+ };
2841
+ res.once('finish', emit);
2842
+ res.once('close', emit);
2843
+ }
2238
2844
  function requestHandler(req, res, next) {
2239
- this.withRequest(req, next, next);
2845
+ var _this = this;
2846
+ this.withRequest(req, function () {
2847
+ _this.setEventContext((0, http_event_1.seedRequestEventContext)(req.headers));
2848
+ if (core_1$3.Util.resolveInsights(_this.config).http) {
2849
+ instrumentInboundRequest(_this, req, res);
2850
+ }
2851
+ next();
2852
+ }, next);
2240
2853
  }
2241
2854
  middleware.requestHandler = requestHandler;
2242
2855
  function errorHandler(err, req, _res, next) {
@@ -2295,7 +2908,7 @@ var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (
2295
2908
  Object.defineProperty(transport, "__esModule", { value: true });
2296
2909
  transport.ServerTransport = void 0;
2297
2910
  var core_1$2 = src;
2298
- var url_1 = require$$0__default$1["default"];
2911
+ var url_1 = require$$0__default$2["default"];
2299
2912
  var util_1 = util;
2300
2913
  var http = __importStar(require$$3__default$1["default"]);
2301
2914
  var https = __importStar(require$$4__default["default"]);
@@ -2413,6 +3026,12 @@ var AsyncStore = /** @class */ (function () {
2413
3026
  AsyncStore.prototype.setContext = function (context) {
2414
3027
  Object.assign(this.__currentContents().context, context || {});
2415
3028
  };
3029
+ AsyncStore.prototype.setEventContext = function (eventContext) {
3030
+ Object.assign(this.__currentContents().eventContext, eventContext || {});
3031
+ };
3032
+ AsyncStore.prototype.clearEventContext = function () {
3033
+ this.__currentContents().eventContext = {};
3034
+ };
2416
3035
  AsyncStore.prototype.addBreadcrumb = function (breadcrumb) {
2417
3036
  if (this.__currentContents().breadcrumbs.length == this.breadcrumbsLimit) {
2418
3037
  this.__currentContents().breadcrumbs.shift();
@@ -2421,6 +3040,7 @@ var AsyncStore = /** @class */ (function () {
2421
3040
  };
2422
3041
  AsyncStore.prototype.clear = function () {
2423
3042
  this.__currentContents().context = {};
3043
+ this.__currentContents().eventContext = {};
2424
3044
  this.__currentContents().breadcrumbs = [];
2425
3045
  };
2426
3046
  AsyncStore.prototype.run = function (callback, request) {
@@ -2457,7 +3077,7 @@ var async_store_1 = async_store;
2457
3077
  */
2458
3078
  var StackedStore = /** @class */ (function () {
2459
3079
  function StackedStore(breadcrumbsLimit) {
2460
- this.contents = { context: {}, breadcrumbs: [] };
3080
+ this.contents = { context: {}, eventContext: {}, breadcrumbs: [] };
2461
3081
  this.asyncStore = async_store_1.AsyncStore.create(this.contents, breadcrumbsLimit);
2462
3082
  this.globalStore = core_1$1.GlobalStore.create(this.contents, breadcrumbsLimit);
2463
3083
  }
@@ -2475,6 +3095,12 @@ var StackedStore = /** @class */ (function () {
2475
3095
  StackedStore.prototype.setContext = function (context) {
2476
3096
  this.__activeStore().setContext(context);
2477
3097
  };
3098
+ StackedStore.prototype.setEventContext = function (eventContext) {
3099
+ this.__activeStore().setEventContext(eventContext);
3100
+ };
3101
+ StackedStore.prototype.clearEventContext = function () {
3102
+ this.__activeStore().clearEventContext();
3103
+ };
2478
3104
  StackedStore.prototype.addBreadcrumb = function (breadcrumb) {
2479
3105
  this.__activeStore().addBreadcrumb(breadcrumb);
2480
3106
  };
@@ -3121,7 +3747,7 @@ client.CheckInsClient = CheckInsClient;
3121
3747
  (0, uncaught_exception_plugin_1.default)(),
3122
3748
  (0, unhandled_rejection_plugin_1.default)(),
3123
3749
  (0, shutdown_plugin_1.default)(),
3124
- core_1.Plugins.events(),
3750
+ core_1.Plugins.consoleEvents(),
3125
3751
  ];
3126
3752
  var Honeybadger = /** @class */ (function (_super) {
3127
3753
  __extends(Honeybadger, _super);
@@ -3279,7 +3905,7 @@ client.CheckInsClient = CheckInsClient;
3279
3905
  var NOTIFIER = {
3280
3906
  name: '@honeybadger-io/js',
3281
3907
  url: 'https://github.com/honeybadger-io/honeybadger-js/tree/master/packages/js',
3282
- version: '6.15.2'
3908
+ version: '6.16.0'
3283
3909
  };
3284
3910
  var userAgent = function () {
3285
3911
  return "Honeybadger JS Server Client ".concat(NOTIFIER.version, ", ").concat(os_1.default.version(), "; ").concat(os_1.default.platform());