@pulseboard/react-native 0.2.1 → 0.2.3

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.mts CHANGED
@@ -55,7 +55,10 @@ type PulseEvent = {
55
55
  context?: EnrichedContext;
56
56
  };
57
57
  type SDKConfig = {
58
- apiKey: string;
58
+ apiKey?: string;
59
+ organisation?: string;
60
+ product?: string;
61
+ project?: string;
59
62
  environment: Environment;
60
63
  debug?: boolean;
61
64
  autoCapture?: boolean;
@@ -69,6 +72,27 @@ type TrackOptions = {
69
72
  type CaptureErrorOptions = {
70
73
  payload?: Record<string, unknown>;
71
74
  };
75
+ type LogLevel = "debug" | "info" | "warn" | "error";
76
+ type LogEntry = {
77
+ apiKey: string;
78
+ level: LogLevel;
79
+ message: string;
80
+ meta?: Record<string, unknown>;
81
+ sessionId?: string;
82
+ appVersion?: string;
83
+ timestamp: string;
84
+ };
85
+ type LogOptions = {
86
+ meta?: Record<string, unknown>;
87
+ };
88
+ type FeedbackType = "bug" | "feature" | "general";
89
+ type FeedbackOptions = {
90
+ type?: FeedbackType;
91
+ meta?: Record<string, unknown>;
92
+ userEmail?: string;
93
+ userName?: string;
94
+ screenshot?: string;
95
+ };
72
96
 
73
97
  declare class PulseBoardSDK {
74
98
  private config;
@@ -79,24 +103,36 @@ declare class PulseBoardSDK {
79
103
  private flushTimer;
80
104
  private initialized;
81
105
  private readonly PULSEBOARD_API;
106
+ private logQueue;
107
+ private logFlushTimer;
108
+ private originalConsole;
109
+ private consoleCapturing;
110
+ private get apiKey();
82
111
  init(config: SDKConfig): void;
112
+ private resolveApiKey;
113
+ private completeInit;
83
114
  getContext(): Promise<EnrichedContext>;
84
115
  identify(user: UserContext): void;
85
116
  clearUser(): void;
86
117
  track(name: string, options?: TrackOptions): void;
87
118
  metric(name: string, value: number, options?: TrackOptions): void;
88
119
  captureError(error: Error, options?: CaptureErrorOptions): void;
120
+ log(level: LogLevel, message: string, options?: LogOptions): void;
121
+ captureConsole(): void;
122
+ releaseConsole(): void;
123
+ flushLogs(): Promise<void>;
89
124
  startSession(): void;
90
125
  endSession(duration?: number): void;
91
126
  trackScreen(screenName: string, loadTime?: number): void;
92
127
  trackApiCall(endpoint: string, httpMethod: string, statusCode: number, duration: number, payloadSize?: number): void;
93
128
  trackCrash(error: Error, isFatal?: boolean): void;
129
+ feedback(message: string, options?: FeedbackOptions): void;
94
130
  flush(): Promise<void>;
95
131
  destroy(): void;
96
132
  private buildAndEnqueue;
97
133
  private assertInitialized;
98
- private log;
134
+ private debugLog;
99
135
  }
100
136
  declare const PulseBoard: PulseBoardSDK;
101
137
 
102
- export { type AppContext, type CaptureErrorOptions, type DeviceContext, type EnrichedContext, type EventType, type NetworkContext, PulseBoard, type PulseEvent, type SDKConfig, type SessionContext, type TrackOptions, type UserContext };
138
+ export { type AppContext, type CaptureErrorOptions, type DeviceContext, type EnrichedContext, type EventType, type LogEntry, type LogLevel, type LogOptions, type NetworkContext, PulseBoard, type PulseEvent, type SDKConfig, type SessionContext, type TrackOptions, type UserContext };
package/dist/index.d.ts CHANGED
@@ -55,7 +55,10 @@ type PulseEvent = {
55
55
  context?: EnrichedContext;
56
56
  };
57
57
  type SDKConfig = {
58
- apiKey: string;
58
+ apiKey?: string;
59
+ organisation?: string;
60
+ product?: string;
61
+ project?: string;
59
62
  environment: Environment;
60
63
  debug?: boolean;
61
64
  autoCapture?: boolean;
@@ -69,6 +72,27 @@ type TrackOptions = {
69
72
  type CaptureErrorOptions = {
70
73
  payload?: Record<string, unknown>;
71
74
  };
75
+ type LogLevel = "debug" | "info" | "warn" | "error";
76
+ type LogEntry = {
77
+ apiKey: string;
78
+ level: LogLevel;
79
+ message: string;
80
+ meta?: Record<string, unknown>;
81
+ sessionId?: string;
82
+ appVersion?: string;
83
+ timestamp: string;
84
+ };
85
+ type LogOptions = {
86
+ meta?: Record<string, unknown>;
87
+ };
88
+ type FeedbackType = "bug" | "feature" | "general";
89
+ type FeedbackOptions = {
90
+ type?: FeedbackType;
91
+ meta?: Record<string, unknown>;
92
+ userEmail?: string;
93
+ userName?: string;
94
+ screenshot?: string;
95
+ };
72
96
 
73
97
  declare class PulseBoardSDK {
74
98
  private config;
@@ -79,24 +103,36 @@ declare class PulseBoardSDK {
79
103
  private flushTimer;
80
104
  private initialized;
81
105
  private readonly PULSEBOARD_API;
106
+ private logQueue;
107
+ private logFlushTimer;
108
+ private originalConsole;
109
+ private consoleCapturing;
110
+ private get apiKey();
82
111
  init(config: SDKConfig): void;
112
+ private resolveApiKey;
113
+ private completeInit;
83
114
  getContext(): Promise<EnrichedContext>;
84
115
  identify(user: UserContext): void;
85
116
  clearUser(): void;
86
117
  track(name: string, options?: TrackOptions): void;
87
118
  metric(name: string, value: number, options?: TrackOptions): void;
88
119
  captureError(error: Error, options?: CaptureErrorOptions): void;
120
+ log(level: LogLevel, message: string, options?: LogOptions): void;
121
+ captureConsole(): void;
122
+ releaseConsole(): void;
123
+ flushLogs(): Promise<void>;
89
124
  startSession(): void;
90
125
  endSession(duration?: number): void;
91
126
  trackScreen(screenName: string, loadTime?: number): void;
92
127
  trackApiCall(endpoint: string, httpMethod: string, statusCode: number, duration: number, payloadSize?: number): void;
93
128
  trackCrash(error: Error, isFatal?: boolean): void;
129
+ feedback(message: string, options?: FeedbackOptions): void;
94
130
  flush(): Promise<void>;
95
131
  destroy(): void;
96
132
  private buildAndEnqueue;
97
133
  private assertInitialized;
98
- private log;
134
+ private debugLog;
99
135
  }
100
136
  declare const PulseBoard: PulseBoardSDK;
101
137
 
102
- export { type AppContext, type CaptureErrorOptions, type DeviceContext, type EnrichedContext, type EventType, type NetworkContext, PulseBoard, type PulseEvent, type SDKConfig, type SessionContext, type TrackOptions, type UserContext };
138
+ export { type AppContext, type CaptureErrorOptions, type DeviceContext, type EnrichedContext, type EventType, type LogEntry, type LogLevel, type LogOptions, type NetworkContext, PulseBoard, type PulseEvent, type SDKConfig, type SessionContext, type TrackOptions, type UserContext };
package/dist/index.js CHANGED
@@ -1,6 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var reactNative = require('react-native');
4
+ var AsyncStorage = require('@react-native-async-storage/async-storage');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var AsyncStorage__default = /*#__PURE__*/_interopDefault(AsyncStorage);
4
9
 
5
10
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
11
  var __commonJS = (cb, mod) => function __require() {
@@ -378,6 +383,60 @@ var PulseBoardClient = class {
378
383
  return false;
379
384
  }
380
385
  }
386
+ // ─── Log Entry ─────────────────────────────────────────────
387
+ async sendLog(entry) {
388
+ try {
389
+ const response = await fetch(`${this.host}/logs`, {
390
+ method: "POST",
391
+ headers: { "Content-Type": "application/json" },
392
+ body: JSON.stringify(entry)
393
+ });
394
+ if (!response.ok) {
395
+ this.log(`Failed to send log: ${response.status}`);
396
+ return false;
397
+ }
398
+ return true;
399
+ } catch (err) {
400
+ this.log(`Network error sending log: ${err}`);
401
+ return false;
402
+ }
403
+ }
404
+ async sendLogBatch(apiKey, logs) {
405
+ try {
406
+ const response = await fetch(`${this.host}/logs/batch`, {
407
+ method: "POST",
408
+ headers: { "Content-Type": "application/json" },
409
+ body: JSON.stringify({ apiKey, logs })
410
+ });
411
+ if (!response.ok) {
412
+ this.log(`Failed to send log batch: ${response.status}`);
413
+ return false;
414
+ }
415
+ return true;
416
+ } catch (err) {
417
+ this.log(`Network error sending log batch: ${err}`);
418
+ return false;
419
+ }
420
+ }
421
+ // ─── Feedback ─────────────────────────────────────────────────────
422
+ async sendFeedback(payload) {
423
+ try {
424
+ const response = await fetch(`${this.host}/feedback`, {
425
+ method: "POST",
426
+ headers: { "Content-Type": "application/json" },
427
+ body: JSON.stringify(payload)
428
+ });
429
+ if (!response.ok) {
430
+ this.log(`Failed to send feedback: ${response.status}`);
431
+ return false;
432
+ }
433
+ this.log("Feedback sent");
434
+ return true;
435
+ } catch (err) {
436
+ this.log(`Network error sending feedback: ${err}`);
437
+ return false;
438
+ }
439
+ }
381
440
  // ─── Private ──────────────────────────────────────────────────────
382
441
  chunk(arr, size) {
383
442
  return Array.from(
@@ -493,30 +552,63 @@ var ContextCollector = class {
493
552
  };
494
553
  }
495
554
  };
496
-
497
- // src/queue.ts
555
+ var QUEUE_KEY = "@pulseboard/event_queue";
556
+ var MAX_QUEUE_SIZE = 100;
498
557
  var EventQueue = class {
499
- constructor(maxSize = 100) {
500
- this.queue = [];
558
+ constructor(maxSize = MAX_QUEUE_SIZE) {
559
+ this.memoryQueue = [];
560
+ this._hydrated = false;
501
561
  this.maxSize = maxSize;
562
+ this.hydrate();
563
+ }
564
+ // ─── Hydrate from storage on init ─────────────────────────────────
565
+ async hydrate() {
566
+ try {
567
+ const raw = await AsyncStorage__default.default.getItem(QUEUE_KEY);
568
+ if (raw) {
569
+ const stored = JSON.parse(raw);
570
+ this.memoryQueue = stored.slice(0, this.maxSize);
571
+ }
572
+ } catch {
573
+ } finally {
574
+ this._hydrated = true;
575
+ }
576
+ }
577
+ // ─── Persist to storage ────────────────────────────────────────────
578
+ async persist() {
579
+ try {
580
+ await AsyncStorage__default.default.setItem(QUEUE_KEY, JSON.stringify(this.memoryQueue));
581
+ } catch {
582
+ }
502
583
  }
584
+ // ─── Public API ────────────────────────────────────────────────────
503
585
  enqueue(event) {
504
- if (this.queue.length >= this.maxSize) {
505
- this.queue.shift();
586
+ if (this.memoryQueue.length >= this.maxSize) {
587
+ this.memoryQueue.shift();
506
588
  }
507
- this.queue.push(event);
589
+ this.memoryQueue.push(event);
590
+ this.persist();
508
591
  }
509
592
  dequeue(count) {
510
- return this.queue.splice(0, count);
593
+ const batch = this.memoryQueue.splice(0, count);
594
+ this.persist();
595
+ return batch;
511
596
  }
512
597
  get size() {
513
- return this.queue.length;
598
+ return this.memoryQueue.length;
514
599
  }
515
600
  get isEmpty() {
516
- return this.queue.length === 0;
601
+ return this.memoryQueue.length === 0;
602
+ }
603
+ get hydrated() {
604
+ return this._hydrated;
517
605
  }
518
- clear() {
519
- this.queue = [];
606
+ async clear() {
607
+ this.memoryQueue = [];
608
+ try {
609
+ await AsyncStorage__default.default.removeItem(QUEUE_KEY);
610
+ } catch {
611
+ }
520
612
  }
521
613
  };
522
614
 
@@ -531,25 +623,70 @@ var PulseBoardSDK = class {
531
623
  this.flushTimer = null;
532
624
  this.initialized = false;
533
625
  this.PULSEBOARD_API = "https://pulseboard-production.up.railway.app/";
626
+ // ─── Log queue ────────────────────────────────────────────────────
627
+ this.logQueue = [];
628
+ this.logFlushTimer = null;
629
+ this.originalConsole = {};
630
+ this.consoleCapturing = false;
631
+ }
632
+ get apiKey() {
633
+ return this.config?.apiKey ?? "";
534
634
  }
535
635
  // ─── Public API ───────────────────────────────────────────────────
536
636
  init(config) {
537
637
  if (this.initialized) {
538
- this.log("SDK already initialized \u2014 skipping");
638
+ this.debugLog("SDK already initialized \u2014 skipping");
539
639
  return;
540
640
  }
641
+ const hasDirectKey = !!config.apiKey;
642
+ const hasIdentifiers = !!(config.organisation && config.product && config.project);
643
+ if (!hasDirectKey && !hasIdentifiers) {
644
+ throw new Error(
645
+ "[PulseBoard] init() requires either apiKey or (organisation + product + project)"
646
+ );
647
+ }
541
648
  this.config = {
542
649
  autoCapture: true,
543
650
  debug: false,
544
651
  flushInterval: 5e3,
545
652
  maxQueueSize: 100,
546
- ...config
653
+ ...config,
654
+ apiKey: config.apiKey ?? ""
655
+ // resolved below if using identifiers
547
656
  };
548
657
  this.client = new PulseBoardClient(this.PULSEBOARD_API, this.config.debug);
549
658
  this.queue = new EventQueue(this.config.maxQueueSize);
550
659
  this.contextCollector = new ContextCollector({
551
660
  environment: config.environment
552
661
  });
662
+ if (!hasDirectKey && hasIdentifiers) {
663
+ this.resolveApiKey(
664
+ config.organisation,
665
+ config.product,
666
+ config.project
667
+ );
668
+ } else {
669
+ this.completeInit();
670
+ }
671
+ }
672
+ async resolveApiKey(organisation, product, project) {
673
+ try {
674
+ const response = await fetch(
675
+ `${this.PULSEBOARD_API}sdk/resolve?organisation=${encodeURIComponent(organisation)}&product=${encodeURIComponent(product)}&project=${encodeURIComponent(project)}`
676
+ );
677
+ if (!response.ok) {
678
+ throw new Error(
679
+ `[PulseBoard] Could not resolve project. Check your organisation, product and project values.`
680
+ );
681
+ }
682
+ const data = await response.json();
683
+ this.config.apiKey = data.data.apiKey;
684
+ this.completeInit();
685
+ } catch (err) {
686
+ console.error("[PulseBoard] Failed to resolve API key:", err);
687
+ }
688
+ }
689
+ completeInit() {
553
690
  this.flushTimer = setInterval(
554
691
  () => this.flush(),
555
692
  this.config.flushInterval
@@ -561,7 +698,7 @@ var PulseBoardSDK = class {
561
698
  this.autoCapture.attach();
562
699
  }
563
700
  this.initialized = true;
564
- this.log(`Initialized \u2014 host: ${this.PULSEBOARD_API}`);
701
+ this.debugLog(`Initialized \u2014 host: ${this.PULSEBOARD_API}`);
565
702
  }
566
703
  async getContext() {
567
704
  this.assertInitialized("getContext");
@@ -570,7 +707,7 @@ var PulseBoardSDK = class {
570
707
  identify(user) {
571
708
  this.assertInitialized("identify");
572
709
  this.contextCollector.identify(user);
573
- this.log(`User identified: ${JSON.stringify(user)}`);
710
+ this.debugLog(`User identified: ${JSON.stringify(user)}`);
574
711
  }
575
712
  clearUser() {
576
713
  this.assertInitialized("clearUser");
@@ -602,26 +739,87 @@ var PulseBoardSDK = class {
602
739
  ...options.payload
603
740
  });
604
741
  }
742
+ // ─── Logging API ──────────────────────────────────────────────────
743
+ log(level, message, options = {}) {
744
+ this.assertInitialized("log");
745
+ this.contextCollector.collect().then((context) => {
746
+ this.logQueue.push({
747
+ level,
748
+ message,
749
+ meta: options.meta,
750
+ sessionId: context.session.sessionId,
751
+ appVersion: context.device.appVersion,
752
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
753
+ });
754
+ if (level === "error") this.flushLogs();
755
+ }).catch(() => {
756
+ });
757
+ }
758
+ captureConsole() {
759
+ this.assertInitialized("captureConsole");
760
+ if (this.consoleCapturing) return;
761
+ this.originalConsole = {
762
+ log: console.log,
763
+ warn: console.warn,
764
+ error: console.error,
765
+ debug: console.debug
766
+ };
767
+ console.log = (...args) => {
768
+ this.originalConsole.log?.(...args);
769
+ this.log("info", args.map(String).join(" "));
770
+ };
771
+ console.warn = (...args) => {
772
+ this.originalConsole.warn?.(...args);
773
+ this.log("warn", args.map(String).join(" "));
774
+ };
775
+ console.error = (...args) => {
776
+ this.originalConsole.error?.(...args);
777
+ this.log("error", args.map(String).join(" "));
778
+ };
779
+ console.debug = (...args) => {
780
+ this.originalConsole.debug?.(...args);
781
+ this.log("debug", args.map(String).join(" "));
782
+ };
783
+ this.logFlushTimer = setInterval(() => this.flushLogs(), 1e4);
784
+ this.consoleCapturing = true;
785
+ this.debugLog("Console capture enabled");
786
+ }
787
+ releaseConsole() {
788
+ if (!this.consoleCapturing) return;
789
+ if (this.originalConsole.log) console.log = this.originalConsole.log;
790
+ if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
791
+ if (this.originalConsole.error) console.error = this.originalConsole.error;
792
+ if (this.originalConsole.debug) console.debug = this.originalConsole.debug;
793
+ if (this.logFlushTimer) {
794
+ clearInterval(this.logFlushTimer);
795
+ this.logFlushTimer = null;
796
+ }
797
+ this.originalConsole = {};
798
+ this.consoleCapturing = false;
799
+ }
800
+ async flushLogs() {
801
+ if (!this.client || !this.config || this.logQueue.length === 0) return;
802
+ const batch = this.logQueue.splice(0, this.logQueue.length);
803
+ await this.client.sendLogBatch(this.apiKey, batch);
804
+ }
605
805
  // ─── Analytics API ────────────────────────────────────────────────
606
806
  startSession() {
607
807
  this.assertInitialized("startSession");
608
808
  this.contextCollector.collect().then((context) => {
609
809
  this.client.sendAnalytics("session", {
610
- apiKey: this.config.apiKey,
810
+ apiKey: this.apiKey,
611
811
  sessionId: context.session.sessionId,
612
812
  startedAt: context.session.startedAt,
613
813
  context
614
814
  });
615
- this.log(`Session started: ${context.session.sessionId}`);
616
- }).catch((err) => {
617
- this.log(`Failed to start session: ${err}`);
618
- });
815
+ this.debugLog(`Session started: ${context.session.sessionId}`);
816
+ }).catch((err) => this.debugLog(`Failed to start session: ${err}`));
619
817
  }
620
818
  endSession(duration) {
621
819
  this.assertInitialized("endSession");
622
820
  this.contextCollector.collect().then((context) => {
623
821
  this.client.sendAnalytics("session", {
624
- apiKey: this.config.apiKey,
822
+ apiKey: this.apiKey,
625
823
  sessionId: context.session.sessionId,
626
824
  startedAt: context.session.startedAt,
627
825
  endedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -629,31 +827,27 @@ var PulseBoardSDK = class {
629
827
  context
630
828
  });
631
829
  this.flush();
632
- this.log(`Session ended: ${context.session.sessionId}`);
633
- }).catch((err) => {
634
- this.log(`Failed to end session: ${err}`);
635
- });
830
+ this.debugLog(`Session ended: ${context.session.sessionId}`);
831
+ }).catch((err) => this.debugLog(`Failed to end session: ${err}`));
636
832
  }
637
833
  trackScreen(screenName, loadTime) {
638
834
  this.assertInitialized("trackScreen");
639
835
  this.contextCollector.collect().then((context) => {
640
836
  this.client.sendAnalytics("screen-view", {
641
- apiKey: this.config.apiKey,
837
+ apiKey: this.apiKey,
642
838
  screenName,
643
839
  loadTime,
644
840
  sessionId: context.session.sessionId,
645
841
  context
646
842
  });
647
- this.log(`Screen tracked: ${screenName}`);
648
- }).catch((err) => {
649
- this.log(`Failed to track screen: ${err}`);
650
- });
843
+ this.debugLog(`Screen tracked: ${screenName}`);
844
+ }).catch((err) => this.debugLog(`Failed to track screen: ${err}`));
651
845
  }
652
846
  trackApiCall(endpoint, httpMethod, statusCode, duration, payloadSize) {
653
847
  this.assertInitialized("trackApiCall");
654
848
  this.contextCollector.collect().then((context) => {
655
849
  this.client.sendAnalytics("api-call", {
656
- apiKey: this.config.apiKey,
850
+ apiKey: this.apiKey,
657
851
  endpoint,
658
852
  httpMethod,
659
853
  statusCode,
@@ -662,16 +856,16 @@ var PulseBoardSDK = class {
662
856
  sessionId: context.session.sessionId,
663
857
  context
664
858
  });
665
- this.log(`API call tracked: ${httpMethod} ${endpoint} ${statusCode}`);
666
- }).catch((err) => {
667
- this.log(`Failed to track API call: ${err}`);
668
- });
859
+ this.debugLog(
860
+ `API call tracked: ${httpMethod} ${endpoint} ${statusCode}`
861
+ );
862
+ }).catch((err) => this.debugLog(`Failed to track API call: ${err}`));
669
863
  }
670
864
  trackCrash(error, isFatal = false) {
671
865
  this.assertInitialized("trackCrash");
672
866
  this.contextCollector.collect().then((context) => {
673
867
  this.client.sendAnalytics("crash", {
674
- apiKey: this.config.apiKey,
868
+ apiKey: this.apiKey,
675
869
  errorName: error.name,
676
870
  errorMessage: error.message,
677
871
  stackTrace: error.stack ?? "",
@@ -679,17 +873,46 @@ var PulseBoardSDK = class {
679
873
  sessionId: context.session.sessionId,
680
874
  context
681
875
  });
682
- this.log(`Crash tracked: ${error.name} \u2014 fatal: ${isFatal}`);
683
- }).catch((err) => {
684
- this.log(`Failed to track crash: ${err}`);
685
- });
876
+ this.debugLog(`Crash tracked: ${error.name} \u2014 fatal: ${isFatal}`);
877
+ }).catch((err) => this.debugLog(`Failed to track crash: ${err}`));
686
878
  }
687
- // ─── Flush & Destroy ─────────────────────────────────────────────
879
+ feedback(message, options = {}) {
880
+ this.assertInitialized("feedback");
881
+ this.contextCollector.collect().then((context) => {
882
+ this.client.sendFeedback({
883
+ apiKey: this.apiKey,
884
+ type: options.type ?? "general",
885
+ message,
886
+ meta: options.meta,
887
+ userEmail: options.userEmail,
888
+ userName: options.userName,
889
+ screenshot: options.screenshot,
890
+ sessionId: context.session.sessionId,
891
+ appVersion: context.device.appVersion
892
+ });
893
+ this.debugLog(`Feedback sent: ${options.type ?? "general"}`);
894
+ }).catch((err) => this.debugLog(`Failed to send feedback: ${err}`));
895
+ }
896
+ // ─── Flush & Destroy ──────────────────────────────────────────────
688
897
  async flush() {
689
- if (!this.queue || this.queue.isEmpty) return;
690
- if (!this.client) return;
898
+ if (!this.queue || !this.client) return;
899
+ if (!this.queue.hydrated) {
900
+ await new Promise((resolve) => {
901
+ const interval = setInterval(() => {
902
+ if (this.queue?.hydrated) {
903
+ clearInterval(interval);
904
+ resolve();
905
+ }
906
+ }, 50);
907
+ setTimeout(() => {
908
+ clearInterval(interval);
909
+ resolve();
910
+ }, 500);
911
+ });
912
+ }
913
+ if (this.queue.isEmpty) return;
691
914
  const events = this.queue.dequeue(10);
692
- this.log(`Flushing ${events.length} event(s)`);
915
+ this.debugLog(`Flushing ${events.length} event(s)`);
693
916
  await this.client.sendBatch(events);
694
917
  }
695
918
  destroy() {
@@ -697,20 +920,22 @@ var PulseBoardSDK = class {
697
920
  clearInterval(this.flushTimer);
698
921
  this.flushTimer = null;
699
922
  }
923
+ this.releaseConsole();
700
924
  this.autoCapture?.detach();
701
925
  this.queue?.clear();
926
+ this.logQueue = [];
702
927
  this.initialized = false;
703
928
  this.config = null;
704
929
  this.client = null;
705
930
  this.queue = null;
706
931
  this.contextCollector = null;
707
- this.log("SDK destroyed");
932
+ this.debugLog("SDK destroyed");
708
933
  }
709
- // ─── Private ─────────────────────────────────────────────────────
934
+ // ─── Private ──────────────────────────────────────────────────────
710
935
  buildAndEnqueue(type, name, payload, timestamp) {
711
936
  this.contextCollector.collect().then((context) => {
712
937
  const event = {
713
- apiKey: this.config.apiKey,
938
+ apiKey: this.apiKey,
714
939
  type,
715
940
  name,
716
941
  payload,
@@ -718,20 +943,16 @@ var PulseBoardSDK = class {
718
943
  context
719
944
  };
720
945
  this.queue.enqueue(event);
721
- this.log(`Queued: ${type} \u2014 ${name}`);
722
- if (type === "error") {
723
- this.flush();
724
- }
725
- }).catch((err) => {
726
- this.log(`Failed to collect context: ${err}`);
727
- });
946
+ this.debugLog(`Queued: ${type} \u2014 ${name}`);
947
+ if (type === "error") this.flush();
948
+ }).catch((err) => this.debugLog(`Failed to collect context: ${err}`));
728
949
  }
729
950
  assertInitialized(method) {
730
951
  if (!this.initialized) {
731
952
  throw new Error(`PulseBoard.${method}() called before PulseBoard.init()`);
732
953
  }
733
954
  }
734
- log(message) {
955
+ debugLog(message) {
735
956
  if (this.config?.debug) {
736
957
  console.log(`[PulseBoard] ${message}`);
737
958
  }