@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.
@@ -35,7 +35,7 @@
35
35
 
36
36
  var src = {};
37
37
 
38
- var events = {};
38
+ var console_events = {};
39
39
 
40
40
  var util$1 = {};
41
41
 
@@ -242,7 +242,7 @@
242
242
  }
243
243
  };
244
244
  Object.defineProperty(exports, "__esModule", { value: true });
245
- 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;
245
+ 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;
246
246
  /* eslint-disable prefer-rest-params */
247
247
  var stackTraceParser = __importStar(require$$0$1);
248
248
  function merge(obj1, obj2) {
@@ -418,6 +418,88 @@
418
418
  };
419
419
  }
420
420
  exports.runBeforeNotifyHandlers = runBeforeNotifyHandlers;
421
+ function runBeforeEventHandlers(payload, handlers, logger) {
422
+ return __awaiter(this, void 0, void 0, function () {
423
+ var i, len, result, err_1;
424
+ return __generator(this, function (_a) {
425
+ switch (_a.label) {
426
+ case 0:
427
+ i = 0, len = handlers.length;
428
+ _a.label = 1;
429
+ case 1:
430
+ if (!(i < len)) return [3 /*break*/, 7];
431
+ result = void 0;
432
+ _a.label = 2;
433
+ case 2:
434
+ _a.trys.push([2, 4, , 5]);
435
+ return [4 /*yield*/, handlers[i](payload)];
436
+ case 3:
437
+ result = _a.sent();
438
+ return [3 /*break*/, 5];
439
+ case 4:
440
+ err_1 = _a.sent();
441
+ // A buggy handler should not suppress unrelated events. Log and treat as no-op.
442
+ logger === null || logger === void 0 ? void 0 : logger.error('beforeEvent handler threw; continuing', err_1);
443
+ return [3 /*break*/, 6];
444
+ case 5:
445
+ if (result === false) {
446
+ return [2 /*return*/, false];
447
+ }
448
+ _a.label = 6;
449
+ case 6:
450
+ i++;
451
+ return [3 /*break*/, 1];
452
+ case 7: return [2 /*return*/, true];
453
+ }
454
+ });
455
+ });
456
+ }
457
+ exports.runBeforeEventHandlers = runBeforeEventHandlers;
458
+ function resolveInsights(config) {
459
+ var _a, _b;
460
+ var insights = config.insights;
461
+ if (!insights || insights.enabled !== true) {
462
+ return { console: false, http: false };
463
+ }
464
+ return {
465
+ console: (_a = insights.console) !== null && _a !== void 0 ? _a : false,
466
+ http: (_b = insights.http) !== null && _b !== void 0 ? _b : false,
467
+ };
468
+ }
469
+ exports.resolveInsights = resolveInsights;
470
+ function fnv1a(str) {
471
+ var h = 0x811c9dc5;
472
+ for (var i = 0, len = str.length; i < len; i++) {
473
+ h ^= str.charCodeAt(i);
474
+ h = Math.imul(h, 0x01000193);
475
+ }
476
+ return h >>> 0;
477
+ }
478
+ function clampSampleRate(rate) {
479
+ if (rate < 0)
480
+ return 0;
481
+ if (rate > 100)
482
+ return 100;
483
+ return rate;
484
+ }
485
+ function shouldSampleEvent(event, configRate) {
486
+ var meta = event._hb;
487
+ var override = meta && Number.isFinite(meta.sampleRate) ? meta.sampleRate : undefined;
488
+ var rawRate = override !== undefined ? override : configRate;
489
+ // Guard against non-finite rates (e.g. NaN from Number(invalidConfig)); an
490
+ // unusable rate must not silently drop every event, so fall back to send-all.
491
+ var rate = clampSampleRate(Number.isFinite(rawRate) ? rawRate : 100);
492
+ if (rate <= 0)
493
+ return false;
494
+ if (rate >= 100)
495
+ return true;
496
+ var requestId = event.request_id;
497
+ if (typeof requestId === 'string' && requestId.length > 0) {
498
+ return fnv1a(requestId) % 100 < rate;
499
+ }
500
+ return Math.random() * 100 < rate;
501
+ }
502
+ exports.shouldSampleEvent = shouldSampleEvent;
421
503
  function runAfterNotifyHandlers(notice, handlers, error) {
422
504
  if (notice && notice.afterNotify) {
423
505
  notice.afterNotify(error, notice);
@@ -838,7 +920,7 @@
838
920
 
839
921
  } (util$1));
840
922
 
841
- Object.defineProperty(events, "__esModule", { value: true });
923
+ Object.defineProperty(console_events, "__esModule", { value: true });
842
924
  var util_1$4 = util$1;
843
925
  function default_1$4(_window) {
844
926
  if (_window === void 0) { _window = (0, util_1$4.globalThisOrWindow)(); }
@@ -846,7 +928,7 @@
846
928
  shouldReloadOnConfigure: false,
847
929
  load: function (client) {
848
930
  function sendEventsToInsights() {
849
- return client.config.eventsEnabled;
931
+ return (0, util_1$4.resolveInsights)(client.config).console;
850
932
  }
851
933
  if (!sendEventsToInsights()) {
852
934
  return;
@@ -873,7 +955,7 @@
873
955
  }
874
956
  };
875
957
  }
876
- events.default = default_1$4;
958
+ console_events.default = default_1$4;
877
959
 
878
960
  var client = {};
879
961
 
@@ -900,6 +982,12 @@
900
982
  GlobalStore.prototype.setContext = function (context) {
901
983
  this.contents.context = (0, util_1$3.merge)(this.contents.context, context || {});
902
984
  };
985
+ GlobalStore.prototype.setEventContext = function (eventContext) {
986
+ this.contents.eventContext = (0, util_1$3.merge)(this.contents.eventContext, eventContext || {});
987
+ };
988
+ GlobalStore.prototype.clearEventContext = function () {
989
+ this.contents.eventContext = {};
990
+ };
903
991
  GlobalStore.prototype.addBreadcrumb = function (breadcrumb) {
904
992
  if (this.contents.breadcrumbs.length == this.breadcrumbsLimit) {
905
993
  this.contents.breadcrumbs.shift();
@@ -908,6 +996,7 @@
908
996
  };
909
997
  GlobalStore.prototype.clear = function () {
910
998
  this.contents.context = {};
999
+ this.contents.eventContext = {};
911
1000
  this.contents.breadcrumbs = [];
912
1001
  };
913
1002
  GlobalStore.prototype.run = function (callback) {
@@ -917,7 +1006,7 @@
917
1006
  }());
918
1007
  store.GlobalStore = GlobalStore;
919
1008
 
920
- var throttled_events_logger = {};
1009
+ var throttled_events_worker = {};
921
1010
 
922
1011
  class NdJson {
923
1012
  static parse(data) {
@@ -952,8 +1041,18 @@
952
1041
  revision: null,
953
1042
  reportData: null,
954
1043
  breadcrumbsEnabled: true,
955
- // we could decide the value of eventsEnabled based on `env` and `developmentEnvironments`
1044
+ // deprecated: `eventsEnabled: true` auto-enables insights.enabled + insights.console
956
1045
  eventsEnabled: false,
1046
+ insights: {
1047
+ enabled: false,
1048
+ console: false,
1049
+ http: false,
1050
+ },
1051
+ events: {
1052
+ dispatchIntervalSeconds: 10,
1053
+ bulkThreshold: 500,
1054
+ sampleRatePercentage: 100,
1055
+ },
957
1056
  maxBreadcrumbs: 40,
958
1057
  maxObjectDepth: 8,
959
1058
  logger: console,
@@ -1016,72 +1115,179 @@
1016
1115
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
1017
1116
  }
1018
1117
  };
1019
- Object.defineProperty(throttled_events_logger, "__esModule", { value: true });
1020
- throttled_events_logger.ThrottledEventsLogger = void 0;
1118
+ Object.defineProperty(throttled_events_worker, "__esModule", { value: true });
1119
+ throttled_events_worker.ThrottledEventsWorker = void 0;
1021
1120
  var json_nd_1 = require$$0;
1022
1121
  var util_1$2 = util$1;
1023
1122
  var defaults_1$1 = defaults;
1024
- var ThrottledEventsLogger = /** @class */ (function () {
1025
- function ThrottledEventsLogger(config, transport) {
1123
+ var ThrottledEventsWorker = /** @class */ (function () {
1124
+ function ThrottledEventsWorker(config, transport) {
1026
1125
  this.config = config;
1027
1126
  this.transport = transport;
1028
1127
  this.queue = [];
1029
- this.isProcessing = false;
1128
+ this.cooldownTimer = null;
1129
+ // Timestamp (ms) until which the post-send cooldown window is active. A timer
1130
+ // is only armed while there are queued events waiting for this window; when the
1131
+ // queue drains empty we keep the timestamp but drop the timer, so an idle worker
1132
+ // never holds a pending timer (which would otherwise keep a Node process alive
1133
+ // for the full interval.
1134
+ this.cooldownUntil = 0;
1135
+ this.inFlight = null;
1030
1136
  this.config = __assign$2(__assign$2({}, defaults_1$1.CONFIG), config);
1031
1137
  this.logger = this.originalLogger();
1032
1138
  }
1033
- ThrottledEventsLogger.prototype.configure = function (opts) {
1139
+ ThrottledEventsWorker.prototype.configure = function (opts) {
1034
1140
  for (var k in opts) {
1035
1141
  this.config[k] = opts[k];
1036
1142
  }
1037
1143
  };
1038
- ThrottledEventsLogger.prototype.log = function (payload) {
1144
+ ThrottledEventsWorker.prototype.log = function (payload) {
1039
1145
  this.queue.push(payload);
1040
- if (!this.isProcessing) {
1146
+ if (this.inFlight) {
1147
+ return;
1148
+ }
1149
+ // Crossing the bulk threshold preempts any pending cooldown and dispatches now.
1150
+ if (this.queue.length >= this.bulkThreshold()) {
1151
+ this.clearCooldownTimer();
1041
1152
  this.processQueue();
1153
+ return;
1154
+ }
1155
+ // A timer is already counting down the cooldown window; the queued event
1156
+ // will be picked up when it fires.
1157
+ if (this.cooldownTimer) {
1158
+ return;
1042
1159
  }
1160
+ // We're inside a cooldown window but no timer is armed (the queue had drained
1161
+ // empty after the previous send). Arm one for the remaining time so this event
1162
+ // still respects the interval.
1163
+ var remaining = this.cooldownRemainingMs();
1164
+ if (remaining > 0) {
1165
+ this.scheduleCooldownTimer(remaining);
1166
+ return;
1167
+ }
1168
+ this.processQueue();
1043
1169
  };
1044
- ThrottledEventsLogger.prototype.flushAsync = function () {
1170
+ ThrottledEventsWorker.prototype.flushAsync = function () {
1171
+ var _this = this;
1172
+ var _a;
1045
1173
  this.logger.debug('[Honeybadger] Flushing events');
1046
- return this.send();
1174
+ this.clearCooldownTimer();
1175
+ var previous = (_a = this.inFlight) !== null && _a !== void 0 ? _a : Promise.resolve();
1176
+ var flush = previous.then(function () { return _this.drainAll(); });
1177
+ // Mark the flush as in-flight so a concurrent log() doesn't start an
1178
+ // overlapping processQueue() drain while we're draining here.
1179
+ this.inFlight = flush;
1180
+ var clear = function () {
1181
+ if (_this.inFlight === flush) {
1182
+ _this.inFlight = null;
1183
+ }
1184
+ };
1185
+ flush.then(clear, clear);
1186
+ return flush;
1187
+ };
1188
+ ThrottledEventsWorker.prototype.drainAll = function () {
1189
+ return __awaiter$2(this, void 0, void 0, function () {
1190
+ return __generator$2(this, function (_a) {
1191
+ switch (_a.label) {
1192
+ case 0:
1193
+ if (!(this.queue.length > 0)) return [3 /*break*/, 2];
1194
+ return [4 /*yield*/, this.send()];
1195
+ case 1:
1196
+ _a.sent();
1197
+ return [3 /*break*/, 0];
1198
+ case 2: return [2 /*return*/];
1199
+ }
1200
+ });
1201
+ });
1047
1202
  };
1048
- ThrottledEventsLogger.prototype.processQueue = function () {
1203
+ ThrottledEventsWorker.prototype.processQueue = function () {
1049
1204
  var _this = this;
1050
- if (this.queue.length === 0 || this.isProcessing) {
1205
+ if (this.queue.length === 0 || this.inFlight) {
1051
1206
  return;
1052
1207
  }
1053
- this.isProcessing = true;
1054
- this.send()
1055
- .then(function () {
1056
- setTimeout(function () {
1057
- _this.isProcessing = false;
1058
- _this.processQueue();
1059
- }, 50);
1060
- })
1208
+ var inFlight = this.send()
1061
1209
  .catch(function (error) {
1062
1210
  _this.logger.error('[Honeybadger] Error making HTTP request:', error);
1063
- // Continue processing the queue even if there's an error
1064
- setTimeout(function () {
1065
- _this.isProcessing = false;
1066
- _this.processQueue();
1067
- }, 50);
1211
+ })
1212
+ .then(function () {
1213
+ // Only reset if we're still the current operation; a flushAsync() may
1214
+ // have taken ownership of inFlight while this send was resolving.
1215
+ if (_this.inFlight === inFlight) {
1216
+ _this.inFlight = null;
1217
+ _this.scheduleNextDispatch();
1218
+ }
1068
1219
  });
1220
+ this.inFlight = inFlight;
1221
+ };
1222
+ ThrottledEventsWorker.prototype.scheduleNextDispatch = function () {
1223
+ var intervalMs = this.dispatchIntervalMs();
1224
+ // Capture the cooldown deadline at dispatch time so a later config change to
1225
+ // the interval doesn't retroactively shorten a window already in progress.
1226
+ this.cooldownUntil = Date.now() + intervalMs;
1227
+ if (this.queue.length >= this.bulkThreshold()) {
1228
+ this.processQueue();
1229
+ return;
1230
+ }
1231
+ // Only arm a timer when there's queued work waiting for it. If the queue is
1232
+ // empty, `cooldownUntil` alone enforces the window: the next log() will arm a
1233
+ // timer for whatever time remains, and an idle worker holds no timer at all.
1234
+ if (this.queue.length > 0) {
1235
+ this.scheduleCooldownTimer(intervalMs);
1236
+ }
1237
+ };
1238
+ ThrottledEventsWorker.prototype.cooldownRemainingMs = function () {
1239
+ if (!this.cooldownUntil) {
1240
+ return 0;
1241
+ }
1242
+ return Math.max(0, this.cooldownUntil - Date.now());
1243
+ };
1244
+ ThrottledEventsWorker.prototype.scheduleCooldownTimer = function (ms) {
1245
+ var _this = this;
1246
+ this.cooldownTimer = setTimeout(function () {
1247
+ _this.cooldownTimer = null;
1248
+ _this.processQueue();
1249
+ }, ms);
1250
+ // In Node, don't let a pending cooldown timer keep the process alive for the
1251
+ // whole batching interval. `unref` lets the event loop go idle so the process
1252
+ // can exit (or a shutdown / `beforeExit` handler can run) promptly instead of
1253
+ // blocking for `dispatchIntervalSeconds`. Queued events aren't lost: a
1254
+ // `flushAsync()` on the way out still drains them before exit. (`unref` is
1255
+ // Node-only; the browser's numeric setTimeout handle has no such method.)
1256
+ if (typeof this.cooldownTimer === 'object' && this.cooldownTimer !== null && typeof this.cooldownTimer.unref === 'function') {
1257
+ this.cooldownTimer.unref();
1258
+ }
1259
+ };
1260
+ ThrottledEventsWorker.prototype.clearCooldownTimer = function () {
1261
+ if (this.cooldownTimer) {
1262
+ clearTimeout(this.cooldownTimer);
1263
+ this.cooldownTimer = null;
1264
+ }
1069
1265
  };
1070
- ThrottledEventsLogger.prototype.send = function () {
1266
+ ThrottledEventsWorker.prototype.send = function () {
1071
1267
  return __awaiter$2(this, void 0, void 0, function () {
1072
1268
  var eventsData, data;
1073
1269
  return __generator$2(this, function (_a) {
1074
1270
  if (this.queue.length === 0) {
1075
1271
  return [2 /*return*/];
1076
1272
  }
1077
- eventsData = this.queue.slice();
1078
- this.queue = [];
1273
+ eventsData = this.queue.splice(0, this.bulkThreshold());
1079
1274
  data = json_nd_1.NdJson.stringify(eventsData);
1080
1275
  return [2 /*return*/, this.makeHttpRequest(data)];
1081
1276
  });
1082
1277
  });
1083
1278
  };
1084
- ThrottledEventsLogger.prototype.makeHttpRequest = function (data) {
1279
+ ThrottledEventsWorker.prototype.bulkThreshold = function () {
1280
+ var _a;
1281
+ var v = (_a = this.config.events) === null || _a === void 0 ? void 0 : _a.bulkThreshold;
1282
+ return typeof v === 'number' && v > 0 ? v : defaults_1$1.CONFIG.events.bulkThreshold;
1283
+ };
1284
+ ThrottledEventsWorker.prototype.dispatchIntervalMs = function () {
1285
+ var _a;
1286
+ var v = (_a = this.config.events) === null || _a === void 0 ? void 0 : _a.dispatchIntervalSeconds;
1287
+ var seconds = typeof v === 'number' && v >= 0 ? v : defaults_1$1.CONFIG.events.dispatchIntervalSeconds;
1288
+ return seconds * 1000;
1289
+ };
1290
+ ThrottledEventsWorker.prototype.makeHttpRequest = function (data) {
1085
1291
  return __awaiter$2(this, void 0, void 0, function () {
1086
1292
  var _this = this;
1087
1293
  return __generator$2(this, function (_a) {
@@ -1096,10 +1302,13 @@
1096
1302
  maxObjectDepth: this.config.maxObjectDepth,
1097
1303
  logger: this.logger,
1098
1304
  }, data)
1099
- .then(function () {
1100
- if (_this.config.debug) {
1305
+ .then(function (resp) {
1306
+ if ([200, 201].includes(resp.statusCode)) {
1101
1307
  _this.logger.debug('[Honeybadger] Events sent successfully');
1102
1308
  }
1309
+ else {
1310
+ _this.logger.debug("[Honeybadger] Events failed[".concat(resp.statusCode, "]: ").concat(resp.body));
1311
+ }
1103
1312
  })
1104
1313
  .catch(function (err) {
1105
1314
  _this.logger.error("[Honeybadger] Error sending events: ".concat(err.message));
@@ -1110,28 +1319,40 @@
1110
1319
  /**
1111
1320
  * todo: improve this
1112
1321
  *
1113
- * The EventsLogger overrides the console methods to enable automatic instrumentation
1322
+ * The events plugin overrides the console methods to enable automatic instrumentation
1114
1323
  * of console logs to the Honeybadger API.
1115
1324
  * So if we want to log something in here we need to use the original methods.
1116
1325
  */
1117
- ThrottledEventsLogger.prototype.originalLogger = function () {
1118
- var _a, _b, _c, _d, _e;
1326
+ ThrottledEventsWorker.prototype.originalLogger = function () {
1327
+ var _this = this;
1328
+ var _a, _b, _c, _d;
1119
1329
  return {
1120
1330
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1121
1331
  log: (_a = console.log.__hb_original) !== null && _a !== void 0 ? _a : console.log,
1122
1332
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1123
1333
  info: (_b = console.info.__hb_original) !== null && _b !== void 0 ? _b : console.info,
1334
+ debug: function () {
1335
+ var _a;
1336
+ var args = [];
1337
+ for (var _i = 0; _i < arguments.length; _i++) {
1338
+ args[_i] = arguments[_i];
1339
+ }
1340
+ if (!_this.config.debug) {
1341
+ return;
1342
+ }
1343
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1344
+ var func = (_a = console.debug.__hb_original) !== null && _a !== void 0 ? _a : console.debug;
1345
+ return func.apply(void 0, args);
1346
+ },
1124
1347
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1125
- debug: (_c = console.debug.__hb_original) !== null && _c !== void 0 ? _c : console.debug,
1126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1127
- warn: (_d = console.warn.__hb_original) !== null && _d !== void 0 ? _d : console.warn,
1348
+ warn: (_c = console.warn.__hb_original) !== null && _c !== void 0 ? _c : console.warn,
1128
1349
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1129
- error: (_e = console.error.__hb_original) !== null && _e !== void 0 ? _e : console.error,
1350
+ error: (_d = console.error.__hb_original) !== null && _d !== void 0 ? _d : console.error,
1130
1351
  };
1131
1352
  };
1132
- return ThrottledEventsLogger;
1353
+ return ThrottledEventsWorker;
1133
1354
  }());
1134
- throttled_events_logger.ThrottledEventsLogger = ThrottledEventsLogger;
1355
+ throttled_events_worker.ThrottledEventsWorker = ThrottledEventsWorker;
1135
1356
 
1136
1357
  var __assign$1 = (commonjsGlobal && commonjsGlobal.__assign) || function () {
1137
1358
  __assign$1 = Object.assign || function(t) {
@@ -1184,7 +1405,7 @@
1184
1405
  client.Client = void 0;
1185
1406
  var util_1$1 = util$1;
1186
1407
  var store_1 = store;
1187
- var throttled_events_logger_1 = throttled_events_logger;
1408
+ var throttled_events_worker_1 = throttled_events_worker;
1188
1409
  var defaults_1 = defaults;
1189
1410
  // Split at commas and spaces
1190
1411
  var TAG_SEPARATOR = /,|\s+/;
@@ -1196,17 +1417,20 @@
1196
1417
  this.__pluginsLoaded = false;
1197
1418
  this.__store = null;
1198
1419
  this.__beforeNotifyHandlers = [];
1420
+ this.__beforeEventHandlers = [];
1199
1421
  this.__afterNotifyHandlers = [];
1422
+ this.__pendingEvents = new Set();
1200
1423
  this.__notifier = {
1201
1424
  name: '@honeybadger-io/core',
1202
1425
  url: 'https://github.com/honeybadger-io/honeybadger-js/tree/master/packages/core',
1203
- version: '6.15.2'
1426
+ version: '6.16.0'
1204
1427
  };
1205
1428
  this.config = __assign$1(__assign$1({}, defaults_1.CONFIG), opts);
1206
1429
  this.__initStore();
1207
1430
  this.__transport = transport;
1208
- this.__eventsLogger = new throttled_events_logger_1.ThrottledEventsLogger(this.config, this.__transport);
1431
+ this.__eventsWorker = new throttled_events_worker_1.ThrottledEventsWorker(this.config, this.__transport);
1209
1432
  this.logger = (0, util_1$1.logger)(this);
1433
+ this.__applyEventsEnabledShim(opts);
1210
1434
  }
1211
1435
  Client.prototype.getVersion = function () {
1212
1436
  return this.__notifier.version;
@@ -1229,10 +1453,24 @@
1229
1453
  for (var k in opts) {
1230
1454
  this.config[k] = opts[k];
1231
1455
  }
1232
- this.__eventsLogger.configure(this.config);
1456
+ this.__applyEventsEnabledShim(opts);
1457
+ this.__eventsWorker.configure(this.config);
1233
1458
  this.loadPlugins();
1234
1459
  return this;
1235
1460
  };
1461
+ /**
1462
+ * Backwards compatibility: `eventsEnabled: true` used to opt into console events.
1463
+ * The deprecated flag auto-enables `insights.enabled` and `insights.console`;
1464
+ * explicit user-provided insights values win over the shim.
1465
+ */
1466
+ Client.prototype.__applyEventsEnabledShim = function (opts) {
1467
+ var _a;
1468
+ if (opts.eventsEnabled !== true) {
1469
+ return;
1470
+ }
1471
+ this.logger.warn('Deprecation warning: `eventsEnabled` has been deprecated; please use `insights.enabled` and `insights.console` instead.');
1472
+ this.config.insights = __assign$1(__assign$1(__assign$1({}, ((_a = this.config.insights) !== null && _a !== void 0 ? _a : {})), { enabled: true, console: true }), opts.insights);
1473
+ };
1236
1474
  Client.prototype.loadPlugins = function () {
1237
1475
  var _this = this;
1238
1476
  var pluginsToLoad = this.__pluginsLoaded
@@ -1242,12 +1480,16 @@
1242
1480
  this.__pluginsLoaded = true;
1243
1481
  };
1244
1482
  Client.prototype.__initStore = function () {
1245
- this.__store = new store_1.GlobalStore({ context: {}, breadcrumbs: [] }, this.config.maxBreadcrumbs);
1483
+ this.__store = new store_1.GlobalStore({ context: {}, eventContext: {}, breadcrumbs: [] }, this.config.maxBreadcrumbs);
1246
1484
  };
1247
1485
  Client.prototype.beforeNotify = function (handler) {
1248
1486
  this.__beforeNotifyHandlers.push(handler);
1249
1487
  return this;
1250
1488
  };
1489
+ Client.prototype.beforeEvent = function (handler) {
1490
+ this.__beforeEventHandlers.push(handler);
1491
+ return this;
1492
+ };
1251
1493
  Client.prototype.afterNotify = function (handler) {
1252
1494
  this.__afterNotifyHandlers.push(handler);
1253
1495
  return this;
@@ -1258,6 +1500,16 @@
1258
1500
  }
1259
1501
  return this;
1260
1502
  };
1503
+ Client.prototype.setEventContext = function (eventContext) {
1504
+ if (typeof eventContext === 'object' && eventContext != null) {
1505
+ this.__store.setEventContext(eventContext);
1506
+ }
1507
+ return this;
1508
+ };
1509
+ Client.prototype.clearEventContext = function () {
1510
+ this.__store.clearEventContext();
1511
+ return this;
1512
+ };
1261
1513
  Client.prototype.resetContext = function (context) {
1262
1514
  this.logger.warn('Deprecation warning: `Honeybadger.resetContext()` has been deprecated; please use `Honeybadger.clear()` instead.');
1263
1515
  this.__store.clear();
@@ -1412,19 +1664,59 @@
1412
1664
  this.event('log', data);
1413
1665
  };
1414
1666
  Client.prototype.event = function (type, data) {
1667
+ var _this = this;
1415
1668
  var _a;
1416
1669
  if (typeof type === 'object') {
1417
1670
  data = type;
1418
1671
  type = (_a = type['event_type']) !== null && _a !== void 0 ? _a : undefined;
1419
1672
  }
1420
- this.__eventsLogger.log(__assign$1({ event_type: type, ts: new Date().toISOString() }, data));
1673
+ var eventContext = this.__store.getContents('eventContext') || {};
1674
+ var payload = __assign$1(__assign$1({ event_type: type, ts: new Date().toISOString() }, eventContext), data);
1675
+ // Track in-flight handler chains so flushAsync() can await them before
1676
+ // delegating to the events logger; otherwise a caller that awaits flushAsync()
1677
+ // immediately after event() may flush before the payload enters the queue.
1678
+ var inFlight = (0, util_1$1.runBeforeEventHandlers)(payload, this.__beforeEventHandlers, this.logger)
1679
+ .then(function (shouldSend) {
1680
+ var _a, _b;
1681
+ if (!shouldSend) {
1682
+ _this.logger.debug('skipping event: beforeEvent handler returned false');
1683
+ return;
1684
+ }
1685
+ var sampleRate = (_b = (_a = _this.config.events) === null || _a === void 0 ? void 0 : _a.sampleRatePercentage) !== null && _b !== void 0 ? _b : 100;
1686
+ if (!(0, util_1$1.shouldSampleEvent)(payload, sampleRate)) {
1687
+ _this.logger.debug('skipping event: dropped by sampleRatePercentage');
1688
+ return;
1689
+ }
1690
+ delete payload._hb;
1691
+ _this.__eventsWorker.log(payload);
1692
+ })
1693
+ .catch(function (err) {
1694
+ // should not happen, because each handler is wrapped with a try/catch
1695
+ _this.logger.error('beforeEvent handler chain failed; dropping event', err);
1696
+ })
1697
+ .finally(function () {
1698
+ _this.__pendingEvents.delete(inFlight);
1699
+ });
1700
+ this.__pendingEvents.add(inFlight);
1421
1701
  };
1422
1702
  /**
1423
1703
  * This method currently flushes the event (Insights) queue.
1424
1704
  * In the future, it should also flush the error queue (assuming an error throttler is implemented).
1425
1705
  */
1426
1706
  Client.prototype.flushAsync = function () {
1427
- return this.__eventsLogger.flushAsync();
1707
+ return __awaiter$1(this, void 0, void 0, function () {
1708
+ return __generator$1(this, function (_a) {
1709
+ switch (_a.label) {
1710
+ case 0:
1711
+ if (!(this.__pendingEvents.size > 0)) return [3 /*break*/, 2];
1712
+ return [4 /*yield*/, Promise.allSettled(Array.from(this.__pendingEvents))];
1713
+ case 1:
1714
+ _a.sent();
1715
+ _a.label = 2;
1716
+ case 2: return [2 /*return*/, this.__eventsWorker.flushAsync()];
1717
+ }
1718
+ });
1719
+ });
1428
1720
  };
1429
1721
  Client.prototype.__getBreadcrumbs = function () {
1430
1722
  return this.__store.getContents('breadcrumbs').slice();
@@ -1616,7 +1908,7 @@
1616
1908
  };
1617
1909
  Object.defineProperty(exports, "__esModule", { value: true });
1618
1910
  exports.Plugins = exports.Defaults = exports.Util = exports.Types = exports.Client = void 0;
1619
- var events_1 = __importDefault(events);
1911
+ var console_events_1 = __importDefault(console_events);
1620
1912
  var client_1 = client;
1621
1913
  Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return client_1.Client; } });
1622
1914
  __exportStar(store, exports);
@@ -1624,7 +1916,12 @@
1624
1916
  exports.Util = __importStar(util$1);
1625
1917
  exports.Defaults = __importStar(defaults);
1626
1918
  exports.Plugins = {
1627
- events: events_1.default
1919
+ consoleEvents: console_events_1.default,
1920
+ /**
1921
+ * @deprecated Use `consoleEvents` instead. Kept as an alias for backwards
1922
+ * compatibility and will be removed in a future major version.
1923
+ */
1924
+ events: console_events_1.default,
1628
1925
  };
1629
1926
 
1630
1927
  } (src));
@@ -2773,7 +3070,7 @@
2773
3070
  var NOTIFIER = {
2774
3071
  name: '@honeybadger-io/js',
2775
3072
  url: 'https://github.com/honeybadger-io/honeybadger-js/tree/master/packages/js',
2776
- version: '6.15.2'
3073
+ version: '6.16.0'
2777
3074
  };
2778
3075
  var userAgent = function () {
2779
3076
  if (typeof navigator !== 'undefined') {
@@ -2788,7 +3085,7 @@
2788
3085
  (0, timers_1.default)(),
2789
3086
  (0, event_listeners_1.default)(),
2790
3087
  (0, breadcrumbs_1.default)(),
2791
- core_1.Plugins.events(),
3088
+ core_1.Plugins.consoleEvents(),
2792
3089
  ]
2793
3090
  });
2794
3091
  singleton.setNotifier(NOTIFIER);