@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.d.cts CHANGED
@@ -200,6 +200,12 @@ declare class BugbanClient {
200
200
  private flushTimer;
201
201
  /** Guard against reporting an error that our own reporting caused. */
202
202
  private sending;
203
+ /**
204
+ * Error objects already reported, so a throw seen by two handlers is only
205
+ * sent once. A WeakSet holds no strong reference, so entries disappear with
206
+ * the errors themselves and this can never grow into a leak.
207
+ */
208
+ private seen;
203
209
  private started;
204
210
  constructor(options?: BugbanOptions);
205
211
  get options(): ResolvedOptions;
@@ -283,7 +289,7 @@ declare function redactUrl(url: string, extraKeys?: string[]): string;
283
289
  * learned to avoid the hard way.
284
290
  */
285
291
  declare const SDK_NAME = "bugban-js";
286
- declare const SDK_VERSION = "1.0.0";
292
+ declare const SDK_VERSION = "1.0.1";
287
293
  /** 'browser' | 'node' | 'unknown' — reported on the install ping. */
288
294
  declare function runtimeName(): string;
289
295
  /**
package/dist/index.d.ts CHANGED
@@ -200,6 +200,12 @@ declare class BugbanClient {
200
200
  private flushTimer;
201
201
  /** Guard against reporting an error that our own reporting caused. */
202
202
  private sending;
203
+ /**
204
+ * Error objects already reported, so a throw seen by two handlers is only
205
+ * sent once. A WeakSet holds no strong reference, so entries disappear with
206
+ * the errors themselves and this can never grow into a leak.
207
+ */
208
+ private seen;
203
209
  private started;
204
210
  constructor(options?: BugbanOptions);
205
211
  get options(): ResolvedOptions;
@@ -283,7 +289,7 @@ declare function redactUrl(url: string, extraKeys?: string[]): string;
283
289
  * learned to avoid the hard way.
284
290
  */
285
291
  declare const SDK_NAME = "bugban-js";
286
- declare const SDK_VERSION = "1.0.0";
292
+ declare const SDK_VERSION = "1.0.1";
287
293
  /** 'browser' | 'node' | 'unknown' — reported on the install ping. */
288
294
  declare function runtimeName(): string;
289
295
  /**
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/env.ts
2
2
  var SDK_NAME = "bugban-js";
3
- var SDK_VERSION = "1.0.0";
3
+ var SDK_VERSION = "1.0.1";
4
4
  var isBrowser = (() => {
5
5
  try {
6
6
  return typeof window !== "undefined" && typeof window.document !== "undefined";
@@ -141,9 +141,21 @@ function section(value, defaults, defaultOn) {
141
141
  }
142
142
  var CONSOLE_DEFAULTS = {
143
143
  levels: ["log", "info", "warn", "error", "debug"],
144
- // Only console.error becomes an event. console.warn is far too noisy in a
145
- // typical app React alone emits several per render in development.
146
- captureAsEvent: ["error"]
144
+ /**
145
+ * Whether console.error becomes an event of its own, or stays a breadcrumb.
146
+ *
147
+ * Browser: yes. Most real front-end failures never reach window.onerror —
148
+ * the app catches the error, logs it, and renders "something went wrong".
149
+ * console.error is the only signal those leave behind.
150
+ *
151
+ * Node: no. Server frameworks log every error they handle, so capturing the
152
+ * log too reports the same failure twice AND defeats filtering: NestJS logs
153
+ * a deliberate 404 that the exception filter correctly skipped, and the
154
+ * console path turned it back into an event. Observed exactly that. The
155
+ * framework adapter already reports real errors with route context, so here
156
+ * the console is breadcrumbs only.
157
+ */
158
+ captureAsEvent: isBrowser ? ["error"] : []
147
159
  };
148
160
  var NETWORK_DEFAULTS = {
149
161
  enabled: true,
@@ -331,7 +343,7 @@ var Transport = class {
331
343
  const nav = navigator;
332
344
  if (typeof nav.sendBeacon !== "function") return false;
333
345
  const blob = new Blob([JSON.stringify({ ...body, _key: this.apiKey })], {
334
- type: "application/json"
346
+ type: "text/plain;charset=UTF-8"
335
347
  });
336
348
  return nav.sendBeacon(url, blob);
337
349
  }, false);
@@ -890,6 +902,12 @@ var LEVEL_MAP = {
890
902
  warn: "warning",
891
903
  error: "error"
892
904
  };
905
+ function hasErrorArg(args) {
906
+ for (const a of args) {
907
+ if (a instanceof Error) return true;
908
+ }
909
+ return false;
910
+ }
893
911
  function installConsole(opts, hooks) {
894
912
  if (typeof console === "undefined") return () => {
895
913
  };
@@ -907,7 +925,15 @@ function installConsole(opts, hooks) {
907
925
  const message = args.map((a) => typeof a === "string" ? a : truncate(a, 200)).join(" ");
908
926
  hooks.breadcrumb(level, truncate(message, 500), args);
909
927
  if ((method === "error" || method === "warn") && opts.captureAsEvent.indexOf(method) !== -1) {
910
- hooks.capture(level, message, args);
928
+ if (hasErrorArg(args)) {
929
+ const deferred = () => safe(() => hooks.capture(level, message, args));
930
+ safe(() => {
931
+ const t = setTimeout(deferred, 50);
932
+ if (t && typeof t.unref === "function") t.unref();
933
+ });
934
+ } else {
935
+ hooks.capture(level, message, args);
936
+ }
911
937
  }
912
938
  inside = false;
913
939
  }
@@ -1320,6 +1346,12 @@ var BugbanClient = class {
1320
1346
  this.flushTimer = null;
1321
1347
  /** Guard against reporting an error that our own reporting caused. */
1322
1348
  this.sending = false;
1349
+ /**
1350
+ * Error objects already reported, so a throw seen by two handlers is only
1351
+ * sent once. A WeakSet holds no strong reference, so entries disappear with
1352
+ * the errors themselves and this can never grow into a leak.
1353
+ */
1354
+ this.seen = /* @__PURE__ */ new WeakSet();
1323
1355
  this.started = false;
1324
1356
  this.opts = resolveOptions(options);
1325
1357
  this.transport = new Transport(this.opts.host, this.opts.apiKey, this.opts.debug);
@@ -1475,6 +1507,10 @@ var BugbanClient = class {
1475
1507
  capture(error, extra = {}) {
1476
1508
  if (!this.opts.enabled) return;
1477
1509
  if (this.sending) return;
1510
+ if (typeof error === "object" && error !== null) {
1511
+ if (this.seen.has(error)) return;
1512
+ safe(() => this.seen.add(error));
1513
+ }
1478
1514
  safe(() => {
1479
1515
  var _a;
1480
1516
  const norm = normalizeError(error);
@@ -1624,40 +1660,62 @@ var BugbanClient = class {
1624
1660
  };
1625
1661
 
1626
1662
  // src/index.ts
1627
- var client = null;
1663
+ var SINGLETON_KEY = "__bugban_client__";
1664
+ function readClient() {
1665
+ var _a;
1666
+ return (_a = safe(() => {
1667
+ var _a2;
1668
+ return (_a2 = globalThis[SINGLETON_KEY]) != null ? _a2 : null;
1669
+ }, null)) != null ? _a : null;
1670
+ }
1671
+ function writeClient(value) {
1672
+ safe(() => {
1673
+ globalThis[SINGLETON_KEY] = value;
1674
+ });
1675
+ }
1628
1676
  function init(options = {}) {
1629
- if (client) safe(() => client.close());
1630
- client = new BugbanClient(options);
1631
- client.start();
1632
- return client;
1677
+ const previous = readClient();
1678
+ if (previous) safe(() => previous.close());
1679
+ const created = new BugbanClient(options);
1680
+ writeClient(created);
1681
+ created.start();
1682
+ return created;
1633
1683
  }
1634
1684
  function getClient() {
1635
- return client;
1685
+ return readClient();
1636
1686
  }
1637
1687
  function capture(error, extra) {
1638
- client == null ? void 0 : client.capture(error, extra);
1688
+ var _a;
1689
+ (_a = readClient()) == null ? void 0 : _a.capture(error, extra);
1639
1690
  }
1640
1691
  function captureMessage(message, level = "info") {
1641
- client == null ? void 0 : client.captureMessage(message, level);
1692
+ var _a;
1693
+ (_a = readClient()) == null ? void 0 : _a.captureMessage(message, level);
1642
1694
  }
1643
1695
  function setUser(user) {
1644
- client == null ? void 0 : client.setUser(user);
1696
+ var _a;
1697
+ (_a = readClient()) == null ? void 0 : _a.setUser(user);
1645
1698
  }
1646
1699
  function setContext(key, value) {
1647
- client == null ? void 0 : client.setContext(key, value);
1700
+ var _a;
1701
+ (_a = readClient()) == null ? void 0 : _a.setContext(key, value);
1648
1702
  }
1649
1703
  function addBreadcrumb(crumb) {
1650
- client == null ? void 0 : client.addBreadcrumb(crumb);
1704
+ var _a;
1705
+ (_a = readClient()) == null ? void 0 : _a.addBreadcrumb(crumb);
1651
1706
  }
1652
1707
  function recordMetric(metric) {
1653
- client == null ? void 0 : client.recordMetric(metric);
1708
+ var _a;
1709
+ (_a = readClient()) == null ? void 0 : _a.recordMetric(metric);
1654
1710
  }
1655
1711
  async function flush() {
1656
- await (client == null ? void 0 : client.flush());
1712
+ var _a;
1713
+ await ((_a = readClient()) == null ? void 0 : _a.flush());
1657
1714
  }
1658
1715
  function close() {
1659
- client == null ? void 0 : client.close();
1660
- client = null;
1716
+ var _a;
1717
+ (_a = readClient()) == null ? void 0 : _a.close();
1718
+ writeClient(null);
1661
1719
  }
1662
1720
  var Bugban = {
1663
1721
  VERSION: SDK_VERSION,