@bugwatch/core 0.1.0 → 0.2.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.
package/dist/index.js CHANGED
@@ -4,6 +4,12 @@ var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
7
13
  var __esm = (fn, res) => function __init() {
8
14
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
15
  };
@@ -45,7 +51,7 @@ var init_transport = __esm({
45
51
  method: "POST",
46
52
  headers: {
47
53
  "Content-Type": "application/json",
48
- "X-API-Key": this.apiKey,
54
+ "Authorization": `Bearer ${this.apiKey}`,
49
55
  "X-Bugwatch-SDK": event.sdk?.name || "bugwatch-core",
50
56
  "X-Bugwatch-SDK-Version": event.sdk?.version || "0.1.0"
51
57
  },
@@ -134,6 +140,13 @@ var init_transport = __esm({
134
140
  this.timer = null;
135
141
  }
136
142
  }
143
+ /**
144
+ * Close the transport, flushing any remaining events and stopping the timer.
145
+ */
146
+ async close() {
147
+ await this.flush();
148
+ this.destroy();
149
+ }
137
150
  };
138
151
  }
139
152
  });
@@ -282,6 +295,126 @@ var init_fingerprint = __esm({
282
295
  }
283
296
  });
284
297
 
298
+ // src/context.ts
299
+ var context_exports = {};
300
+ __export(context_exports, {
301
+ addRequestBreadcrumb: () => addRequestBreadcrumb,
302
+ createScopedContext: () => createScopedContext,
303
+ getMergedContext: () => getMergedContext,
304
+ getRequestContext: () => getRequestContext,
305
+ hasRequestContext: () => hasRequestContext,
306
+ runWithContext: () => runWithContext,
307
+ runWithContextAsync: () => runWithContextAsync,
308
+ setRequestExtra: () => setRequestExtra,
309
+ setRequestTag: () => setRequestTag,
310
+ setRequestUser: () => setRequestUser
311
+ });
312
+ function createScopedContext() {
313
+ return {
314
+ user: null,
315
+ tags: {},
316
+ extra: {},
317
+ breadcrumbs: []
318
+ };
319
+ }
320
+ function isNodeEnvironment() {
321
+ return typeof process !== "undefined" && process.versions?.node !== void 0 && typeof __require !== "undefined";
322
+ }
323
+ function getAsyncLocalStorage() {
324
+ if (asyncLocalStorage) {
325
+ return asyncLocalStorage;
326
+ }
327
+ if (!isNodeEnvironment()) {
328
+ return null;
329
+ }
330
+ try {
331
+ const asyncHooks = __require("async_hooks");
332
+ asyncLocalStorage = new asyncHooks.AsyncLocalStorage();
333
+ return asyncLocalStorage;
334
+ } catch {
335
+ return null;
336
+ }
337
+ }
338
+ function runWithContext(context, fn) {
339
+ const storage = getAsyncLocalStorage();
340
+ if (storage) {
341
+ return storage.run(context, fn);
342
+ }
343
+ return fn();
344
+ }
345
+ async function runWithContextAsync(context, fn) {
346
+ const storage = getAsyncLocalStorage();
347
+ if (storage) {
348
+ return storage.run(context, fn);
349
+ }
350
+ return fn();
351
+ }
352
+ function getRequestContext() {
353
+ const storage = getAsyncLocalStorage();
354
+ return storage?.getStore();
355
+ }
356
+ function hasRequestContext() {
357
+ return getRequestContext() !== void 0;
358
+ }
359
+ function setRequestUser(user) {
360
+ const ctx = getRequestContext();
361
+ if (ctx) {
362
+ ctx.user = user;
363
+ return true;
364
+ }
365
+ return false;
366
+ }
367
+ function setRequestTag(key, value) {
368
+ const ctx = getRequestContext();
369
+ if (ctx) {
370
+ ctx.tags[key] = value;
371
+ return true;
372
+ }
373
+ return false;
374
+ }
375
+ function setRequestExtra(key, value) {
376
+ const ctx = getRequestContext();
377
+ if (ctx) {
378
+ ctx.extra[key] = value;
379
+ return true;
380
+ }
381
+ return false;
382
+ }
383
+ function addRequestBreadcrumb(breadcrumb, maxBreadcrumbs = 100) {
384
+ const ctx = getRequestContext();
385
+ if (ctx) {
386
+ ctx.breadcrumbs.push(breadcrumb);
387
+ if (ctx.breadcrumbs.length > maxBreadcrumbs) {
388
+ ctx.breadcrumbs = ctx.breadcrumbs.slice(-maxBreadcrumbs);
389
+ }
390
+ return true;
391
+ }
392
+ return false;
393
+ }
394
+ function getMergedContext(globalUser, globalTags, globalExtra, globalBreadcrumbs) {
395
+ const reqCtx = getRequestContext();
396
+ if (!reqCtx) {
397
+ return {
398
+ user: globalUser,
399
+ tags: globalTags,
400
+ extra: globalExtra,
401
+ breadcrumbs: globalBreadcrumbs
402
+ };
403
+ }
404
+ return {
405
+ user: reqCtx.user || globalUser,
406
+ tags: { ...globalTags, ...reqCtx.tags },
407
+ extra: { ...globalExtra, ...reqCtx.extra },
408
+ breadcrumbs: [...globalBreadcrumbs, ...reqCtx.breadcrumbs]
409
+ };
410
+ }
411
+ var asyncLocalStorage;
412
+ var init_context = __esm({
413
+ "src/context.ts"() {
414
+ asyncLocalStorage = null;
415
+ }
416
+ });
417
+
285
418
  // src/client.ts
286
419
  var client_exports = {};
287
420
  __export(client_exports, {
@@ -292,12 +425,52 @@ function generateEventId() {
292
425
  const random = Math.random().toString(36).substring(2, 10);
293
426
  return `${timestamp}${random}`;
294
427
  }
295
- var SDK_NAME, SDK_VERSION, DEFAULT_OPTIONS; exports.Bugwatch = void 0;
428
+ var RingBuffer, SDK_NAME, SDK_VERSION, DEFAULT_OPTIONS; exports.Bugwatch = void 0;
296
429
  var init_client = __esm({
297
430
  "src/client.ts"() {
298
431
  init_transport();
299
432
  init_stacktrace();
300
433
  init_fingerprint();
434
+ init_context();
435
+ RingBuffer = class {
436
+ constructor(maxSize) {
437
+ this.maxSize = maxSize;
438
+ this.buffer = new Array(maxSize);
439
+ }
440
+ buffer;
441
+ head = 0;
442
+ count = 0;
443
+ push(item) {
444
+ this.buffer[this.head] = item;
445
+ this.head = (this.head + 1) % this.maxSize;
446
+ if (this.count < this.maxSize) {
447
+ this.count++;
448
+ }
449
+ }
450
+ toArray() {
451
+ if (this.count === 0) {
452
+ return [];
453
+ }
454
+ const result = [];
455
+ const start = this.count < this.maxSize ? 0 : this.head;
456
+ for (let i = 0; i < this.count; i++) {
457
+ const index = (start + i) % this.maxSize;
458
+ const item = this.buffer[index];
459
+ if (item !== void 0) {
460
+ result.push(item);
461
+ }
462
+ }
463
+ return result;
464
+ }
465
+ clear() {
466
+ this.buffer = new Array(this.maxSize);
467
+ this.head = 0;
468
+ this.count = 0;
469
+ }
470
+ get length() {
471
+ return this.count;
472
+ }
473
+ };
301
474
  SDK_NAME = "@bugwatch/core";
302
475
  SDK_VERSION = "0.1.0";
303
476
  DEFAULT_OPTIONS = {
@@ -310,7 +483,7 @@ var init_client = __esm({
310
483
  exports.Bugwatch = class {
311
484
  options;
312
485
  transport;
313
- breadcrumbs = [];
486
+ breadcrumbs;
314
487
  tags = {};
315
488
  extra = {};
316
489
  user = null;
@@ -319,6 +492,7 @@ var init_client = __esm({
319
492
  constructor(options) {
320
493
  this.options = { ...DEFAULT_OPTIONS, ...options };
321
494
  this.transport = this.createTransport();
495
+ this.breadcrumbs = new RingBuffer(this.options.maxBreadcrumbs || 100);
322
496
  if (options.tags) {
323
497
  this.tags = { ...options.tags };
324
498
  }
@@ -364,7 +538,17 @@ var init_client = __esm({
364
538
  return "";
365
539
  }
366
540
  const event = this.createEventFromError(error, context);
367
- const processedEvent = this.options.beforeSend ? this.options.beforeSend(event) : event;
541
+ let processedEvent = event;
542
+ if (this.options.beforeSend) {
543
+ try {
544
+ processedEvent = this.options.beforeSend(event);
545
+ } catch (err) {
546
+ if (this.options.debug) {
547
+ console.error("[Bugwatch] beforeSend threw an error:", err);
548
+ }
549
+ processedEvent = event;
550
+ }
551
+ }
368
552
  if (!processedEvent) {
369
553
  if (this.options.debug) {
370
554
  console.log("[Bugwatch] Event dropped by beforeSend");
@@ -386,7 +570,17 @@ var init_client = __esm({
386
570
  message,
387
571
  level
388
572
  });
389
- const processedEvent = this.options.beforeSend ? this.options.beforeSend(event) : event;
573
+ let processedEvent = event;
574
+ if (this.options.beforeSend) {
575
+ try {
576
+ processedEvent = this.options.beforeSend(event);
577
+ } catch (err) {
578
+ if (this.options.debug) {
579
+ console.error("[Bugwatch] beforeSend threw an error:", err);
580
+ }
581
+ processedEvent = event;
582
+ }
583
+ }
390
584
  if (!processedEvent) {
391
585
  return "";
392
586
  }
@@ -403,10 +597,6 @@ var init_client = __esm({
403
597
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
404
598
  };
405
599
  this.breadcrumbs.push(crumb);
406
- const max = this.options.maxBreadcrumbs || 100;
407
- if (this.breadcrumbs.length > max) {
408
- this.breadcrumbs = this.breadcrumbs.slice(-max);
409
- }
410
600
  }
411
601
  /**
412
602
  * Set user context
@@ -430,7 +620,7 @@ var init_client = __esm({
430
620
  * Clear breadcrumbs
431
621
  */
432
622
  clearBreadcrumbs() {
433
- this.breadcrumbs = [];
623
+ this.breadcrumbs.clear();
434
624
  }
435
625
  /**
436
626
  * Create an event from an Error object
@@ -454,6 +644,12 @@ var init_client = __esm({
454
644
  * Create a base event
455
645
  */
456
646
  createEvent(partial) {
647
+ const mergedContext = getMergedContext(
648
+ this.user,
649
+ this.tags,
650
+ this.extra,
651
+ this.breadcrumbs.toArray()
652
+ );
457
653
  const event = {
458
654
  event_id: generateEventId(),
459
655
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -462,17 +658,17 @@ var init_client = __esm({
462
658
  message: partial.message || "",
463
659
  environment: this.options.environment,
464
660
  release: this.options.release,
465
- tags: { ...this.tags, ...partial.tags },
466
- extra: { ...this.extra, ...partial.extra },
467
- breadcrumbs: [...this.breadcrumbs],
661
+ tags: { ...mergedContext.tags, ...partial.tags },
662
+ extra: { ...mergedContext.extra, ...partial.extra },
663
+ breadcrumbs: mergedContext.breadcrumbs,
468
664
  sdk: {
469
665
  name: SDK_NAME,
470
666
  version: SDK_VERSION
471
667
  },
472
668
  ...partial
473
669
  };
474
- if (this.user || partial.user) {
475
- event.user = { ...this.user, ...partial.user };
670
+ if (mergedContext.user || partial.user) {
671
+ event.user = { ...mergedContext.user, ...partial.user };
476
672
  }
477
673
  if (event.exception) {
478
674
  const fingerprint = fingerprintFromException(event.exception);
@@ -516,6 +712,95 @@ var init_client = __esm({
516
712
  }
517
713
  return "javascript";
518
714
  }
715
+ /**
716
+ * Flush any pending events.
717
+ * Call this before process exit to ensure no events are lost.
718
+ */
719
+ async flush() {
720
+ if (this.transport.flush) {
721
+ await this.transport.flush();
722
+ }
723
+ }
724
+ /**
725
+ * Close the client and release any resources.
726
+ * This flushes any pending events and closes the transport.
727
+ * After calling this method, the client should not be used again.
728
+ */
729
+ async close() {
730
+ if (this.transport.close) {
731
+ await this.transport.close();
732
+ } else {
733
+ await this.flush();
734
+ }
735
+ this.initialized = false;
736
+ }
737
+ };
738
+ }
739
+ });
740
+
741
+ // src/env.ts
742
+ var env_exports = {};
743
+ __export(env_exports, {
744
+ ENV_VARS: () => exports.ENV_VARS,
745
+ getEnvConfig: () => getEnvConfig,
746
+ hasApiKey: () => hasApiKey,
747
+ validateApiKey: () => validateApiKey
748
+ });
749
+ function getEnvVar(name) {
750
+ if (typeof process !== "undefined" && process.env) {
751
+ return process.env[name];
752
+ }
753
+ return void 0;
754
+ }
755
+ function getEnvConfig() {
756
+ const config = {};
757
+ const apiKey = getEnvVar(exports.ENV_VARS.API_KEY);
758
+ if (apiKey) {
759
+ config.apiKey = apiKey;
760
+ }
761
+ const environment = getEnvVar(exports.ENV_VARS.ENVIRONMENT);
762
+ if (environment) {
763
+ config.environment = environment;
764
+ }
765
+ const release = getEnvVar(exports.ENV_VARS.RELEASE);
766
+ if (release) {
767
+ config.release = release;
768
+ }
769
+ const debug = getEnvVar(exports.ENV_VARS.DEBUG);
770
+ if (debug === "true") {
771
+ config.debug = true;
772
+ }
773
+ const endpoint = getEnvVar(exports.ENV_VARS.ENDPOINT);
774
+ if (endpoint) {
775
+ config.endpoint = endpoint;
776
+ }
777
+ return config;
778
+ }
779
+ function validateApiKey(key) {
780
+ if (!key) {
781
+ throw new Error(
782
+ "Bugwatch: API key required. Set BUGWATCH_API_KEY environment variable or pass { apiKey: '...' }"
783
+ );
784
+ }
785
+ if (!key.startsWith("bw_")) {
786
+ const preview = key.length > 10 ? `${key.slice(0, 6)}...` : "[hidden]";
787
+ console.warn(
788
+ `Bugwatch: API key should start with 'bw_'. Got '${preview}'`
789
+ );
790
+ }
791
+ }
792
+ function hasApiKey(explicitKey) {
793
+ return Boolean(explicitKey || getEnvVar(exports.ENV_VARS.API_KEY));
794
+ }
795
+ exports.ENV_VARS = void 0;
796
+ var init_env = __esm({
797
+ "src/env.ts"() {
798
+ exports.ENV_VARS = {
799
+ API_KEY: "BUGWATCH_API_KEY",
800
+ ENVIRONMENT: "BUGWATCH_ENVIRONMENT",
801
+ RELEASE: "BUGWATCH_RELEASE",
802
+ DEBUG: "BUGWATCH_DEBUG",
803
+ ENDPOINT: "BUGWATCH_ENDPOINT"
519
804
  };
520
805
  }
521
806
  });
@@ -525,26 +810,57 @@ init_client();
525
810
  init_transport();
526
811
  init_stacktrace();
527
812
  init_fingerprint();
813
+ init_env();
814
+ init_context();
528
815
  var globalClient = null;
816
+ var lazyInitWarned = false;
529
817
  function init(options) {
530
818
  const { Bugwatch: Bugwatch2 } = (init_client(), __toCommonJS(client_exports));
531
- const client = new Bugwatch2(options);
819
+ const { getEnvConfig: getEnvConfig2, validateApiKey: validateApiKey2 } = (init_env(), __toCommonJS(env_exports));
820
+ const envConfig = getEnvConfig2();
821
+ const mergedOptions = { ...envConfig, ...options };
822
+ validateApiKey2(mergedOptions.apiKey);
823
+ const client = new Bugwatch2(mergedOptions);
532
824
  globalClient = client;
825
+ lazyInitWarned = false;
533
826
  return client;
534
827
  }
535
828
  function getClient() {
536
829
  return globalClient;
537
830
  }
831
+ function tryLazyInit() {
832
+ if (globalClient) return true;
833
+ const { getEnvConfig: getEnvConfig2, hasApiKey: hasApiKey2 } = (init_env(), __toCommonJS(env_exports));
834
+ if (!hasApiKey2()) {
835
+ return false;
836
+ }
837
+ try {
838
+ const { Bugwatch: Bugwatch2 } = (init_client(), __toCommonJS(client_exports));
839
+ const envConfig = getEnvConfig2();
840
+ if (!envConfig.apiKey) {
841
+ return false;
842
+ }
843
+ const client = new Bugwatch2(envConfig);
844
+ globalClient = client;
845
+ if (envConfig.debug && !lazyInitWarned) {
846
+ console.warn("[Bugwatch] Auto-initialized from BUGWATCH_API_KEY environment variable");
847
+ lazyInitWarned = true;
848
+ }
849
+ return true;
850
+ } catch {
851
+ return false;
852
+ }
853
+ }
538
854
  function captureException(error, context) {
539
- if (!globalClient) {
540
- console.warn("[Bugwatch] SDK not initialized. Call init() first.");
855
+ if (!globalClient && !tryLazyInit()) {
856
+ console.warn("[Bugwatch] SDK not initialized. Call init() first or set BUGWATCH_API_KEY environment variable.");
541
857
  return "";
542
858
  }
543
859
  return globalClient.captureException(error, context);
544
860
  }
545
861
  function captureMessage(message, level) {
546
- if (!globalClient) {
547
- console.warn("[Bugwatch] SDK not initialized. Call init() first.");
862
+ if (!globalClient && !tryLazyInit()) {
863
+ console.warn("[Bugwatch] SDK not initialized. Call init() first or set BUGWATCH_API_KEY environment variable.");
548
864
  return "";
549
865
  }
550
866
  return globalClient.captureMessage(message, level);
@@ -553,38 +869,93 @@ function addBreadcrumb(breadcrumb) {
553
869
  if (!globalClient) {
554
870
  return;
555
871
  }
872
+ const { addRequestBreadcrumb: addRequestBreadcrumb2 } = (init_context(), __toCommonJS(context_exports));
873
+ const crumb = {
874
+ ...breadcrumb,
875
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
876
+ };
877
+ if (addRequestBreadcrumb2(crumb, globalClient.getOptions().maxBreadcrumbs || 100)) {
878
+ return;
879
+ }
556
880
  globalClient.addBreadcrumb(breadcrumb);
557
881
  }
558
882
  function setUser(user) {
559
883
  if (!globalClient) {
560
884
  return;
561
885
  }
886
+ const { setRequestUser: setRequestUser2 } = (init_context(), __toCommonJS(context_exports));
887
+ if (setRequestUser2(user)) {
888
+ return;
889
+ }
562
890
  globalClient.setUser(user);
563
891
  }
564
892
  function setTag(key, value) {
565
893
  if (!globalClient) {
566
894
  return;
567
895
  }
896
+ const { setRequestTag: setRequestTag2 } = (init_context(), __toCommonJS(context_exports));
897
+ if (setRequestTag2(key, value)) {
898
+ return;
899
+ }
568
900
  globalClient.setTag(key, value);
569
901
  }
570
902
  function setExtra(key, value) {
571
903
  if (!globalClient) {
572
904
  return;
573
905
  }
906
+ const { setRequestExtra: setRequestExtra2 } = (init_context(), __toCommonJS(context_exports));
907
+ if (setRequestExtra2(key, value)) {
908
+ return;
909
+ }
574
910
  globalClient.setExtra(key, value);
575
911
  }
912
+ async function flush() {
913
+ if (!globalClient) {
914
+ console.warn("[Bugwatch] SDK not initialized. Call init() first.");
915
+ return;
916
+ }
917
+ await globalClient.flush();
918
+ }
919
+ async function close() {
920
+ if (!globalClient) {
921
+ console.warn("[Bugwatch] SDK not initialized. Call init() first.");
922
+ return;
923
+ }
924
+ await globalClient.close();
925
+ globalClient = null;
926
+ }
927
+ function reset() {
928
+ globalClient = null;
929
+ lazyInitWarned = false;
930
+ }
576
931
 
577
932
  exports.addBreadcrumb = addBreadcrumb;
933
+ exports.addRequestBreadcrumb = addRequestBreadcrumb;
578
934
  exports.captureException = captureException;
579
935
  exports.captureMessage = captureMessage;
936
+ exports.close = close;
937
+ exports.createScopedContext = createScopedContext;
580
938
  exports.extractErrorInfo = extractErrorInfo;
581
939
  exports.fingerprintFromException = fingerprintFromException;
940
+ exports.flush = flush;
582
941
  exports.generateFingerprint = generateFingerprint;
583
942
  exports.getClient = getClient;
943
+ exports.getEnvConfig = getEnvConfig;
944
+ exports.getMergedContext = getMergedContext;
945
+ exports.getRequestContext = getRequestContext;
946
+ exports.hasApiKey = hasApiKey;
947
+ exports.hasRequestContext = hasRequestContext;
584
948
  exports.init = init;
585
949
  exports.parseStackTrace = parseStackTrace;
950
+ exports.reset = reset;
951
+ exports.runWithContext = runWithContext;
952
+ exports.runWithContextAsync = runWithContextAsync;
586
953
  exports.setExtra = setExtra;
954
+ exports.setRequestExtra = setRequestExtra;
955
+ exports.setRequestTag = setRequestTag;
956
+ exports.setRequestUser = setRequestUser;
587
957
  exports.setTag = setTag;
588
958
  exports.setUser = setUser;
959
+ exports.validateApiKey = validateApiKey;
589
960
  //# sourceMappingURL=index.js.map
590
961
  //# sourceMappingURL=index.js.map