@pulseboard/react-native 0.2.0 → 0.2.2
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 +44 -9
- package/dist/index.d.ts +44 -9
- package/dist/index.js +334 -110
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +330 -110
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -3
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TurboModuleRegistry, Dimensions, Platform, I18nManager } from 'react-native';
|
|
2
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
3
|
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
5
|
var __commonJS = (cb, mod) => function __require() {
|
|
@@ -285,6 +286,42 @@ var require_rejection_tracking = __commonJS({
|
|
|
285
286
|
}
|
|
286
287
|
});
|
|
287
288
|
|
|
289
|
+
// src/auto-capture.ts
|
|
290
|
+
var AutoCapture = class {
|
|
291
|
+
constructor(handler) {
|
|
292
|
+
this.attached = false;
|
|
293
|
+
this.previousHandler = null;
|
|
294
|
+
this.handler = handler;
|
|
295
|
+
}
|
|
296
|
+
attach() {
|
|
297
|
+
if (this.attached) return;
|
|
298
|
+
this.previousHandler = ErrorUtils.getGlobalHandler();
|
|
299
|
+
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
300
|
+
this.handler("uncaughtException", error, { isFatal: isFatal ?? false });
|
|
301
|
+
if (this.previousHandler) {
|
|
302
|
+
this.previousHandler(error, isFatal);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
const tracking = require_rejection_tracking();
|
|
306
|
+
tracking.enable({
|
|
307
|
+
allRejections: true,
|
|
308
|
+
onUnhandled: (_id, error) => {
|
|
309
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
310
|
+
this.handler("unhandledRejection", err, { fatal: false });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
this.attached = true;
|
|
314
|
+
}
|
|
315
|
+
detach() {
|
|
316
|
+
if (!this.attached) return;
|
|
317
|
+
if (this.previousHandler) {
|
|
318
|
+
ErrorUtils.setGlobalHandler(this.previousHandler);
|
|
319
|
+
this.previousHandler = null;
|
|
320
|
+
}
|
|
321
|
+
this.attached = false;
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
288
325
|
// src/client.ts
|
|
289
326
|
var PulseBoardClient = class {
|
|
290
327
|
constructor(host, debug = false) {
|
|
@@ -340,6 +377,60 @@ var PulseBoardClient = class {
|
|
|
340
377
|
return false;
|
|
341
378
|
}
|
|
342
379
|
}
|
|
380
|
+
// ─── Log Entry ─────────────────────────────────────────────
|
|
381
|
+
async sendLog(entry) {
|
|
382
|
+
try {
|
|
383
|
+
const response = await fetch(`${this.host}/logs`, {
|
|
384
|
+
method: "POST",
|
|
385
|
+
headers: { "Content-Type": "application/json" },
|
|
386
|
+
body: JSON.stringify(entry)
|
|
387
|
+
});
|
|
388
|
+
if (!response.ok) {
|
|
389
|
+
this.log(`Failed to send log: ${response.status}`);
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
return true;
|
|
393
|
+
} catch (err) {
|
|
394
|
+
this.log(`Network error sending log: ${err}`);
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async sendLogBatch(apiKey, logs) {
|
|
399
|
+
try {
|
|
400
|
+
const response = await fetch(`${this.host}/logs/batch`, {
|
|
401
|
+
method: "POST",
|
|
402
|
+
headers: { "Content-Type": "application/json" },
|
|
403
|
+
body: JSON.stringify({ apiKey, logs })
|
|
404
|
+
});
|
|
405
|
+
if (!response.ok) {
|
|
406
|
+
this.log(`Failed to send log batch: ${response.status}`);
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
return true;
|
|
410
|
+
} catch (err) {
|
|
411
|
+
this.log(`Network error sending log batch: ${err}`);
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// ─── Feedback ─────────────────────────────────────────────────────
|
|
416
|
+
async sendFeedback(payload) {
|
|
417
|
+
try {
|
|
418
|
+
const response = await fetch(`${this.host}/feedback`, {
|
|
419
|
+
method: "POST",
|
|
420
|
+
headers: { "Content-Type": "application/json" },
|
|
421
|
+
body: JSON.stringify(payload)
|
|
422
|
+
});
|
|
423
|
+
if (!response.ok) {
|
|
424
|
+
this.log(`Failed to send feedback: ${response.status}`);
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
this.log("Feedback sent");
|
|
428
|
+
return true;
|
|
429
|
+
} catch (err) {
|
|
430
|
+
this.log(`Network error sending feedback: ${err}`);
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
343
434
|
// ─── Private ──────────────────────────────────────────────────────
|
|
344
435
|
chunk(arr, size) {
|
|
345
436
|
return Array.from(
|
|
@@ -353,68 +444,6 @@ var PulseBoardClient = class {
|
|
|
353
444
|
}
|
|
354
445
|
}
|
|
355
446
|
};
|
|
356
|
-
|
|
357
|
-
// src/queue.ts
|
|
358
|
-
var EventQueue = class {
|
|
359
|
-
constructor(maxSize = 100) {
|
|
360
|
-
this.queue = [];
|
|
361
|
-
this.maxSize = maxSize;
|
|
362
|
-
}
|
|
363
|
-
enqueue(event) {
|
|
364
|
-
if (this.queue.length >= this.maxSize) {
|
|
365
|
-
this.queue.shift();
|
|
366
|
-
}
|
|
367
|
-
this.queue.push(event);
|
|
368
|
-
}
|
|
369
|
-
dequeue(count) {
|
|
370
|
-
return this.queue.splice(0, count);
|
|
371
|
-
}
|
|
372
|
-
get size() {
|
|
373
|
-
return this.queue.length;
|
|
374
|
-
}
|
|
375
|
-
get isEmpty() {
|
|
376
|
-
return this.queue.length === 0;
|
|
377
|
-
}
|
|
378
|
-
clear() {
|
|
379
|
-
this.queue = [];
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
// src/auto-capture.ts
|
|
384
|
-
var AutoCapture = class {
|
|
385
|
-
constructor(handler) {
|
|
386
|
-
this.attached = false;
|
|
387
|
-
this.previousHandler = null;
|
|
388
|
-
this.handler = handler;
|
|
389
|
-
}
|
|
390
|
-
attach() {
|
|
391
|
-
if (this.attached) return;
|
|
392
|
-
this.previousHandler = ErrorUtils.getGlobalHandler();
|
|
393
|
-
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
394
|
-
this.handler("uncaughtException", error, { isFatal: isFatal ?? false });
|
|
395
|
-
if (this.previousHandler) {
|
|
396
|
-
this.previousHandler(error, isFatal);
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
const tracking = require_rejection_tracking();
|
|
400
|
-
tracking.enable({
|
|
401
|
-
allRejections: true,
|
|
402
|
-
onUnhandled: (_id, error) => {
|
|
403
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
404
|
-
this.handler("unhandledRejection", err, { fatal: false });
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
this.attached = true;
|
|
408
|
-
}
|
|
409
|
-
detach() {
|
|
410
|
-
if (!this.attached) return;
|
|
411
|
-
if (this.previousHandler) {
|
|
412
|
-
ErrorUtils.setGlobalHandler(this.previousHandler);
|
|
413
|
-
this.previousHandler = null;
|
|
414
|
-
}
|
|
415
|
-
this.attached = false;
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
447
|
var NativePulseBoardDevice_default = TurboModuleRegistry.getEnforcing("PulseBoardDevice");
|
|
419
448
|
var NativePulseBoardNetwork_default = TurboModuleRegistry.getEnforcing("PulseBoardNetwork");
|
|
420
449
|
|
|
@@ -491,8 +520,8 @@ var ContextCollector = class {
|
|
|
491
520
|
manufacturer: deviceInfo?.manufacturer ?? "unknown",
|
|
492
521
|
brand: deviceInfo?.brand ?? "unknown",
|
|
493
522
|
isTablet: deviceInfo?.isTablet ?? false,
|
|
494
|
-
appVersion:
|
|
495
|
-
buildNumber:
|
|
523
|
+
appVersion: deviceInfo?.appVersion ?? "unknown",
|
|
524
|
+
buildNumber: deviceInfo?.buildNumber ?? "unknown",
|
|
496
525
|
bundleId: deviceInfo?.bundleId ?? "unknown",
|
|
497
526
|
screenWidth: deviceInfo?.screenWidth ?? width,
|
|
498
527
|
screenHeight: deviceInfo?.screenHeight ?? height,
|
|
@@ -517,6 +546,65 @@ var ContextCollector = class {
|
|
|
517
546
|
};
|
|
518
547
|
}
|
|
519
548
|
};
|
|
549
|
+
var QUEUE_KEY = "@pulseboard/event_queue";
|
|
550
|
+
var MAX_QUEUE_SIZE = 100;
|
|
551
|
+
var EventQueue = class {
|
|
552
|
+
constructor(maxSize = MAX_QUEUE_SIZE) {
|
|
553
|
+
this.memoryQueue = [];
|
|
554
|
+
this._hydrated = false;
|
|
555
|
+
this.maxSize = maxSize;
|
|
556
|
+
this.hydrate();
|
|
557
|
+
}
|
|
558
|
+
// ─── Hydrate from storage on init ─────────────────────────────────
|
|
559
|
+
async hydrate() {
|
|
560
|
+
try {
|
|
561
|
+
const raw = await AsyncStorage.getItem(QUEUE_KEY);
|
|
562
|
+
if (raw) {
|
|
563
|
+
const stored = JSON.parse(raw);
|
|
564
|
+
this.memoryQueue = stored.slice(0, this.maxSize);
|
|
565
|
+
}
|
|
566
|
+
} catch {
|
|
567
|
+
} finally {
|
|
568
|
+
this._hydrated = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
// ─── Persist to storage ────────────────────────────────────────────
|
|
572
|
+
async persist() {
|
|
573
|
+
try {
|
|
574
|
+
await AsyncStorage.setItem(QUEUE_KEY, JSON.stringify(this.memoryQueue));
|
|
575
|
+
} catch {
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
// ─── Public API ────────────────────────────────────────────────────
|
|
579
|
+
enqueue(event) {
|
|
580
|
+
if (this.memoryQueue.length >= this.maxSize) {
|
|
581
|
+
this.memoryQueue.shift();
|
|
582
|
+
}
|
|
583
|
+
this.memoryQueue.push(event);
|
|
584
|
+
this.persist();
|
|
585
|
+
}
|
|
586
|
+
dequeue(count) {
|
|
587
|
+
const batch = this.memoryQueue.splice(0, count);
|
|
588
|
+
this.persist();
|
|
589
|
+
return batch;
|
|
590
|
+
}
|
|
591
|
+
get size() {
|
|
592
|
+
return this.memoryQueue.length;
|
|
593
|
+
}
|
|
594
|
+
get isEmpty() {
|
|
595
|
+
return this.memoryQueue.length === 0;
|
|
596
|
+
}
|
|
597
|
+
get hydrated() {
|
|
598
|
+
return this._hydrated;
|
|
599
|
+
}
|
|
600
|
+
async clear() {
|
|
601
|
+
this.memoryQueue = [];
|
|
602
|
+
try {
|
|
603
|
+
await AsyncStorage.removeItem(QUEUE_KEY);
|
|
604
|
+
} catch {
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
};
|
|
520
608
|
|
|
521
609
|
// src/index.ts
|
|
522
610
|
var PulseBoardSDK = class {
|
|
@@ -528,23 +616,71 @@ var PulseBoardSDK = class {
|
|
|
528
616
|
this.contextCollector = null;
|
|
529
617
|
this.flushTimer = null;
|
|
530
618
|
this.initialized = false;
|
|
619
|
+
this.PULSEBOARD_API = "https://pulseboard-production.up.railway.app/";
|
|
620
|
+
// ─── Log queue ────────────────────────────────────────────────────
|
|
621
|
+
this.logQueue = [];
|
|
622
|
+
this.logFlushTimer = null;
|
|
623
|
+
this.originalConsole = {};
|
|
624
|
+
this.consoleCapturing = false;
|
|
625
|
+
}
|
|
626
|
+
get apiKey() {
|
|
627
|
+
return this.config?.apiKey ?? "";
|
|
531
628
|
}
|
|
532
629
|
// ─── Public API ───────────────────────────────────────────────────
|
|
533
630
|
init(config) {
|
|
534
631
|
if (this.initialized) {
|
|
535
|
-
this.
|
|
632
|
+
this.debugLog("SDK already initialized \u2014 skipping");
|
|
536
633
|
return;
|
|
537
634
|
}
|
|
635
|
+
const hasDirectKey = !!config.apiKey;
|
|
636
|
+
const hasIdentifiers = !!(config.organisation && config.product && config.project);
|
|
637
|
+
if (!hasDirectKey && !hasIdentifiers) {
|
|
638
|
+
throw new Error(
|
|
639
|
+
"[PulseBoard] init() requires either apiKey or (organisation + product + project)"
|
|
640
|
+
);
|
|
641
|
+
}
|
|
538
642
|
this.config = {
|
|
539
643
|
autoCapture: true,
|
|
540
644
|
debug: false,
|
|
541
645
|
flushInterval: 5e3,
|
|
542
646
|
maxQueueSize: 100,
|
|
543
|
-
...config
|
|
647
|
+
...config,
|
|
648
|
+
apiKey: config.apiKey ?? ""
|
|
649
|
+
// resolved below if using identifiers
|
|
544
650
|
};
|
|
545
|
-
this.client = new PulseBoardClient(this.
|
|
651
|
+
this.client = new PulseBoardClient(this.PULSEBOARD_API, this.config.debug);
|
|
546
652
|
this.queue = new EventQueue(this.config.maxQueueSize);
|
|
547
|
-
this.contextCollector = new ContextCollector(
|
|
653
|
+
this.contextCollector = new ContextCollector({
|
|
654
|
+
environment: config.environment
|
|
655
|
+
});
|
|
656
|
+
if (!hasDirectKey && hasIdentifiers) {
|
|
657
|
+
this.resolveApiKey(
|
|
658
|
+
config.organisation,
|
|
659
|
+
config.product,
|
|
660
|
+
config.project
|
|
661
|
+
);
|
|
662
|
+
} else {
|
|
663
|
+
this.completeInit();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
async resolveApiKey(organisation, product, project) {
|
|
667
|
+
try {
|
|
668
|
+
const response = await fetch(
|
|
669
|
+
`${this.PULSEBOARD_API}sdk/resolve?organisation=${encodeURIComponent(organisation)}&product=${encodeURIComponent(product)}&project=${encodeURIComponent(project)}`
|
|
670
|
+
);
|
|
671
|
+
if (!response.ok) {
|
|
672
|
+
throw new Error(
|
|
673
|
+
`[PulseBoard] Could not resolve project. Check your organisation, product and project values.`
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
const data = await response.json();
|
|
677
|
+
this.config.apiKey = data.data.apiKey;
|
|
678
|
+
this.completeInit();
|
|
679
|
+
} catch (err) {
|
|
680
|
+
console.error("[PulseBoard] Failed to resolve API key:", err);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
completeInit() {
|
|
548
684
|
this.flushTimer = setInterval(
|
|
549
685
|
() => this.flush(),
|
|
550
686
|
this.config.flushInterval
|
|
@@ -556,7 +692,7 @@ var PulseBoardSDK = class {
|
|
|
556
692
|
this.autoCapture.attach();
|
|
557
693
|
}
|
|
558
694
|
this.initialized = true;
|
|
559
|
-
this.
|
|
695
|
+
this.debugLog(`Initialized \u2014 host: ${this.PULSEBOARD_API}`);
|
|
560
696
|
}
|
|
561
697
|
async getContext() {
|
|
562
698
|
this.assertInitialized("getContext");
|
|
@@ -565,7 +701,7 @@ var PulseBoardSDK = class {
|
|
|
565
701
|
identify(user) {
|
|
566
702
|
this.assertInitialized("identify");
|
|
567
703
|
this.contextCollector.identify(user);
|
|
568
|
-
this.
|
|
704
|
+
this.debugLog(`User identified: ${JSON.stringify(user)}`);
|
|
569
705
|
}
|
|
570
706
|
clearUser() {
|
|
571
707
|
this.assertInitialized("clearUser");
|
|
@@ -597,26 +733,87 @@ var PulseBoardSDK = class {
|
|
|
597
733
|
...options.payload
|
|
598
734
|
});
|
|
599
735
|
}
|
|
736
|
+
// ─── Logging API ──────────────────────────────────────────────────
|
|
737
|
+
log(level, message, options = {}) {
|
|
738
|
+
this.assertInitialized("log");
|
|
739
|
+
this.contextCollector.collect().then((context) => {
|
|
740
|
+
this.logQueue.push({
|
|
741
|
+
level,
|
|
742
|
+
message,
|
|
743
|
+
meta: options.meta,
|
|
744
|
+
sessionId: context.session.sessionId,
|
|
745
|
+
appVersion: context.device.appVersion,
|
|
746
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
747
|
+
});
|
|
748
|
+
if (level === "error") this.flushLogs();
|
|
749
|
+
}).catch(() => {
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
captureConsole() {
|
|
753
|
+
this.assertInitialized("captureConsole");
|
|
754
|
+
if (this.consoleCapturing) return;
|
|
755
|
+
this.originalConsole = {
|
|
756
|
+
log: console.log,
|
|
757
|
+
warn: console.warn,
|
|
758
|
+
error: console.error,
|
|
759
|
+
debug: console.debug
|
|
760
|
+
};
|
|
761
|
+
console.log = (...args) => {
|
|
762
|
+
this.originalConsole.log?.(...args);
|
|
763
|
+
this.log("info", args.map(String).join(" "));
|
|
764
|
+
};
|
|
765
|
+
console.warn = (...args) => {
|
|
766
|
+
this.originalConsole.warn?.(...args);
|
|
767
|
+
this.log("warn", args.map(String).join(" "));
|
|
768
|
+
};
|
|
769
|
+
console.error = (...args) => {
|
|
770
|
+
this.originalConsole.error?.(...args);
|
|
771
|
+
this.log("error", args.map(String).join(" "));
|
|
772
|
+
};
|
|
773
|
+
console.debug = (...args) => {
|
|
774
|
+
this.originalConsole.debug?.(...args);
|
|
775
|
+
this.log("debug", args.map(String).join(" "));
|
|
776
|
+
};
|
|
777
|
+
this.logFlushTimer = setInterval(() => this.flushLogs(), 1e4);
|
|
778
|
+
this.consoleCapturing = true;
|
|
779
|
+
this.debugLog("Console capture enabled");
|
|
780
|
+
}
|
|
781
|
+
releaseConsole() {
|
|
782
|
+
if (!this.consoleCapturing) return;
|
|
783
|
+
if (this.originalConsole.log) console.log = this.originalConsole.log;
|
|
784
|
+
if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
|
|
785
|
+
if (this.originalConsole.error) console.error = this.originalConsole.error;
|
|
786
|
+
if (this.originalConsole.debug) console.debug = this.originalConsole.debug;
|
|
787
|
+
if (this.logFlushTimer) {
|
|
788
|
+
clearInterval(this.logFlushTimer);
|
|
789
|
+
this.logFlushTimer = null;
|
|
790
|
+
}
|
|
791
|
+
this.originalConsole = {};
|
|
792
|
+
this.consoleCapturing = false;
|
|
793
|
+
}
|
|
794
|
+
async flushLogs() {
|
|
795
|
+
if (!this.client || !this.config || this.logQueue.length === 0) return;
|
|
796
|
+
const batch = this.logQueue.splice(0, this.logQueue.length);
|
|
797
|
+
await this.client.sendLogBatch(this.apiKey, batch);
|
|
798
|
+
}
|
|
600
799
|
// ─── Analytics API ────────────────────────────────────────────────
|
|
601
800
|
startSession() {
|
|
602
801
|
this.assertInitialized("startSession");
|
|
603
802
|
this.contextCollector.collect().then((context) => {
|
|
604
803
|
this.client.sendAnalytics("session", {
|
|
605
|
-
apiKey: this.
|
|
804
|
+
apiKey: this.apiKey,
|
|
606
805
|
sessionId: context.session.sessionId,
|
|
607
806
|
startedAt: context.session.startedAt,
|
|
608
807
|
context
|
|
609
808
|
});
|
|
610
|
-
this.
|
|
611
|
-
}).catch((err) => {
|
|
612
|
-
this.log(`Failed to start session: ${err}`);
|
|
613
|
-
});
|
|
809
|
+
this.debugLog(`Session started: ${context.session.sessionId}`);
|
|
810
|
+
}).catch((err) => this.debugLog(`Failed to start session: ${err}`));
|
|
614
811
|
}
|
|
615
812
|
endSession(duration) {
|
|
616
813
|
this.assertInitialized("endSession");
|
|
617
814
|
this.contextCollector.collect().then((context) => {
|
|
618
815
|
this.client.sendAnalytics("session", {
|
|
619
|
-
apiKey: this.
|
|
816
|
+
apiKey: this.apiKey,
|
|
620
817
|
sessionId: context.session.sessionId,
|
|
621
818
|
startedAt: context.session.startedAt,
|
|
622
819
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -624,31 +821,27 @@ var PulseBoardSDK = class {
|
|
|
624
821
|
context
|
|
625
822
|
});
|
|
626
823
|
this.flush();
|
|
627
|
-
this.
|
|
628
|
-
}).catch((err) => {
|
|
629
|
-
this.log(`Failed to end session: ${err}`);
|
|
630
|
-
});
|
|
824
|
+
this.debugLog(`Session ended: ${context.session.sessionId}`);
|
|
825
|
+
}).catch((err) => this.debugLog(`Failed to end session: ${err}`));
|
|
631
826
|
}
|
|
632
827
|
trackScreen(screenName, loadTime) {
|
|
633
828
|
this.assertInitialized("trackScreen");
|
|
634
829
|
this.contextCollector.collect().then((context) => {
|
|
635
830
|
this.client.sendAnalytics("screen-view", {
|
|
636
|
-
apiKey: this.
|
|
831
|
+
apiKey: this.apiKey,
|
|
637
832
|
screenName,
|
|
638
833
|
loadTime,
|
|
639
834
|
sessionId: context.session.sessionId,
|
|
640
835
|
context
|
|
641
836
|
});
|
|
642
|
-
this.
|
|
643
|
-
}).catch((err) => {
|
|
644
|
-
this.log(`Failed to track screen: ${err}`);
|
|
645
|
-
});
|
|
837
|
+
this.debugLog(`Screen tracked: ${screenName}`);
|
|
838
|
+
}).catch((err) => this.debugLog(`Failed to track screen: ${err}`));
|
|
646
839
|
}
|
|
647
840
|
trackApiCall(endpoint, httpMethod, statusCode, duration, payloadSize) {
|
|
648
841
|
this.assertInitialized("trackApiCall");
|
|
649
842
|
this.contextCollector.collect().then((context) => {
|
|
650
843
|
this.client.sendAnalytics("api-call", {
|
|
651
|
-
apiKey: this.
|
|
844
|
+
apiKey: this.apiKey,
|
|
652
845
|
endpoint,
|
|
653
846
|
httpMethod,
|
|
654
847
|
statusCode,
|
|
@@ -657,16 +850,16 @@ var PulseBoardSDK = class {
|
|
|
657
850
|
sessionId: context.session.sessionId,
|
|
658
851
|
context
|
|
659
852
|
});
|
|
660
|
-
this.
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
});
|
|
853
|
+
this.debugLog(
|
|
854
|
+
`API call tracked: ${httpMethod} ${endpoint} ${statusCode}`
|
|
855
|
+
);
|
|
856
|
+
}).catch((err) => this.debugLog(`Failed to track API call: ${err}`));
|
|
664
857
|
}
|
|
665
858
|
trackCrash(error, isFatal = false) {
|
|
666
859
|
this.assertInitialized("trackCrash");
|
|
667
860
|
this.contextCollector.collect().then((context) => {
|
|
668
861
|
this.client.sendAnalytics("crash", {
|
|
669
|
-
apiKey: this.
|
|
862
|
+
apiKey: this.apiKey,
|
|
670
863
|
errorName: error.name,
|
|
671
864
|
errorMessage: error.message,
|
|
672
865
|
stackTrace: error.stack ?? "",
|
|
@@ -674,17 +867,46 @@ var PulseBoardSDK = class {
|
|
|
674
867
|
sessionId: context.session.sessionId,
|
|
675
868
|
context
|
|
676
869
|
});
|
|
677
|
-
this.
|
|
678
|
-
}).catch((err) => {
|
|
679
|
-
this.log(`Failed to track crash: ${err}`);
|
|
680
|
-
});
|
|
870
|
+
this.debugLog(`Crash tracked: ${error.name} \u2014 fatal: ${isFatal}`);
|
|
871
|
+
}).catch((err) => this.debugLog(`Failed to track crash: ${err}`));
|
|
681
872
|
}
|
|
682
|
-
|
|
873
|
+
feedback(message, options = {}) {
|
|
874
|
+
this.assertInitialized("feedback");
|
|
875
|
+
this.contextCollector.collect().then((context) => {
|
|
876
|
+
this.client.sendFeedback({
|
|
877
|
+
apiKey: this.apiKey,
|
|
878
|
+
type: options.type ?? "general",
|
|
879
|
+
message,
|
|
880
|
+
meta: options.meta,
|
|
881
|
+
userEmail: options.userEmail,
|
|
882
|
+
userName: options.userName,
|
|
883
|
+
screenshot: options.screenshot,
|
|
884
|
+
sessionId: context.session.sessionId,
|
|
885
|
+
appVersion: context.device.appVersion
|
|
886
|
+
});
|
|
887
|
+
this.debugLog(`Feedback sent: ${options.type ?? "general"}`);
|
|
888
|
+
}).catch((err) => this.debugLog(`Failed to send feedback: ${err}`));
|
|
889
|
+
}
|
|
890
|
+
// ─── Flush & Destroy ──────────────────────────────────────────────
|
|
683
891
|
async flush() {
|
|
684
|
-
if (!this.queue || this.
|
|
685
|
-
if (!this.
|
|
892
|
+
if (!this.queue || !this.client) return;
|
|
893
|
+
if (!this.queue.hydrated) {
|
|
894
|
+
await new Promise((resolve) => {
|
|
895
|
+
const interval = setInterval(() => {
|
|
896
|
+
if (this.queue?.hydrated) {
|
|
897
|
+
clearInterval(interval);
|
|
898
|
+
resolve();
|
|
899
|
+
}
|
|
900
|
+
}, 50);
|
|
901
|
+
setTimeout(() => {
|
|
902
|
+
clearInterval(interval);
|
|
903
|
+
resolve();
|
|
904
|
+
}, 500);
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
if (this.queue.isEmpty) return;
|
|
686
908
|
const events = this.queue.dequeue(10);
|
|
687
|
-
this.
|
|
909
|
+
this.debugLog(`Flushing ${events.length} event(s)`);
|
|
688
910
|
await this.client.sendBatch(events);
|
|
689
911
|
}
|
|
690
912
|
destroy() {
|
|
@@ -692,20 +914,22 @@ var PulseBoardSDK = class {
|
|
|
692
914
|
clearInterval(this.flushTimer);
|
|
693
915
|
this.flushTimer = null;
|
|
694
916
|
}
|
|
917
|
+
this.releaseConsole();
|
|
695
918
|
this.autoCapture?.detach();
|
|
696
919
|
this.queue?.clear();
|
|
920
|
+
this.logQueue = [];
|
|
697
921
|
this.initialized = false;
|
|
698
922
|
this.config = null;
|
|
699
923
|
this.client = null;
|
|
700
924
|
this.queue = null;
|
|
701
925
|
this.contextCollector = null;
|
|
702
|
-
this.
|
|
926
|
+
this.debugLog("SDK destroyed");
|
|
703
927
|
}
|
|
704
|
-
// ─── Private
|
|
928
|
+
// ─── Private ──────────────────────────────────────────────────────
|
|
705
929
|
buildAndEnqueue(type, name, payload, timestamp) {
|
|
706
930
|
this.contextCollector.collect().then((context) => {
|
|
707
931
|
const event = {
|
|
708
|
-
apiKey: this.
|
|
932
|
+
apiKey: this.apiKey,
|
|
709
933
|
type,
|
|
710
934
|
name,
|
|
711
935
|
payload,
|
|
@@ -713,20 +937,16 @@ var PulseBoardSDK = class {
|
|
|
713
937
|
context
|
|
714
938
|
};
|
|
715
939
|
this.queue.enqueue(event);
|
|
716
|
-
this.
|
|
717
|
-
if (type === "error")
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
}).catch((err) => {
|
|
721
|
-
this.log(`Failed to collect context: ${err}`);
|
|
722
|
-
});
|
|
940
|
+
this.debugLog(`Queued: ${type} \u2014 ${name}`);
|
|
941
|
+
if (type === "error") this.flush();
|
|
942
|
+
}).catch((err) => this.debugLog(`Failed to collect context: ${err}`));
|
|
723
943
|
}
|
|
724
944
|
assertInitialized(method) {
|
|
725
945
|
if (!this.initialized) {
|
|
726
946
|
throw new Error(`PulseBoard.${method}() called before PulseBoard.init()`);
|
|
727
947
|
}
|
|
728
948
|
}
|
|
729
|
-
|
|
949
|
+
debugLog(message) {
|
|
730
950
|
if (this.config?.debug) {
|
|
731
951
|
console.log(`[PulseBoard] ${message}`);
|
|
732
952
|
}
|