@bugban/js 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/env.ts
4
4
  var SDK_NAME = "bugban-js";
5
- var SDK_VERSION = "1.0.0";
5
+ var SDK_VERSION = "1.0.1";
6
6
  var isBrowser = (() => {
7
7
  try {
8
8
  return typeof window !== "undefined" && typeof window.document !== "undefined";
@@ -143,9 +143,21 @@ function section(value, defaults, defaultOn) {
143
143
  }
144
144
  var CONSOLE_DEFAULTS = {
145
145
  levels: ["log", "info", "warn", "error", "debug"],
146
- // Only console.error becomes an event. console.warn is far too noisy in a
147
- // typical app React alone emits several per render in development.
148
- captureAsEvent: ["error"]
146
+ /**
147
+ * Whether console.error becomes an event of its own, or stays a breadcrumb.
148
+ *
149
+ * Browser: yes. Most real front-end failures never reach window.onerror —
150
+ * the app catches the error, logs it, and renders "something went wrong".
151
+ * console.error is the only signal those leave behind.
152
+ *
153
+ * Node: no. Server frameworks log every error they handle, so capturing the
154
+ * log too reports the same failure twice AND defeats filtering: NestJS logs
155
+ * a deliberate 404 that the exception filter correctly skipped, and the
156
+ * console path turned it back into an event. Observed exactly that. The
157
+ * framework adapter already reports real errors with route context, so here
158
+ * the console is breadcrumbs only.
159
+ */
160
+ captureAsEvent: isBrowser ? ["error"] : []
149
161
  };
150
162
  var NETWORK_DEFAULTS = {
151
163
  enabled: true,
@@ -333,7 +345,7 @@ var Transport = class {
333
345
  const nav = navigator;
334
346
  if (typeof nav.sendBeacon !== "function") return false;
335
347
  const blob = new Blob([JSON.stringify({ ...body, _key: this.apiKey })], {
336
- type: "application/json"
348
+ type: "text/plain;charset=UTF-8"
337
349
  });
338
350
  return nav.sendBeacon(url, blob);
339
351
  }, false);
@@ -892,6 +904,12 @@ var LEVEL_MAP = {
892
904
  warn: "warning",
893
905
  error: "error"
894
906
  };
907
+ function hasErrorArg(args) {
908
+ for (const a of args) {
909
+ if (a instanceof Error) return true;
910
+ }
911
+ return false;
912
+ }
895
913
  function installConsole(opts, hooks) {
896
914
  if (typeof console === "undefined") return () => {
897
915
  };
@@ -909,7 +927,15 @@ function installConsole(opts, hooks) {
909
927
  const message = args.map((a) => typeof a === "string" ? a : truncate(a, 200)).join(" ");
910
928
  hooks.breadcrumb(level, truncate(message, 500), args);
911
929
  if ((method === "error" || method === "warn") && opts.captureAsEvent.indexOf(method) !== -1) {
912
- hooks.capture(level, message, args);
930
+ if (hasErrorArg(args)) {
931
+ const deferred = () => safe(() => hooks.capture(level, message, args));
932
+ safe(() => {
933
+ const t = setTimeout(deferred, 50);
934
+ if (t && typeof t.unref === "function") t.unref();
935
+ });
936
+ } else {
937
+ hooks.capture(level, message, args);
938
+ }
913
939
  }
914
940
  inside = false;
915
941
  }
@@ -1322,6 +1348,12 @@ var BugbanClient = class {
1322
1348
  this.flushTimer = null;
1323
1349
  /** Guard against reporting an error that our own reporting caused. */
1324
1350
  this.sending = false;
1351
+ /**
1352
+ * Error objects already reported, so a throw seen by two handlers is only
1353
+ * sent once. A WeakSet holds no strong reference, so entries disappear with
1354
+ * the errors themselves and this can never grow into a leak.
1355
+ */
1356
+ this.seen = /* @__PURE__ */ new WeakSet();
1325
1357
  this.started = false;
1326
1358
  this.opts = resolveOptions(options);
1327
1359
  this.transport = new Transport(this.opts.host, this.opts.apiKey, this.opts.debug);
@@ -1477,6 +1509,10 @@ var BugbanClient = class {
1477
1509
  capture(error, extra = {}) {
1478
1510
  if (!this.opts.enabled) return;
1479
1511
  if (this.sending) return;
1512
+ if (typeof error === "object" && error !== null) {
1513
+ if (this.seen.has(error)) return;
1514
+ safe(() => this.seen.add(error));
1515
+ }
1480
1516
  safe(() => {
1481
1517
  var _a;
1482
1518
  const norm = normalizeError(error);
@@ -1626,40 +1662,62 @@ var BugbanClient = class {
1626
1662
  };
1627
1663
 
1628
1664
  // src/index.ts
1629
- var client = null;
1665
+ var SINGLETON_KEY = "__bugban_client__";
1666
+ function readClient() {
1667
+ var _a;
1668
+ return (_a = safe(() => {
1669
+ var _a2;
1670
+ return (_a2 = globalThis[SINGLETON_KEY]) != null ? _a2 : null;
1671
+ }, null)) != null ? _a : null;
1672
+ }
1673
+ function writeClient(value) {
1674
+ safe(() => {
1675
+ globalThis[SINGLETON_KEY] = value;
1676
+ });
1677
+ }
1630
1678
  function init(options = {}) {
1631
- if (client) safe(() => client.close());
1632
- client = new BugbanClient(options);
1633
- client.start();
1634
- return client;
1679
+ const previous = readClient();
1680
+ if (previous) safe(() => previous.close());
1681
+ const created = new BugbanClient(options);
1682
+ writeClient(created);
1683
+ created.start();
1684
+ return created;
1635
1685
  }
1636
1686
  function getClient() {
1637
- return client;
1687
+ return readClient();
1638
1688
  }
1639
1689
  function capture(error, extra) {
1640
- client == null ? void 0 : client.capture(error, extra);
1690
+ var _a;
1691
+ (_a = readClient()) == null ? void 0 : _a.capture(error, extra);
1641
1692
  }
1642
1693
  function captureMessage(message, level = "info") {
1643
- client == null ? void 0 : client.captureMessage(message, level);
1694
+ var _a;
1695
+ (_a = readClient()) == null ? void 0 : _a.captureMessage(message, level);
1644
1696
  }
1645
1697
  function setUser(user) {
1646
- client == null ? void 0 : client.setUser(user);
1698
+ var _a;
1699
+ (_a = readClient()) == null ? void 0 : _a.setUser(user);
1647
1700
  }
1648
1701
  function setContext(key, value) {
1649
- client == null ? void 0 : client.setContext(key, value);
1702
+ var _a;
1703
+ (_a = readClient()) == null ? void 0 : _a.setContext(key, value);
1650
1704
  }
1651
1705
  function addBreadcrumb(crumb) {
1652
- client == null ? void 0 : client.addBreadcrumb(crumb);
1706
+ var _a;
1707
+ (_a = readClient()) == null ? void 0 : _a.addBreadcrumb(crumb);
1653
1708
  }
1654
1709
  function recordMetric(metric) {
1655
- client == null ? void 0 : client.recordMetric(metric);
1710
+ var _a;
1711
+ (_a = readClient()) == null ? void 0 : _a.recordMetric(metric);
1656
1712
  }
1657
1713
  async function flush() {
1658
- await (client == null ? void 0 : client.flush());
1714
+ var _a;
1715
+ await ((_a = readClient()) == null ? void 0 : _a.flush());
1659
1716
  }
1660
1717
  function close() {
1661
- client == null ? void 0 : client.close();
1662
- client = null;
1718
+ var _a;
1719
+ (_a = readClient()) == null ? void 0 : _a.close();
1720
+ writeClient(null);
1663
1721
  }
1664
1722
  var Bugban = {
1665
1723
  VERSION: SDK_VERSION,