@pulseboard/react-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,741 @@
1
+ 'use strict';
2
+
3
+ var reactNative = require('react-native');
4
+
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __commonJS = (cb, mod) => function __require() {
7
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
8
+ };
9
+
10
+ // node_modules/promise/setimmediate/core.js
11
+ var require_core = __commonJS({
12
+ "node_modules/promise/setimmediate/core.js"(exports$1, module) {
13
+ function noop() {
14
+ }
15
+ var LAST_ERROR = null;
16
+ var IS_ERROR = {};
17
+ function getThen(obj) {
18
+ try {
19
+ return obj.then;
20
+ } catch (ex) {
21
+ LAST_ERROR = ex;
22
+ return IS_ERROR;
23
+ }
24
+ }
25
+ function tryCallOne(fn, a) {
26
+ try {
27
+ return fn(a);
28
+ } catch (ex) {
29
+ LAST_ERROR = ex;
30
+ return IS_ERROR;
31
+ }
32
+ }
33
+ function tryCallTwo(fn, a, b) {
34
+ try {
35
+ fn(a, b);
36
+ } catch (ex) {
37
+ LAST_ERROR = ex;
38
+ return IS_ERROR;
39
+ }
40
+ }
41
+ module.exports = Promise2;
42
+ function Promise2(fn) {
43
+ if (typeof this !== "object") {
44
+ throw new TypeError("Promises must be constructed via new");
45
+ }
46
+ if (typeof fn !== "function") {
47
+ throw new TypeError("Promise constructor's argument is not a function");
48
+ }
49
+ this._x = 0;
50
+ this._y = 0;
51
+ this._z = null;
52
+ this._A = null;
53
+ if (fn === noop) return;
54
+ doResolve(fn, this);
55
+ }
56
+ Promise2._B = null;
57
+ Promise2._C = null;
58
+ Promise2._D = noop;
59
+ Promise2.prototype.then = function(onFulfilled, onRejected) {
60
+ if (this.constructor !== Promise2) {
61
+ return safeThen(this, onFulfilled, onRejected);
62
+ }
63
+ var res = new Promise2(noop);
64
+ handle(this, new Handler(onFulfilled, onRejected, res));
65
+ return res;
66
+ };
67
+ function safeThen(self, onFulfilled, onRejected) {
68
+ return new self.constructor(function(resolve2, reject2) {
69
+ var res = new Promise2(noop);
70
+ res.then(resolve2, reject2);
71
+ handle(self, new Handler(onFulfilled, onRejected, res));
72
+ });
73
+ }
74
+ function handle(self, deferred) {
75
+ while (self._y === 3) {
76
+ self = self._z;
77
+ }
78
+ if (Promise2._B) {
79
+ Promise2._B(self);
80
+ }
81
+ if (self._y === 0) {
82
+ if (self._x === 0) {
83
+ self._x = 1;
84
+ self._A = deferred;
85
+ return;
86
+ }
87
+ if (self._x === 1) {
88
+ self._x = 2;
89
+ self._A = [self._A, deferred];
90
+ return;
91
+ }
92
+ self._A.push(deferred);
93
+ return;
94
+ }
95
+ handleResolved(self, deferred);
96
+ }
97
+ function handleResolved(self, deferred) {
98
+ setImmediate(function() {
99
+ var cb = self._y === 1 ? deferred.onFulfilled : deferred.onRejected;
100
+ if (cb === null) {
101
+ if (self._y === 1) {
102
+ resolve(deferred.promise, self._z);
103
+ } else {
104
+ reject(deferred.promise, self._z);
105
+ }
106
+ return;
107
+ }
108
+ var ret = tryCallOne(cb, self._z);
109
+ if (ret === IS_ERROR) {
110
+ reject(deferred.promise, LAST_ERROR);
111
+ } else {
112
+ resolve(deferred.promise, ret);
113
+ }
114
+ });
115
+ }
116
+ function resolve(self, newValue) {
117
+ if (newValue === self) {
118
+ return reject(
119
+ self,
120
+ new TypeError("A promise cannot be resolved with itself.")
121
+ );
122
+ }
123
+ if (newValue && (typeof newValue === "object" || typeof newValue === "function")) {
124
+ var then = getThen(newValue);
125
+ if (then === IS_ERROR) {
126
+ return reject(self, LAST_ERROR);
127
+ }
128
+ if (then === self.then && newValue instanceof Promise2) {
129
+ self._y = 3;
130
+ self._z = newValue;
131
+ finale(self);
132
+ return;
133
+ } else if (typeof then === "function") {
134
+ doResolve(then.bind(newValue), self);
135
+ return;
136
+ }
137
+ }
138
+ self._y = 1;
139
+ self._z = newValue;
140
+ finale(self);
141
+ }
142
+ function reject(self, newValue) {
143
+ self._y = 2;
144
+ self._z = newValue;
145
+ if (Promise2._C) {
146
+ Promise2._C(self, newValue);
147
+ }
148
+ finale(self);
149
+ }
150
+ function finale(self) {
151
+ if (self._x === 1) {
152
+ handle(self, self._A);
153
+ self._A = null;
154
+ }
155
+ if (self._x === 2) {
156
+ for (var i = 0; i < self._A.length; i++) {
157
+ handle(self, self._A[i]);
158
+ }
159
+ self._A = null;
160
+ }
161
+ }
162
+ function Handler(onFulfilled, onRejected, promise) {
163
+ this.onFulfilled = typeof onFulfilled === "function" ? onFulfilled : null;
164
+ this.onRejected = typeof onRejected === "function" ? onRejected : null;
165
+ this.promise = promise;
166
+ }
167
+ function doResolve(fn, promise) {
168
+ var done = false;
169
+ var res = tryCallTwo(fn, function(value) {
170
+ if (done) return;
171
+ done = true;
172
+ resolve(promise, value);
173
+ }, function(reason) {
174
+ if (done) return;
175
+ done = true;
176
+ reject(promise, reason);
177
+ });
178
+ if (!done && res === IS_ERROR) {
179
+ done = true;
180
+ reject(promise, LAST_ERROR);
181
+ }
182
+ }
183
+ }
184
+ });
185
+
186
+ // node_modules/promise/setimmediate/rejection-tracking.js
187
+ var require_rejection_tracking = __commonJS({
188
+ "node_modules/promise/setimmediate/rejection-tracking.js"(exports$1) {
189
+ var Promise2 = require_core();
190
+ var DEFAULT_WHITELIST = [
191
+ ReferenceError,
192
+ TypeError,
193
+ RangeError
194
+ ];
195
+ var enabled = false;
196
+ exports$1.disable = disable;
197
+ function disable() {
198
+ enabled = false;
199
+ Promise2._B = null;
200
+ Promise2._C = null;
201
+ }
202
+ exports$1.enable = enable;
203
+ function enable(options) {
204
+ options = options || {};
205
+ if (enabled) disable();
206
+ enabled = true;
207
+ var id = 0;
208
+ var displayId = 0;
209
+ var rejections = {};
210
+ Promise2._B = function(promise) {
211
+ if (promise._y === 2 && // IS REJECTED
212
+ rejections[promise._E]) {
213
+ if (rejections[promise._E].logged) {
214
+ onHandled(promise._E);
215
+ } else {
216
+ clearTimeout(rejections[promise._E].timeout);
217
+ }
218
+ delete rejections[promise._E];
219
+ }
220
+ };
221
+ Promise2._C = function(promise, err) {
222
+ if (promise._x === 0) {
223
+ promise._E = id++;
224
+ rejections[promise._E] = {
225
+ displayId: null,
226
+ error: err,
227
+ timeout: setTimeout(
228
+ onUnhandled.bind(null, promise._E),
229
+ // For reference errors and type errors, this almost always
230
+ // means the programmer made a mistake, so log them after just
231
+ // 100ms
232
+ // otherwise, wait 2 seconds to see if they get handled
233
+ matchWhitelist(err, DEFAULT_WHITELIST) ? 100 : 2e3
234
+ ),
235
+ logged: false
236
+ };
237
+ }
238
+ };
239
+ function onUnhandled(id2) {
240
+ if (options.allRejections || matchWhitelist(
241
+ rejections[id2].error,
242
+ options.whitelist || DEFAULT_WHITELIST
243
+ )) {
244
+ rejections[id2].displayId = displayId++;
245
+ if (options.onUnhandled) {
246
+ rejections[id2].logged = true;
247
+ options.onUnhandled(
248
+ rejections[id2].displayId,
249
+ rejections[id2].error
250
+ );
251
+ } else {
252
+ rejections[id2].logged = true;
253
+ logError(
254
+ rejections[id2].displayId,
255
+ rejections[id2].error
256
+ );
257
+ }
258
+ }
259
+ }
260
+ function onHandled(id2) {
261
+ if (rejections[id2].logged) {
262
+ if (options.onHandled) {
263
+ options.onHandled(rejections[id2].displayId, rejections[id2].error);
264
+ } else if (!rejections[id2].onUnhandled) {
265
+ console.warn(
266
+ "Promise Rejection Handled (id: " + rejections[id2].displayId + "):"
267
+ );
268
+ console.warn(
269
+ ' This means you can ignore any previous messages of the form "Possible Unhandled Promise Rejection" with id ' + rejections[id2].displayId + "."
270
+ );
271
+ }
272
+ }
273
+ }
274
+ }
275
+ function logError(id, error) {
276
+ console.warn("Possible Unhandled Promise Rejection (id: " + id + "):");
277
+ var errStr = (error && (error.stack || error)) + "";
278
+ errStr.split("\n").forEach(function(line) {
279
+ console.warn(" " + line);
280
+ });
281
+ }
282
+ function matchWhitelist(error, list) {
283
+ return list.some(function(cls) {
284
+ return error instanceof cls;
285
+ });
286
+ }
287
+ }
288
+ });
289
+
290
+ // src/client.ts
291
+ var PulseBoardClient = class {
292
+ constructor(host, debug = false) {
293
+ this.host = host.replace(/\/$/, "");
294
+ this.debug = debug;
295
+ }
296
+ // ─── Event Ingest ─────────────────────────────────────────────────
297
+ async send(event) {
298
+ try {
299
+ const response = await fetch(`${this.host}/ingest`, {
300
+ method: "POST",
301
+ headers: { "Content-Type": "application/json" },
302
+ body: JSON.stringify(event)
303
+ });
304
+ if (!response.ok) {
305
+ this.log(
306
+ `Failed to send event: ${response.status} ${response.statusText}`
307
+ );
308
+ return false;
309
+ }
310
+ this.log(`Event sent: ${event.type} \u2014 ${event.name}`);
311
+ return true;
312
+ } catch (err) {
313
+ this.log(`Network error sending event: ${err}`);
314
+ return false;
315
+ }
316
+ }
317
+ async sendBatch(events) {
318
+ const chunks = this.chunk(events, 5);
319
+ for (const chunk of chunks) {
320
+ await Promise.all(chunk.map((e) => this.send(e)));
321
+ }
322
+ return true;
323
+ }
324
+ // ─── Analytics Ingest ─────────────────────────────────────────────
325
+ async sendAnalytics(type, payload) {
326
+ try {
327
+ const response = await fetch(`${this.host}/analytics/${type}`, {
328
+ method: "POST",
329
+ headers: { "Content-Type": "application/json" },
330
+ body: JSON.stringify(payload)
331
+ });
332
+ if (!response.ok) {
333
+ this.log(
334
+ `Failed to send analytics: ${response.status} ${response.statusText}`
335
+ );
336
+ return false;
337
+ }
338
+ this.log(`Analytics sent: ${type}`);
339
+ return true;
340
+ } catch (err) {
341
+ this.log(`Network error sending analytics: ${err}`);
342
+ return false;
343
+ }
344
+ }
345
+ // ─── Private ──────────────────────────────────────────────────────
346
+ chunk(arr, size) {
347
+ return Array.from(
348
+ { length: Math.ceil(arr.length / size) },
349
+ (_, i) => arr.slice(i * size, i * size + size)
350
+ );
351
+ }
352
+ log(message) {
353
+ if (this.debug) {
354
+ console.log(`[PulseBoard] ${message}`);
355
+ }
356
+ }
357
+ };
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
+ var NativePulseBoardDevice_default = reactNative.TurboModuleRegistry.getEnforcing("PulseBoardDevice");
421
+ var NativePulseBoardNetwork_default = reactNative.TurboModuleRegistry.getEnforcing("PulseBoardNetwork");
422
+
423
+ // src/context.ts
424
+ function generateSessionId() {
425
+ const timestamp = Date.now().toString(36);
426
+ const random = Math.random().toString(36).substring(2, 10);
427
+ return `${timestamp}-${random}`;
428
+ }
429
+ function mapNetworkType(type) {
430
+ switch (type) {
431
+ case "wifi":
432
+ return "wifi";
433
+ case "cellular":
434
+ return "cellular";
435
+ case "offline":
436
+ return "offline";
437
+ default:
438
+ return "unknown";
439
+ }
440
+ }
441
+ function getTimezone() {
442
+ try {
443
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
444
+ } catch {
445
+ return "unknown";
446
+ }
447
+ }
448
+ function getLanguage() {
449
+ try {
450
+ return reactNative.I18nManager.getConstants?.()?.localeIdentifier?.replace("_", "-") ?? Intl.DateTimeFormat().resolvedOptions().locale ?? "unknown";
451
+ } catch {
452
+ return "unknown";
453
+ }
454
+ }
455
+ var ContextCollector = class {
456
+ constructor(appContext = {}) {
457
+ this.userContext = {};
458
+ this.appContext = appContext;
459
+ this.sessionContext = {
460
+ sessionId: generateSessionId(),
461
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
462
+ };
463
+ }
464
+ identify(user) {
465
+ this.userContext = { ...this.userContext, ...user };
466
+ }
467
+ clearUser() {
468
+ this.userContext = {};
469
+ }
470
+ async collect() {
471
+ const { width, height } = reactNative.Dimensions.get("window");
472
+ let deviceInfo = null;
473
+ let networkInfo = null;
474
+ try {
475
+ if (NativePulseBoardDevice_default) {
476
+ deviceInfo = await NativePulseBoardDevice_default.getDeviceInfo();
477
+ }
478
+ } catch (e) {
479
+ console.warn("[PulseBoard] Failed to collect device info:", e);
480
+ }
481
+ try {
482
+ if (NativePulseBoardNetwork_default) {
483
+ networkInfo = await NativePulseBoardNetwork_default.getNetworkInfo();
484
+ }
485
+ } catch (e) {
486
+ console.warn("[PulseBoard] Failed to collect network info:", e);
487
+ }
488
+ const device = {
489
+ platform: reactNative.Platform.OS === "ios" ? "ios" : "android",
490
+ os: deviceInfo?.os ?? (reactNative.Platform.OS === "ios" ? "iOS" : "Android"),
491
+ osVersion: deviceInfo?.osVersion ?? reactNative.Platform.Version?.toString() ?? "unknown",
492
+ model: deviceInfo?.model ?? "unknown",
493
+ manufacturer: deviceInfo?.manufacturer ?? "unknown",
494
+ brand: deviceInfo?.brand ?? "unknown",
495
+ isTablet: deviceInfo?.isTablet ?? false,
496
+ appVersion: this.appContext.appVersion ?? deviceInfo?.appVersion ?? "unknown",
497
+ buildNumber: this.appContext.buildNumber ?? deviceInfo?.buildNumber ?? "unknown",
498
+ bundleId: deviceInfo?.bundleId ?? "unknown",
499
+ screenWidth: deviceInfo?.screenWidth ?? width,
500
+ screenHeight: deviceInfo?.screenHeight ?? height,
501
+ fontScale: reactNative.Dimensions.get("window").fontScale ?? 1,
502
+ isEmulator: deviceInfo?.isEmulator ?? false,
503
+ language: getLanguage(),
504
+ timezone: getTimezone()
505
+ };
506
+ const network = {
507
+ type: mapNetworkType(networkInfo?.type ?? "unknown"),
508
+ isConnected: networkInfo?.isConnected ?? false,
509
+ isWifiEnabled: networkInfo?.isWifiEnabled ?? false,
510
+ carrier: networkInfo?.carrier ?? "unknown",
511
+ ipAddress: networkInfo?.ipAddress ?? "unknown"
512
+ };
513
+ return {
514
+ app: this.appContext,
515
+ device,
516
+ network,
517
+ session: this.sessionContext,
518
+ user: this.userContext
519
+ };
520
+ }
521
+ };
522
+
523
+ // src/index.ts
524
+ var PulseBoardSDK = class {
525
+ constructor() {
526
+ this.config = null;
527
+ this.client = null;
528
+ this.queue = null;
529
+ this.autoCapture = null;
530
+ this.contextCollector = null;
531
+ this.flushTimer = null;
532
+ this.initialized = false;
533
+ }
534
+ // ─── Public API ───────────────────────────────────────────────────
535
+ init(config) {
536
+ if (this.initialized) {
537
+ this.log("SDK already initialized \u2014 skipping");
538
+ return;
539
+ }
540
+ this.config = {
541
+ autoCapture: true,
542
+ debug: false,
543
+ flushInterval: 5e3,
544
+ maxQueueSize: 100,
545
+ ...config
546
+ };
547
+ this.client = new PulseBoardClient(this.config.host, this.config.debug);
548
+ this.queue = new EventQueue(this.config.maxQueueSize);
549
+ this.contextCollector = new ContextCollector(this.config.app ?? {});
550
+ this.flushTimer = setInterval(
551
+ () => this.flush(),
552
+ this.config.flushInterval
553
+ );
554
+ if (this.config.autoCapture) {
555
+ this.autoCapture = new AutoCapture((name, error, context) => {
556
+ this.captureError(error, { payload: context });
557
+ });
558
+ this.autoCapture.attach();
559
+ }
560
+ this.initialized = true;
561
+ this.log(`Initialized \u2014 host: ${this.config.host}`);
562
+ }
563
+ async getContext() {
564
+ this.assertInitialized("getContext");
565
+ return this.contextCollector.collect();
566
+ }
567
+ identify(user) {
568
+ this.assertInitialized("identify");
569
+ this.contextCollector.identify(user);
570
+ this.log(`User identified: ${JSON.stringify(user)}`);
571
+ }
572
+ clearUser() {
573
+ this.assertInitialized("clearUser");
574
+ this.contextCollector.clearUser();
575
+ }
576
+ track(name, options = {}) {
577
+ this.assertInitialized("track");
578
+ this.buildAndEnqueue(
579
+ "event",
580
+ name,
581
+ options.payload ?? {},
582
+ options.timestamp
583
+ );
584
+ }
585
+ metric(name, value, options = {}) {
586
+ this.assertInitialized("metric");
587
+ this.buildAndEnqueue(
588
+ "metric",
589
+ name,
590
+ { value, ...options.payload },
591
+ options.timestamp
592
+ );
593
+ }
594
+ captureError(error, options = {}) {
595
+ this.assertInitialized("captureError");
596
+ this.buildAndEnqueue("error", error.name ?? "UnknownError", {
597
+ message: error.message,
598
+ stack: error.stack,
599
+ ...options.payload
600
+ });
601
+ }
602
+ // ─── Analytics API ────────────────────────────────────────────────
603
+ startSession() {
604
+ this.assertInitialized("startSession");
605
+ this.contextCollector.collect().then((context) => {
606
+ this.client.sendAnalytics("session", {
607
+ apiKey: this.config.apiKey,
608
+ sessionId: context.session.sessionId,
609
+ startedAt: context.session.startedAt,
610
+ context
611
+ });
612
+ this.log(`Session started: ${context.session.sessionId}`);
613
+ }).catch((err) => {
614
+ this.log(`Failed to start session: ${err}`);
615
+ });
616
+ }
617
+ endSession(duration) {
618
+ this.assertInitialized("endSession");
619
+ this.contextCollector.collect().then((context) => {
620
+ this.client.sendAnalytics("session", {
621
+ apiKey: this.config.apiKey,
622
+ sessionId: context.session.sessionId,
623
+ startedAt: context.session.startedAt,
624
+ endedAt: (/* @__PURE__ */ new Date()).toISOString(),
625
+ duration,
626
+ context
627
+ });
628
+ this.flush();
629
+ this.log(`Session ended: ${context.session.sessionId}`);
630
+ }).catch((err) => {
631
+ this.log(`Failed to end session: ${err}`);
632
+ });
633
+ }
634
+ trackScreen(screenName, loadTime) {
635
+ this.assertInitialized("trackScreen");
636
+ this.contextCollector.collect().then((context) => {
637
+ this.client.sendAnalytics("screen-view", {
638
+ apiKey: this.config.apiKey,
639
+ screenName,
640
+ loadTime,
641
+ sessionId: context.session.sessionId,
642
+ context
643
+ });
644
+ this.log(`Screen tracked: ${screenName}`);
645
+ }).catch((err) => {
646
+ this.log(`Failed to track screen: ${err}`);
647
+ });
648
+ }
649
+ trackApiCall(endpoint, httpMethod, statusCode, duration, payloadSize) {
650
+ this.assertInitialized("trackApiCall");
651
+ this.contextCollector.collect().then((context) => {
652
+ this.client.sendAnalytics("api-call", {
653
+ apiKey: this.config.apiKey,
654
+ endpoint,
655
+ httpMethod,
656
+ statusCode,
657
+ duration,
658
+ payloadSize,
659
+ sessionId: context.session.sessionId,
660
+ context
661
+ });
662
+ this.log(`API call tracked: ${httpMethod} ${endpoint} ${statusCode}`);
663
+ }).catch((err) => {
664
+ this.log(`Failed to track API call: ${err}`);
665
+ });
666
+ }
667
+ trackCrash(error, isFatal = false) {
668
+ this.assertInitialized("trackCrash");
669
+ this.contextCollector.collect().then((context) => {
670
+ this.client.sendAnalytics("crash", {
671
+ apiKey: this.config.apiKey,
672
+ errorName: error.name,
673
+ errorMessage: error.message,
674
+ stackTrace: error.stack ?? "",
675
+ isFatal,
676
+ sessionId: context.session.sessionId,
677
+ context
678
+ });
679
+ this.log(`Crash tracked: ${error.name} \u2014 fatal: ${isFatal}`);
680
+ }).catch((err) => {
681
+ this.log(`Failed to track crash: ${err}`);
682
+ });
683
+ }
684
+ // ─── Flush & Destroy ─────────────────────────────────────────────
685
+ async flush() {
686
+ if (!this.queue || this.queue.isEmpty) return;
687
+ if (!this.client) return;
688
+ const events = this.queue.dequeue(10);
689
+ this.log(`Flushing ${events.length} event(s)`);
690
+ await this.client.sendBatch(events);
691
+ }
692
+ destroy() {
693
+ if (this.flushTimer) {
694
+ clearInterval(this.flushTimer);
695
+ this.flushTimer = null;
696
+ }
697
+ this.autoCapture?.detach();
698
+ this.queue?.clear();
699
+ this.initialized = false;
700
+ this.config = null;
701
+ this.client = null;
702
+ this.queue = null;
703
+ this.contextCollector = null;
704
+ this.log("SDK destroyed");
705
+ }
706
+ // ─── Private ─────────────────────────────────────────────────────
707
+ buildAndEnqueue(type, name, payload, timestamp) {
708
+ this.contextCollector.collect().then((context) => {
709
+ const event = {
710
+ apiKey: this.config.apiKey,
711
+ type,
712
+ name,
713
+ payload,
714
+ timestamp: timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
715
+ context
716
+ };
717
+ this.queue.enqueue(event);
718
+ this.log(`Queued: ${type} \u2014 ${name}`);
719
+ if (type === "error") {
720
+ this.flush();
721
+ }
722
+ }).catch((err) => {
723
+ this.log(`Failed to collect context: ${err}`);
724
+ });
725
+ }
726
+ assertInitialized(method) {
727
+ if (!this.initialized) {
728
+ throw new Error(`PulseBoard.${method}() called before PulseBoard.init()`);
729
+ }
730
+ }
731
+ log(message) {
732
+ if (this.config?.debug) {
733
+ console.log(`[PulseBoard] ${message}`);
734
+ }
735
+ }
736
+ };
737
+ var PulseBoard = new PulseBoardSDK();
738
+
739
+ exports.PulseBoard = PulseBoard;
740
+ //# sourceMappingURL=index.js.map
741
+ //# sourceMappingURL=index.js.map