@honeybadger-io/js 6.16.0 → 6.16.2

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.
@@ -838,8 +838,36 @@
838
838
  return formattedVars;
839
839
  }
840
840
  exports.formatCGIData = formatCGIData;
841
+ /**
842
+ * Deep-copies a value exactly the way a JSON round trip does -- `toJSON` is
843
+ * honored, so dates become ISO strings -- except that circular references are
844
+ * replaced with '[RECURSION]' instead of throwing.
845
+ *
846
+ * Only true cycles are replaced: a value referenced twice in sibling branches
847
+ * is copied twice, as a JSON round trip would.
848
+ *
849
+ * Anything else a round trip rejects (a BigInt, a getter that throws) still
850
+ * throws here. Callers that must not fail are expected to handle it: silently
851
+ * substituting a placeholder would let a malformed value -- a string where an
852
+ * object is expected, say -- travel on as though it were valid.
853
+ */
841
854
  function clone(obj) {
842
- return JSON.parse(JSON.stringify(obj));
855
+ var ancestors = [];
856
+ return JSON.parse(JSON.stringify(obj, function (_key, value) {
857
+ if (typeof value !== 'object' || value === null) {
858
+ return value;
859
+ }
860
+ // `this` is the object `value` sits in, so anything stacked above it has
861
+ // been fully visited and is no longer an ancestor of `value`.
862
+ while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) {
863
+ ancestors.pop();
864
+ }
865
+ if (ancestors.indexOf(value) !== -1) {
866
+ return '[RECURSION]';
867
+ }
868
+ ancestors.push(value);
869
+ return value;
870
+ }));
843
871
  }
844
872
  exports.clone = clone;
845
873
  var THRESHOLD_COLUMN_NUMBER = 10000;
@@ -977,7 +1005,11 @@
977
1005
  };
978
1006
  GlobalStore.prototype.getContents = function (key) {
979
1007
  var value = key ? this.contents[key] : this.contents;
980
- return JSON.parse(JSON.stringify(value));
1008
+ // `clone` keeps JSON round trip semantics but tolerates values a bare
1009
+ // round trip throws on -- notably circular references, which arrive via
1010
+ // context or breadcrumb metadata the host application controls.
1011
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1012
+ return (0, util_1$3.clone)(value);
981
1013
  };
982
1014
  GlobalStore.prototype.setContext = function (context) {
983
1015
  this.contents.context = (0, util_1$3.merge)(this.contents.context, context || {});
@@ -1423,7 +1455,7 @@
1423
1455
  this.__notifier = {
1424
1456
  name: '@honeybadger-io/core',
1425
1457
  url: 'https://github.com/honeybadger-io/honeybadger-js/tree/master/packages/core',
1426
- version: '6.16.0'
1458
+ version: '6.16.2'
1427
1459
  };
1428
1460
  this.config = __assign$1(__assign$1({}, defaults_1.CONFIG), opts);
1429
1461
  this.__initStore();
@@ -1522,31 +1554,63 @@
1522
1554
  this.__store.clear();
1523
1555
  return this;
1524
1556
  };
1557
+ /**
1558
+ * Reporting an error must never break the host application, so any failure
1559
+ * inside the reporting path is logged and swallowed rather than thrown back
1560
+ * into the caller.
1561
+ */
1525
1562
  Client.prototype.notify = function (noticeable, name, extra) {
1526
1563
  var _this = this;
1527
1564
  if (name === void 0) { name = undefined; }
1528
1565
  if (extra === void 0) { extra = undefined; }
1529
- var notice = this.makeNotice(noticeable, name, extra);
1530
- // we need to have the source file data before the beforeNotifyHandlers,
1531
- // in case they modify them
1532
- var sourceCodeData = notice && notice.backtrace ? notice.backtrace.map(function (trace) { return (0, util_1$1.shallowClone)(trace); }) : null;
1533
- var preConditionsResult = this.__runPreconditions(notice);
1534
- if (preConditionsResult instanceof Error) {
1535
- (0, util_1$1.runAfterNotifyHandlers)(notice, this.__afterNotifyHandlers, preConditionsResult);
1566
+ // The body is inlined in this try/catch rather than delegated to a private
1567
+ // method: generated backtraces are trimmed by a fixed frame count
1568
+ // (DEFAULT_BACKTRACE_SHIFT), so an extra call frame here would shift every
1569
+ // generated backtrace into Honeybadger's own source.
1570
+ try {
1571
+ var notice_1 = this.makeNotice(noticeable, name, extra);
1572
+ // we need to have the source file data before the beforeNotifyHandlers,
1573
+ // in case they modify them
1574
+ var sourceCodeData_1 = notice_1 && notice_1.backtrace ? notice_1.backtrace.map(function (trace) { return (0, util_1$1.shallowClone)(trace); }) : null;
1575
+ var preConditionsResult = this.__runPreconditions(notice_1);
1576
+ if (preConditionsResult instanceof Error) {
1577
+ (0, util_1$1.runAfterNotifyHandlers)(notice_1, this.__afterNotifyHandlers, preConditionsResult);
1578
+ return false;
1579
+ }
1580
+ if (preConditionsResult instanceof Promise) {
1581
+ preConditionsResult.then(function (result) {
1582
+ if (result instanceof Error) {
1583
+ (0, util_1$1.runAfterNotifyHandlers)(notice_1, _this.__afterNotifyHandlers, result);
1584
+ return false;
1585
+ }
1586
+ return _this.__send(notice_1, sourceCodeData_1);
1587
+ }).catch(function (err) {
1588
+ // __send installs its own catch, which logs and runs the afterNotify
1589
+ // handlers. Anything arriving here failed before that chain existed,
1590
+ // so it is still unreported -- and notify() has already returned
1591
+ // true, leaving notifyAsync() waiting on those handlers.
1592
+ _this.__logReportingFailure(err);
1593
+ (0, util_1$1.runAfterNotifyHandlers)(notice_1, _this.__afterNotifyHandlers, err instanceof Error ? err : new Error(String(err)));
1594
+ });
1595
+ return true;
1596
+ }
1597
+ this.__send(notice_1, sourceCodeData_1).catch(function (_err) { });
1598
+ return true;
1599
+ }
1600
+ catch (err) {
1601
+ // Reporting an error must never break the host application, so a failure
1602
+ // in the reporting path is logged rather than thrown back at the caller.
1603
+ // notifyAsync() settles on the false return below.
1604
+ this.__logReportingFailure(err);
1536
1605
  return false;
1537
1606
  }
1538
- if (preConditionsResult instanceof Promise) {
1539
- preConditionsResult.then(function (result) {
1540
- if (result instanceof Error) {
1541
- (0, util_1$1.runAfterNotifyHandlers)(notice, _this.__afterNotifyHandlers, result);
1542
- return false;
1543
- }
1544
- return _this.__send(notice, sourceCodeData);
1545
- });
1546
- return true;
1607
+ };
1608
+ /** The logger is host-supplied, so even reporting a failure gets a guard. */
1609
+ Client.prototype.__logReportingFailure = function (err) {
1610
+ try {
1611
+ this.logger.error('Error report failed: an internal error occurred while reporting', err);
1547
1612
  }
1548
- this.__send(notice, sourceCodeData).catch(function (_err) { });
1549
- return true;
1613
+ catch (_loggerErr) { /* nothing left to report it with */ }
1550
1614
  };
1551
1615
  /**
1552
1616
  * An async version of {@link notify} that resolves only after the notice has been reported to Honeybadger.
@@ -1562,11 +1626,19 @@
1562
1626
  var applyAfterNotify = function (partialNotice) {
1563
1627
  var originalAfterNotify = partialNotice.afterNotify;
1564
1628
  partialNotice.afterNotify = function (err) {
1565
- originalAfterNotify === null || originalAfterNotify === void 0 ? void 0 : originalAfterNotify.call(_this, err);
1566
- if (err) {
1567
- return reject(err);
1629
+ // Settle from a `finally` so a throwing handler cannot leave the
1630
+ // caller awaiting forever; its error propagates as it always has.
1631
+ try {
1632
+ originalAfterNotify === null || originalAfterNotify === void 0 ? void 0 : originalAfterNotify.call(_this, err);
1633
+ }
1634
+ finally {
1635
+ if (err) {
1636
+ reject(err);
1637
+ }
1638
+ else {
1639
+ resolve();
1640
+ }
1568
1641
  }
1569
- resolve();
1570
1642
  };
1571
1643
  };
1572
1644
  // We have to respect any afterNotify hooks that come from the arguments
@@ -1590,7 +1662,14 @@
1590
1662
  objectToOverride = name = {};
1591
1663
  }
1592
1664
  applyAfterNotify(objectToOverride);
1593
- _this.notify(noticeable, name, extra);
1665
+ // `notify` never throws, so a false return is the only signal that the
1666
+ // report was abandoned. Every expected failure has already settled this
1667
+ // promise through the afterNotify hook above (a second reject is a
1668
+ // no-op); this catches the unexpected ones, which would otherwise leave
1669
+ // the caller awaiting forever.
1670
+ if (!_this.notify(noticeable, name, extra)) {
1671
+ reject(new Error('Error report failed: see logs for details'));
1672
+ }
1594
1673
  });
1595
1674
  };
1596
1675
  Client.prototype.makeNotice = function (noticeable, name, extra) {
@@ -2364,7 +2443,11 @@
2364
2443
  metadata: {
2365
2444
  selector: selector,
2366
2445
  text: text,
2367
- event: event
2446
+ // Frameworks decorate the native event with their own properties
2447
+ // (preact/compat adds a `nativeEvent` back-reference), so the raw
2448
+ // event may be circular. Sanitize it here rather than storing a
2449
+ // value we cannot serialize later.
2450
+ event: sanitize$1(event, 3)
2368
2451
  }
2369
2452
  });
2370
2453
  }, _window.location ? true : false); // In CloudFlare workers useCapture must be false. window.location is a hacky way to detect it.
@@ -3070,7 +3153,7 @@
3070
3153
  var NOTIFIER = {
3071
3154
  name: '@honeybadger-io/js',
3072
3155
  url: 'https://github.com/honeybadger-io/honeybadger-js/tree/master/packages/js',
3073
- version: '6.16.0'
3156
+ version: '6.16.2'
3074
3157
  };
3075
3158
  var userAgent = function () {
3076
3159
  if (typeof navigator !== 'undefined') {