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