@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 +39 -3
- package/dist/index.d.ts +39 -3
- package/dist/index.js +277 -56
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +273 -56
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -3
package/dist/index.d.mts
CHANGED
|
@@ -55,7 +55,10 @@ type PulseEvent = {
|
|
|
55
55
|
context?: EnrichedContext;
|
|
56
56
|
};
|
|
57
57
|
type SDKConfig = {
|
|
58
|
-
apiKey
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
555
|
+
var QUEUE_KEY = "@pulseboard/event_queue";
|
|
556
|
+
var MAX_QUEUE_SIZE = 100;
|
|
498
557
|
var EventQueue = class {
|
|
499
|
-
constructor(maxSize =
|
|
500
|
-
this.
|
|
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.
|
|
505
|
-
this.
|
|
586
|
+
if (this.memoryQueue.length >= this.maxSize) {
|
|
587
|
+
this.memoryQueue.shift();
|
|
506
588
|
}
|
|
507
|
-
this.
|
|
589
|
+
this.memoryQueue.push(event);
|
|
590
|
+
this.persist();
|
|
508
591
|
}
|
|
509
592
|
dequeue(count) {
|
|
510
|
-
|
|
593
|
+
const batch = this.memoryQueue.splice(0, count);
|
|
594
|
+
this.persist();
|
|
595
|
+
return batch;
|
|
511
596
|
}
|
|
512
597
|
get size() {
|
|
513
|
-
return this.
|
|
598
|
+
return this.memoryQueue.length;
|
|
514
599
|
}
|
|
515
600
|
get isEmpty() {
|
|
516
|
-
return this.
|
|
601
|
+
return this.memoryQueue.length === 0;
|
|
602
|
+
}
|
|
603
|
+
get hydrated() {
|
|
604
|
+
return this._hydrated;
|
|
517
605
|
}
|
|
518
|
-
clear() {
|
|
519
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
810
|
+
apiKey: this.apiKey,
|
|
611
811
|
sessionId: context.session.sessionId,
|
|
612
812
|
startedAt: context.session.startedAt,
|
|
613
813
|
context
|
|
614
814
|
});
|
|
615
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
837
|
+
apiKey: this.apiKey,
|
|
642
838
|
screenName,
|
|
643
839
|
loadTime,
|
|
644
840
|
sessionId: context.session.sessionId,
|
|
645
841
|
context
|
|
646
842
|
});
|
|
647
|
-
this.
|
|
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.
|
|
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.
|
|
666
|
-
|
|
667
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
690
|
-
if (!this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
722
|
-
if (type === "error")
|
|
723
|
-
|
|
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
|
-
|
|
955
|
+
debugLog(message) {
|
|
735
956
|
if (this.config?.debug) {
|
|
736
957
|
console.log(`[PulseBoard] ${message}`);
|
|
737
958
|
}
|