@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.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() {
|
|
@@ -287,6 +292,42 @@ var require_rejection_tracking = __commonJS({
|
|
|
287
292
|
}
|
|
288
293
|
});
|
|
289
294
|
|
|
295
|
+
// src/auto-capture.ts
|
|
296
|
+
var AutoCapture = class {
|
|
297
|
+
constructor(handler) {
|
|
298
|
+
this.attached = false;
|
|
299
|
+
this.previousHandler = null;
|
|
300
|
+
this.handler = handler;
|
|
301
|
+
}
|
|
302
|
+
attach() {
|
|
303
|
+
if (this.attached) return;
|
|
304
|
+
this.previousHandler = ErrorUtils.getGlobalHandler();
|
|
305
|
+
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
306
|
+
this.handler("uncaughtException", error, { isFatal: isFatal ?? false });
|
|
307
|
+
if (this.previousHandler) {
|
|
308
|
+
this.previousHandler(error, isFatal);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
const tracking = require_rejection_tracking();
|
|
312
|
+
tracking.enable({
|
|
313
|
+
allRejections: true,
|
|
314
|
+
onUnhandled: (_id, error) => {
|
|
315
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
316
|
+
this.handler("unhandledRejection", err, { fatal: false });
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
this.attached = true;
|
|
320
|
+
}
|
|
321
|
+
detach() {
|
|
322
|
+
if (!this.attached) return;
|
|
323
|
+
if (this.previousHandler) {
|
|
324
|
+
ErrorUtils.setGlobalHandler(this.previousHandler);
|
|
325
|
+
this.previousHandler = null;
|
|
326
|
+
}
|
|
327
|
+
this.attached = false;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
290
331
|
// src/client.ts
|
|
291
332
|
var PulseBoardClient = class {
|
|
292
333
|
constructor(host, debug = false) {
|
|
@@ -342,6 +383,60 @@ var PulseBoardClient = class {
|
|
|
342
383
|
return false;
|
|
343
384
|
}
|
|
344
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
|
+
}
|
|
345
440
|
// ─── Private ──────────────────────────────────────────────────────
|
|
346
441
|
chunk(arr, size) {
|
|
347
442
|
return Array.from(
|
|
@@ -355,68 +450,6 @@ var PulseBoardClient = class {
|
|
|
355
450
|
}
|
|
356
451
|
}
|
|
357
452
|
};
|
|
358
|
-
|
|
359
|
-
// src/queue.ts
|
|
360
|
-
var EventQueue = class {
|
|
361
|
-
constructor(maxSize = 100) {
|
|
362
|
-
this.queue = [];
|
|
363
|
-
this.maxSize = maxSize;
|
|
364
|
-
}
|
|
365
|
-
enqueue(event) {
|
|
366
|
-
if (this.queue.length >= this.maxSize) {
|
|
367
|
-
this.queue.shift();
|
|
368
|
-
}
|
|
369
|
-
this.queue.push(event);
|
|
370
|
-
}
|
|
371
|
-
dequeue(count) {
|
|
372
|
-
return this.queue.splice(0, count);
|
|
373
|
-
}
|
|
374
|
-
get size() {
|
|
375
|
-
return this.queue.length;
|
|
376
|
-
}
|
|
377
|
-
get isEmpty() {
|
|
378
|
-
return this.queue.length === 0;
|
|
379
|
-
}
|
|
380
|
-
clear() {
|
|
381
|
-
this.queue = [];
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
// src/auto-capture.ts
|
|
386
|
-
var AutoCapture = class {
|
|
387
|
-
constructor(handler) {
|
|
388
|
-
this.attached = false;
|
|
389
|
-
this.previousHandler = null;
|
|
390
|
-
this.handler = handler;
|
|
391
|
-
}
|
|
392
|
-
attach() {
|
|
393
|
-
if (this.attached) return;
|
|
394
|
-
this.previousHandler = ErrorUtils.getGlobalHandler();
|
|
395
|
-
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
396
|
-
this.handler("uncaughtException", error, { isFatal: isFatal ?? false });
|
|
397
|
-
if (this.previousHandler) {
|
|
398
|
-
this.previousHandler(error, isFatal);
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
const tracking = require_rejection_tracking();
|
|
402
|
-
tracking.enable({
|
|
403
|
-
allRejections: true,
|
|
404
|
-
onUnhandled: (_id, error) => {
|
|
405
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
406
|
-
this.handler("unhandledRejection", err, { fatal: false });
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
this.attached = true;
|
|
410
|
-
}
|
|
411
|
-
detach() {
|
|
412
|
-
if (!this.attached) return;
|
|
413
|
-
if (this.previousHandler) {
|
|
414
|
-
ErrorUtils.setGlobalHandler(this.previousHandler);
|
|
415
|
-
this.previousHandler = null;
|
|
416
|
-
}
|
|
417
|
-
this.attached = false;
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
453
|
var NativePulseBoardDevice_default = reactNative.TurboModuleRegistry.getEnforcing("PulseBoardDevice");
|
|
421
454
|
var NativePulseBoardNetwork_default = reactNative.TurboModuleRegistry.getEnforcing("PulseBoardNetwork");
|
|
422
455
|
|
|
@@ -493,8 +526,8 @@ var ContextCollector = class {
|
|
|
493
526
|
manufacturer: deviceInfo?.manufacturer ?? "unknown",
|
|
494
527
|
brand: deviceInfo?.brand ?? "unknown",
|
|
495
528
|
isTablet: deviceInfo?.isTablet ?? false,
|
|
496
|
-
appVersion:
|
|
497
|
-
buildNumber:
|
|
529
|
+
appVersion: deviceInfo?.appVersion ?? "unknown",
|
|
530
|
+
buildNumber: deviceInfo?.buildNumber ?? "unknown",
|
|
498
531
|
bundleId: deviceInfo?.bundleId ?? "unknown",
|
|
499
532
|
screenWidth: deviceInfo?.screenWidth ?? width,
|
|
500
533
|
screenHeight: deviceInfo?.screenHeight ?? height,
|
|
@@ -519,6 +552,65 @@ var ContextCollector = class {
|
|
|
519
552
|
};
|
|
520
553
|
}
|
|
521
554
|
};
|
|
555
|
+
var QUEUE_KEY = "@pulseboard/event_queue";
|
|
556
|
+
var MAX_QUEUE_SIZE = 100;
|
|
557
|
+
var EventQueue = class {
|
|
558
|
+
constructor(maxSize = MAX_QUEUE_SIZE) {
|
|
559
|
+
this.memoryQueue = [];
|
|
560
|
+
this._hydrated = false;
|
|
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
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// ─── Public API ────────────────────────────────────────────────────
|
|
585
|
+
enqueue(event) {
|
|
586
|
+
if (this.memoryQueue.length >= this.maxSize) {
|
|
587
|
+
this.memoryQueue.shift();
|
|
588
|
+
}
|
|
589
|
+
this.memoryQueue.push(event);
|
|
590
|
+
this.persist();
|
|
591
|
+
}
|
|
592
|
+
dequeue(count) {
|
|
593
|
+
const batch = this.memoryQueue.splice(0, count);
|
|
594
|
+
this.persist();
|
|
595
|
+
return batch;
|
|
596
|
+
}
|
|
597
|
+
get size() {
|
|
598
|
+
return this.memoryQueue.length;
|
|
599
|
+
}
|
|
600
|
+
get isEmpty() {
|
|
601
|
+
return this.memoryQueue.length === 0;
|
|
602
|
+
}
|
|
603
|
+
get hydrated() {
|
|
604
|
+
return this._hydrated;
|
|
605
|
+
}
|
|
606
|
+
async clear() {
|
|
607
|
+
this.memoryQueue = [];
|
|
608
|
+
try {
|
|
609
|
+
await AsyncStorage__default.default.removeItem(QUEUE_KEY);
|
|
610
|
+
} catch {
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
};
|
|
522
614
|
|
|
523
615
|
// src/index.ts
|
|
524
616
|
var PulseBoardSDK = class {
|
|
@@ -530,23 +622,71 @@ var PulseBoardSDK = class {
|
|
|
530
622
|
this.contextCollector = null;
|
|
531
623
|
this.flushTimer = null;
|
|
532
624
|
this.initialized = false;
|
|
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 ?? "";
|
|
533
634
|
}
|
|
534
635
|
// ─── Public API ───────────────────────────────────────────────────
|
|
535
636
|
init(config) {
|
|
536
637
|
if (this.initialized) {
|
|
537
|
-
this.
|
|
638
|
+
this.debugLog("SDK already initialized \u2014 skipping");
|
|
538
639
|
return;
|
|
539
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
|
+
}
|
|
540
648
|
this.config = {
|
|
541
649
|
autoCapture: true,
|
|
542
650
|
debug: false,
|
|
543
651
|
flushInterval: 5e3,
|
|
544
652
|
maxQueueSize: 100,
|
|
545
|
-
...config
|
|
653
|
+
...config,
|
|
654
|
+
apiKey: config.apiKey ?? ""
|
|
655
|
+
// resolved below if using identifiers
|
|
546
656
|
};
|
|
547
|
-
this.client = new PulseBoardClient(this.
|
|
657
|
+
this.client = new PulseBoardClient(this.PULSEBOARD_API, this.config.debug);
|
|
548
658
|
this.queue = new EventQueue(this.config.maxQueueSize);
|
|
549
|
-
this.contextCollector = new ContextCollector(
|
|
659
|
+
this.contextCollector = new ContextCollector({
|
|
660
|
+
environment: config.environment
|
|
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() {
|
|
550
690
|
this.flushTimer = setInterval(
|
|
551
691
|
() => this.flush(),
|
|
552
692
|
this.config.flushInterval
|
|
@@ -558,7 +698,7 @@ var PulseBoardSDK = class {
|
|
|
558
698
|
this.autoCapture.attach();
|
|
559
699
|
}
|
|
560
700
|
this.initialized = true;
|
|
561
|
-
this.
|
|
701
|
+
this.debugLog(`Initialized \u2014 host: ${this.PULSEBOARD_API}`);
|
|
562
702
|
}
|
|
563
703
|
async getContext() {
|
|
564
704
|
this.assertInitialized("getContext");
|
|
@@ -567,7 +707,7 @@ var PulseBoardSDK = class {
|
|
|
567
707
|
identify(user) {
|
|
568
708
|
this.assertInitialized("identify");
|
|
569
709
|
this.contextCollector.identify(user);
|
|
570
|
-
this.
|
|
710
|
+
this.debugLog(`User identified: ${JSON.stringify(user)}`);
|
|
571
711
|
}
|
|
572
712
|
clearUser() {
|
|
573
713
|
this.assertInitialized("clearUser");
|
|
@@ -599,26 +739,87 @@ var PulseBoardSDK = class {
|
|
|
599
739
|
...options.payload
|
|
600
740
|
});
|
|
601
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
|
+
}
|
|
602
805
|
// ─── Analytics API ────────────────────────────────────────────────
|
|
603
806
|
startSession() {
|
|
604
807
|
this.assertInitialized("startSession");
|
|
605
808
|
this.contextCollector.collect().then((context) => {
|
|
606
809
|
this.client.sendAnalytics("session", {
|
|
607
|
-
apiKey: this.
|
|
810
|
+
apiKey: this.apiKey,
|
|
608
811
|
sessionId: context.session.sessionId,
|
|
609
812
|
startedAt: context.session.startedAt,
|
|
610
813
|
context
|
|
611
814
|
});
|
|
612
|
-
this.
|
|
613
|
-
}).catch((err) => {
|
|
614
|
-
this.log(`Failed to start session: ${err}`);
|
|
615
|
-
});
|
|
815
|
+
this.debugLog(`Session started: ${context.session.sessionId}`);
|
|
816
|
+
}).catch((err) => this.debugLog(`Failed to start session: ${err}`));
|
|
616
817
|
}
|
|
617
818
|
endSession(duration) {
|
|
618
819
|
this.assertInitialized("endSession");
|
|
619
820
|
this.contextCollector.collect().then((context) => {
|
|
620
821
|
this.client.sendAnalytics("session", {
|
|
621
|
-
apiKey: this.
|
|
822
|
+
apiKey: this.apiKey,
|
|
622
823
|
sessionId: context.session.sessionId,
|
|
623
824
|
startedAt: context.session.startedAt,
|
|
624
825
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -626,31 +827,27 @@ var PulseBoardSDK = class {
|
|
|
626
827
|
context
|
|
627
828
|
});
|
|
628
829
|
this.flush();
|
|
629
|
-
this.
|
|
630
|
-
}).catch((err) => {
|
|
631
|
-
this.log(`Failed to end session: ${err}`);
|
|
632
|
-
});
|
|
830
|
+
this.debugLog(`Session ended: ${context.session.sessionId}`);
|
|
831
|
+
}).catch((err) => this.debugLog(`Failed to end session: ${err}`));
|
|
633
832
|
}
|
|
634
833
|
trackScreen(screenName, loadTime) {
|
|
635
834
|
this.assertInitialized("trackScreen");
|
|
636
835
|
this.contextCollector.collect().then((context) => {
|
|
637
836
|
this.client.sendAnalytics("screen-view", {
|
|
638
|
-
apiKey: this.
|
|
837
|
+
apiKey: this.apiKey,
|
|
639
838
|
screenName,
|
|
640
839
|
loadTime,
|
|
641
840
|
sessionId: context.session.sessionId,
|
|
642
841
|
context
|
|
643
842
|
});
|
|
644
|
-
this.
|
|
645
|
-
}).catch((err) => {
|
|
646
|
-
this.log(`Failed to track screen: ${err}`);
|
|
647
|
-
});
|
|
843
|
+
this.debugLog(`Screen tracked: ${screenName}`);
|
|
844
|
+
}).catch((err) => this.debugLog(`Failed to track screen: ${err}`));
|
|
648
845
|
}
|
|
649
846
|
trackApiCall(endpoint, httpMethod, statusCode, duration, payloadSize) {
|
|
650
847
|
this.assertInitialized("trackApiCall");
|
|
651
848
|
this.contextCollector.collect().then((context) => {
|
|
652
849
|
this.client.sendAnalytics("api-call", {
|
|
653
|
-
apiKey: this.
|
|
850
|
+
apiKey: this.apiKey,
|
|
654
851
|
endpoint,
|
|
655
852
|
httpMethod,
|
|
656
853
|
statusCode,
|
|
@@ -659,16 +856,16 @@ var PulseBoardSDK = class {
|
|
|
659
856
|
sessionId: context.session.sessionId,
|
|
660
857
|
context
|
|
661
858
|
});
|
|
662
|
-
this.
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
});
|
|
859
|
+
this.debugLog(
|
|
860
|
+
`API call tracked: ${httpMethod} ${endpoint} ${statusCode}`
|
|
861
|
+
);
|
|
862
|
+
}).catch((err) => this.debugLog(`Failed to track API call: ${err}`));
|
|
666
863
|
}
|
|
667
864
|
trackCrash(error, isFatal = false) {
|
|
668
865
|
this.assertInitialized("trackCrash");
|
|
669
866
|
this.contextCollector.collect().then((context) => {
|
|
670
867
|
this.client.sendAnalytics("crash", {
|
|
671
|
-
apiKey: this.
|
|
868
|
+
apiKey: this.apiKey,
|
|
672
869
|
errorName: error.name,
|
|
673
870
|
errorMessage: error.message,
|
|
674
871
|
stackTrace: error.stack ?? "",
|
|
@@ -676,17 +873,46 @@ var PulseBoardSDK = class {
|
|
|
676
873
|
sessionId: context.session.sessionId,
|
|
677
874
|
context
|
|
678
875
|
});
|
|
679
|
-
this.
|
|
680
|
-
}).catch((err) => {
|
|
681
|
-
this.log(`Failed to track crash: ${err}`);
|
|
682
|
-
});
|
|
876
|
+
this.debugLog(`Crash tracked: ${error.name} \u2014 fatal: ${isFatal}`);
|
|
877
|
+
}).catch((err) => this.debugLog(`Failed to track crash: ${err}`));
|
|
683
878
|
}
|
|
684
|
-
|
|
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 ──────────────────────────────────────────────
|
|
685
897
|
async flush() {
|
|
686
|
-
if (!this.queue || this.
|
|
687
|
-
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;
|
|
688
914
|
const events = this.queue.dequeue(10);
|
|
689
|
-
this.
|
|
915
|
+
this.debugLog(`Flushing ${events.length} event(s)`);
|
|
690
916
|
await this.client.sendBatch(events);
|
|
691
917
|
}
|
|
692
918
|
destroy() {
|
|
@@ -694,20 +920,22 @@ var PulseBoardSDK = class {
|
|
|
694
920
|
clearInterval(this.flushTimer);
|
|
695
921
|
this.flushTimer = null;
|
|
696
922
|
}
|
|
923
|
+
this.releaseConsole();
|
|
697
924
|
this.autoCapture?.detach();
|
|
698
925
|
this.queue?.clear();
|
|
926
|
+
this.logQueue = [];
|
|
699
927
|
this.initialized = false;
|
|
700
928
|
this.config = null;
|
|
701
929
|
this.client = null;
|
|
702
930
|
this.queue = null;
|
|
703
931
|
this.contextCollector = null;
|
|
704
|
-
this.
|
|
932
|
+
this.debugLog("SDK destroyed");
|
|
705
933
|
}
|
|
706
|
-
// ─── Private
|
|
934
|
+
// ─── Private ──────────────────────────────────────────────────────
|
|
707
935
|
buildAndEnqueue(type, name, payload, timestamp) {
|
|
708
936
|
this.contextCollector.collect().then((context) => {
|
|
709
937
|
const event = {
|
|
710
|
-
apiKey: this.
|
|
938
|
+
apiKey: this.apiKey,
|
|
711
939
|
type,
|
|
712
940
|
name,
|
|
713
941
|
payload,
|
|
@@ -715,20 +943,16 @@ var PulseBoardSDK = class {
|
|
|
715
943
|
context
|
|
716
944
|
};
|
|
717
945
|
this.queue.enqueue(event);
|
|
718
|
-
this.
|
|
719
|
-
if (type === "error")
|
|
720
|
-
|
|
721
|
-
}
|
|
722
|
-
}).catch((err) => {
|
|
723
|
-
this.log(`Failed to collect context: ${err}`);
|
|
724
|
-
});
|
|
946
|
+
this.debugLog(`Queued: ${type} \u2014 ${name}`);
|
|
947
|
+
if (type === "error") this.flush();
|
|
948
|
+
}).catch((err) => this.debugLog(`Failed to collect context: ${err}`));
|
|
725
949
|
}
|
|
726
950
|
assertInitialized(method) {
|
|
727
951
|
if (!this.initialized) {
|
|
728
952
|
throw new Error(`PulseBoard.${method}() called before PulseBoard.init()`);
|
|
729
953
|
}
|
|
730
954
|
}
|
|
731
|
-
|
|
955
|
+
debugLog(message) {
|
|
732
956
|
if (this.config?.debug) {
|
|
733
957
|
console.log(`[PulseBoard] ${message}`);
|
|
734
958
|
}
|