@qhr123/sa2kit 0.2.0 → 0.3.1

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.
@@ -0,0 +1,1215 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/analytics/types.ts
6
+ var EventType = /* @__PURE__ */ ((EventType2) => {
7
+ EventType2["PAGE_VIEW"] = "page_view";
8
+ EventType2["PAGE_LEAVE"] = "page_leave";
9
+ EventType2["CLICK"] = "click";
10
+ EventType2["SCROLL"] = "scroll";
11
+ EventType2["INPUT"] = "input";
12
+ EventType2["SUBMIT"] = "submit";
13
+ EventType2["LOGIN"] = "login";
14
+ EventType2["LOGOUT"] = "logout";
15
+ EventType2["REGISTER"] = "register";
16
+ EventType2["SEARCH"] = "search";
17
+ EventType2["SHARE"] = "share";
18
+ EventType2["PERFORMANCE"] = "performance";
19
+ EventType2["ERROR"] = "error";
20
+ EventType2["API_CALL"] = "api_call";
21
+ EventType2["CUSTOM"] = "custom";
22
+ return EventType2;
23
+ })(EventType || {});
24
+ var EventPriority = /* @__PURE__ */ ((EventPriority2) => {
25
+ EventPriority2[EventPriority2["LOW"] = 0] = "LOW";
26
+ EventPriority2[EventPriority2["NORMAL"] = 1] = "NORMAL";
27
+ EventPriority2[EventPriority2["HIGH"] = 2] = "HIGH";
28
+ EventPriority2[EventPriority2["CRITICAL"] = 3] = "CRITICAL";
29
+ return EventPriority2;
30
+ })(EventPriority || {});
31
+
32
+ // src/analytics/core/EventQueue.ts
33
+ var EventQueue = class {
34
+ constructor(maxSize = 100) {
35
+ this.queue = [];
36
+ this.maxSize = maxSize;
37
+ }
38
+ /**
39
+ * 添加事件到队列
40
+ */
41
+ enqueue(event) {
42
+ if (this.queue.length >= this.maxSize) {
43
+ this.removeLowestPriorityEvent();
44
+ }
45
+ this.queue.push(event);
46
+ this.queue.sort((a, b) => b.priority - a.priority);
47
+ }
48
+ /**
49
+ * 批量添加事件
50
+ */
51
+ enqueueBatch(events) {
52
+ events.forEach((event) => this.enqueue(event));
53
+ }
54
+ /**
55
+ * 获取指定数量的事件
56
+ */
57
+ dequeue(count) {
58
+ return this.queue.splice(0, count);
59
+ }
60
+ /**
61
+ * 获取所有事件
62
+ */
63
+ dequeueAll() {
64
+ const events = [...this.queue];
65
+ this.queue = [];
66
+ return events;
67
+ }
68
+ /**
69
+ * 获取高优先级事件
70
+ */
71
+ getHighPriorityEvents() {
72
+ const highPriorityEvents = this.queue.filter(
73
+ (event) => event.priority >= 2
74
+ // HIGH 和 CRITICAL
75
+ );
76
+ this.queue = this.queue.filter((event) => event.priority < 2);
77
+ return highPriorityEvents;
78
+ }
79
+ /**
80
+ * 查看队列长度
81
+ */
82
+ size() {
83
+ return this.queue.length;
84
+ }
85
+ /**
86
+ * 队列是否为空
87
+ */
88
+ isEmpty() {
89
+ return this.queue.length === 0;
90
+ }
91
+ /**
92
+ * 队列是否已满
93
+ */
94
+ isFull() {
95
+ return this.queue.length >= this.maxSize;
96
+ }
97
+ /**
98
+ * 清空队列
99
+ */
100
+ clear() {
101
+ this.queue = [];
102
+ }
103
+ /**
104
+ * 移除最低优先级的事件
105
+ */
106
+ removeLowestPriorityEvent() {
107
+ if (this.queue.length === 0) return;
108
+ let lowestPriorityIndex = 0;
109
+ let lowestPriority = this.queue[0]?.priority ?? 0;
110
+ for (let i = 1; i < this.queue.length; i++) {
111
+ const currentPriority = this.queue[i]?.priority ?? 0;
112
+ if (currentPriority < lowestPriority) {
113
+ lowestPriority = currentPriority;
114
+ lowestPriorityIndex = i;
115
+ }
116
+ }
117
+ this.queue.splice(lowestPriorityIndex, 1);
118
+ }
119
+ /**
120
+ * 获取队列快照(不移除事件)
121
+ */
122
+ snapshot() {
123
+ return [...this.queue];
124
+ }
125
+ };
126
+
127
+ // src/analytics/core/Uploader.ts
128
+ var Uploader = class {
129
+ constructor(config) {
130
+ this.uploading = false;
131
+ this.retryQueue = /* @__PURE__ */ new Map();
132
+ this.config = config;
133
+ }
134
+ /**
135
+ * 上传事件
136
+ */
137
+ async upload(events) {
138
+ if (events.length === 0) return true;
139
+ const isOnline = await this.config.networkAdapter.isOnline();
140
+ if (!isOnline) {
141
+ await this.saveToLocal(events);
142
+ return false;
143
+ }
144
+ try {
145
+ this.uploading = true;
146
+ const response = await this.config.networkAdapter.upload(this.config.endpoint, events);
147
+ if (response.success) {
148
+ this.config.onSuccess?.(events);
149
+ return true;
150
+ } else {
151
+ throw new Error(response.message || "Upload failed");
152
+ }
153
+ } catch (error) {
154
+ const err = error instanceof Error ? error : new Error(String(error));
155
+ this.config.onError?.(err, events);
156
+ await this.addToRetryQueue(events);
157
+ return false;
158
+ } finally {
159
+ this.uploading = false;
160
+ }
161
+ }
162
+ /**
163
+ * 批量上传
164
+ */
165
+ async uploadBatch(events) {
166
+ const batches = this.splitIntoBatches(events, this.config.batchSize);
167
+ const results = await Promise.all(batches.map((batch) => this.upload(batch)));
168
+ return results.every((result) => result);
169
+ }
170
+ /**
171
+ * 重试失败的上传
172
+ */
173
+ async retryFailedUploads() {
174
+ if (this.uploading || this.retryQueue.size === 0) return;
175
+ const isOnline = await this.config.networkAdapter.isOnline();
176
+ if (!isOnline) return;
177
+ const entries = Array.from(this.retryQueue.entries());
178
+ for (const [key, item] of entries) {
179
+ if (item.retryCount >= this.config.retryTimes) {
180
+ await this.saveToLocal(item.events);
181
+ this.retryQueue.delete(key);
182
+ continue;
183
+ }
184
+ await this.delay(this.config.retryInterval * (item.retryCount + 1));
185
+ const success = await this.upload(item.events);
186
+ if (success) {
187
+ this.retryQueue.delete(key);
188
+ } else {
189
+ item.retryCount++;
190
+ }
191
+ }
192
+ }
193
+ /**
194
+ * 上传本地缓存的事件
195
+ */
196
+ async uploadCachedEvents() {
197
+ try {
198
+ const cachedEvents = await this.config.storageAdapter.getEvents();
199
+ if (cachedEvents.length > 0) {
200
+ const success = await this.uploadBatch(cachedEvents);
201
+ if (success) {
202
+ await this.config.storageAdapter.clearEvents();
203
+ }
204
+ }
205
+ } catch (error) {
206
+ console.error("Failed to upload cached events:", error);
207
+ }
208
+ }
209
+ /**
210
+ * 保存事件到本地存储
211
+ */
212
+ async saveToLocal(events) {
213
+ try {
214
+ const existingEvents = await this.config.storageAdapter.getEvents();
215
+ const allEvents = [...existingEvents, ...events];
216
+ await this.config.storageAdapter.saveEvents(allEvents);
217
+ } catch (error) {
218
+ console.error("Failed to save events to local storage:", error);
219
+ }
220
+ }
221
+ /**
222
+ * 添加到重试队列
223
+ */
224
+ async addToRetryQueue(events) {
225
+ const key = `${Date.now()}_${Math.random()}`;
226
+ this.retryQueue.set(key, {
227
+ events,
228
+ retryCount: 0
229
+ });
230
+ }
231
+ /**
232
+ * 分割成批次
233
+ */
234
+ splitIntoBatches(events, batchSize) {
235
+ const batches = [];
236
+ for (let i = 0; i < events.length; i += batchSize) {
237
+ batches.push(events.slice(i, i + batchSize));
238
+ }
239
+ return batches;
240
+ }
241
+ /**
242
+ * 延迟函数
243
+ */
244
+ delay(ms) {
245
+ return new Promise((resolve) => setTimeout(resolve, ms));
246
+ }
247
+ /**
248
+ * 是否正在上传
249
+ */
250
+ isUploading() {
251
+ return this.uploading;
252
+ }
253
+ /**
254
+ * 获取重试队列大小
255
+ */
256
+ getRetryQueueSize() {
257
+ return this.retryQueue.size;
258
+ }
259
+ /**
260
+ * 清空重试队列
261
+ */
262
+ clearRetryQueue() {
263
+ this.retryQueue.clear();
264
+ }
265
+ };
266
+
267
+ // src/analytics/core/Analytics.ts
268
+ var SDK_VERSION = "1.0.0";
269
+ var Analytics = class {
270
+ constructor(config, storageAdapter, networkAdapter, deviceAdapter) {
271
+ this.sessionId = "";
272
+ this.deviceId = "";
273
+ this.deviceInfo = null;
274
+ this.initialized = false;
275
+ this.batchTimer = null;
276
+ if (config.adapter) {
277
+ this.storageAdapter = config.adapter.storage;
278
+ this.networkAdapter = config.adapter.network;
279
+ this.deviceAdapter = config.adapter.device;
280
+ } else if (storageAdapter && networkAdapter && deviceAdapter) {
281
+ this.storageAdapter = storageAdapter;
282
+ this.networkAdapter = networkAdapter;
283
+ this.deviceAdapter = deviceAdapter;
284
+ } else {
285
+ throw new Error("Analytics initialization failed: adapter is required");
286
+ }
287
+ const serverUrl = config.serverUrl || config.endpoint || "/api/analytics/events";
288
+ const platform = config.platform || "web";
289
+ this.config = {
290
+ ...config,
291
+ appId: config.appId,
292
+ appVersion: config.appVersion || "1.0.0",
293
+ platform,
294
+ endpoint: serverUrl,
295
+ batchSize: config.batchSize ?? 10,
296
+ batchInterval: config.batchInterval ?? 5e3,
297
+ maxQueueSize: config.maxQueueSize ?? 100,
298
+ retryTimes: config.retryTimes ?? 3,
299
+ retryInterval: config.retryInterval ?? 1e3,
300
+ enableAutoPageView: config.enableAutoPageView ?? true,
301
+ enableAutoClick: config.enableAutoClick ?? false,
302
+ enableAutoError: config.enableAutoError ?? true,
303
+ enableAutoPerformance: config.enableAutoPerformance ?? true,
304
+ debug: config.debug ?? false,
305
+ ignoreUrls: config.ignoreUrls ?? [],
306
+ ignoreErrors: config.ignoreErrors ?? [],
307
+ beforeSend: config.beforeSend
308
+ };
309
+ this.eventQueue = new EventQueue(this.config.maxQueueSize);
310
+ this.uploader = new Uploader({
311
+ endpoint: this.config.endpoint,
312
+ batchSize: this.config.batchSize,
313
+ retryTimes: this.config.retryTimes,
314
+ retryInterval: this.config.retryInterval,
315
+ networkAdapter: this.networkAdapter,
316
+ storageAdapter: this.storageAdapter,
317
+ onSuccess: (events) => this.onUploadSuccess(events),
318
+ onError: (error, events) => this.onUploadError(error, events)
319
+ });
320
+ this.init().catch((error) => {
321
+ console.error("Failed to initialize Analytics:", error);
322
+ });
323
+ }
324
+ /**
325
+ * 初始化
326
+ */
327
+ async init() {
328
+ if (this.initialized) {
329
+ this.log("Analytics already initialized");
330
+ return;
331
+ }
332
+ try {
333
+ await this.initDeviceInfo();
334
+ await this.initSession();
335
+ await this.uploader.uploadCachedEvents();
336
+ this.startBatchTimer();
337
+ if (typeof window !== "undefined") {
338
+ window.addEventListener("analytics-debug-changed", ((e) => {
339
+ this.log(`Debug mode changed: ${e.detail.enabled ? "enabled" : "disabled"}`);
340
+ }));
341
+ }
342
+ this.initialized = true;
343
+ this.log("Analytics initialized successfully");
344
+ } catch (error) {
345
+ console.error("Failed to initialize analytics:", error);
346
+ throw error;
347
+ }
348
+ }
349
+ /**
350
+ * 追踪事件(简化版本,支持两种调用方式)
351
+ */
352
+ track(eventNameOrType, propertiesOrName, maybeProperties, priority = 1 /* NORMAL */) {
353
+ if (!this.initialized) {
354
+ console.warn("Analytics not initialized yet, queuing event...");
355
+ setTimeout(
356
+ () => this.track(eventNameOrType, propertiesOrName, maybeProperties, priority),
357
+ 100
358
+ );
359
+ return;
360
+ }
361
+ let eventType;
362
+ let eventName;
363
+ let properties;
364
+ if (typeof propertiesOrName === "string") {
365
+ eventType = eventNameOrType;
366
+ eventName = propertiesOrName;
367
+ properties = maybeProperties;
368
+ } else {
369
+ eventType = "custom" /* CUSTOM */;
370
+ eventName = eventNameOrType;
371
+ properties = propertiesOrName;
372
+ }
373
+ const event = this.createEvent(eventType, eventName, properties, priority);
374
+ const processedEvent = this.config.beforeSend?.(event) ?? event;
375
+ if (!processedEvent) {
376
+ this.log("Event filtered by beforeSend hook", event);
377
+ return;
378
+ }
379
+ this.eventQueue.enqueue(processedEvent);
380
+ this.log("Event tracked", processedEvent);
381
+ if (priority >= 2 /* HIGH */) {
382
+ this.flushHighPriority();
383
+ }
384
+ if (this.eventQueue.isFull()) {
385
+ this.flush();
386
+ }
387
+ }
388
+ /**
389
+ * 追踪页面浏览
390
+ */
391
+ trackPageView(pageUrl, pageTitle, properties) {
392
+ this.track(
393
+ "page_view" /* PAGE_VIEW */,
394
+ "page_view",
395
+ {
396
+ page_url: pageUrl,
397
+ page_title: pageTitle,
398
+ ...properties
399
+ },
400
+ 1 /* NORMAL */
401
+ );
402
+ }
403
+ /**
404
+ * 追踪点击事件
405
+ */
406
+ trackClick(elementInfo, properties) {
407
+ this.track(
408
+ "click" /* CLICK */,
409
+ "click",
410
+ {
411
+ ...elementInfo,
412
+ ...properties
413
+ },
414
+ 0 /* LOW */
415
+ );
416
+ }
417
+ /**
418
+ * 追踪错误
419
+ */
420
+ trackError(errorMessage, errorStack, errorType, properties) {
421
+ this.track(
422
+ "error" /* ERROR */,
423
+ "error",
424
+ {
425
+ error_message: errorMessage,
426
+ error_stack: errorStack,
427
+ error_type: errorType,
428
+ ...properties
429
+ },
430
+ 2 /* HIGH */
431
+ );
432
+ }
433
+ /**
434
+ * 追踪性能指标
435
+ */
436
+ trackPerformance(metricName, metricValue, metricUnit, properties) {
437
+ this.track(
438
+ "performance" /* PERFORMANCE */,
439
+ "performance",
440
+ {
441
+ metric_name: metricName,
442
+ metric_value: metricValue,
443
+ metric_unit: metricUnit,
444
+ ...properties
445
+ },
446
+ 0 /* LOW */
447
+ );
448
+ }
449
+ /**
450
+ * 追踪 API 调用
451
+ */
452
+ trackApiCall(apiUrl, apiMethod, apiStatus, duration, success, properties) {
453
+ this.track(
454
+ "api_call" /* API_CALL */,
455
+ "api_call",
456
+ {
457
+ api_url: apiUrl,
458
+ api_method: apiMethod,
459
+ api_status: apiStatus,
460
+ duration,
461
+ success,
462
+ ...properties
463
+ },
464
+ 1 /* NORMAL */
465
+ );
466
+ }
467
+ /**
468
+ * 设置用户ID
469
+ */
470
+ setUserId(userId) {
471
+ this.config.userId = userId;
472
+ this.log("User ID set", userId);
473
+ }
474
+ /**
475
+ * 设置用户信息(包括用户ID和其他属性)
476
+ */
477
+ setUser(userInfo) {
478
+ const { userId, ...otherProps } = userInfo;
479
+ this.config.userId = userId;
480
+ this.config.customProperties = {
481
+ ...this.config.customProperties,
482
+ ...otherProps
483
+ };
484
+ this.log("User info set", userInfo);
485
+ }
486
+ /**
487
+ * 设置自定义属性
488
+ */
489
+ setCustomProperties(properties) {
490
+ this.config.customProperties = {
491
+ ...this.config.customProperties,
492
+ ...properties
493
+ };
494
+ this.log("Custom properties set", properties);
495
+ }
496
+ /**
497
+ * 立即上传所有事件
498
+ */
499
+ async flush() {
500
+ if (this.eventQueue.isEmpty()) {
501
+ return;
502
+ }
503
+ const events = this.eventQueue.dequeueAll();
504
+ await this.uploader.upload(events);
505
+ }
506
+ /**
507
+ * 立即上传高优先级事件
508
+ */
509
+ async flushHighPriority() {
510
+ const highPriorityEvents = this.eventQueue.getHighPriorityEvents();
511
+ if (highPriorityEvents.length > 0) {
512
+ await this.uploader.upload(highPriorityEvents);
513
+ }
514
+ }
515
+ /**
516
+ * 销毁实例
517
+ */
518
+ async destroy() {
519
+ if (this.batchTimer) {
520
+ clearInterval(this.batchTimer);
521
+ this.batchTimer = null;
522
+ }
523
+ await this.flush();
524
+ this.initialized = false;
525
+ this.log("Analytics destroyed");
526
+ }
527
+ /**
528
+ * 创建事件对象
529
+ */
530
+ createEvent(eventType, eventName, properties, priority = 1 /* NORMAL */) {
531
+ let pageUrl;
532
+ let pageTitle;
533
+ let referrer;
534
+ if (typeof window !== "undefined") {
535
+ pageUrl = window.location.href;
536
+ pageTitle = document.title;
537
+ referrer = document.referrer || void 0;
538
+ }
539
+ return {
540
+ event_id: this.generateEventId(),
541
+ event_type: eventType,
542
+ event_name: eventName,
543
+ timestamp: Date.now(),
544
+ priority,
545
+ user_id: this.config.userId,
546
+ session_id: this.sessionId,
547
+ device_id: this.deviceId,
548
+ page_url: pageUrl,
549
+ page_title: pageTitle,
550
+ referrer,
551
+ properties: {
552
+ ...this.config.customProperties,
553
+ ...properties
554
+ },
555
+ platform: this.getPlatform(),
556
+ app_version: this.config.appVersion,
557
+ sdk_version: SDK_VERSION
558
+ };
559
+ }
560
+ /**
561
+ * 初始化设备信息
562
+ */
563
+ async initDeviceInfo() {
564
+ try {
565
+ let cachedDeviceInfo = await this.storageAdapter.getDeviceInfo();
566
+ if (!cachedDeviceInfo) {
567
+ this.deviceInfo = await this.deviceAdapter.getDeviceInfo();
568
+ this.deviceId = await this.deviceAdapter.generateDeviceId();
569
+ await this.storageAdapter.saveDeviceInfo(this.deviceInfo);
570
+ } else {
571
+ this.deviceInfo = cachedDeviceInfo;
572
+ this.deviceId = cachedDeviceInfo.device_id;
573
+ }
574
+ } catch (error) {
575
+ console.error("Failed to init device info:", error);
576
+ this.deviceId = `temp_${Date.now()}_${Math.random()}`;
577
+ }
578
+ }
579
+ /**
580
+ * 初始化会话
581
+ */
582
+ async initSession() {
583
+ try {
584
+ const cachedSessionId = await this.storageAdapter.getSessionId();
585
+ if (cachedSessionId) {
586
+ this.sessionId = cachedSessionId;
587
+ } else {
588
+ this.sessionId = this.generateSessionId();
589
+ await this.storageAdapter.saveSessionId(this.sessionId);
590
+ }
591
+ } catch (error) {
592
+ console.error("Failed to init session:", error);
593
+ this.sessionId = this.generateSessionId();
594
+ }
595
+ }
596
+ /**
597
+ * 启动批量上传定时器
598
+ */
599
+ startBatchTimer() {
600
+ this.batchTimer = setInterval(() => {
601
+ if (!this.eventQueue.isEmpty()) {
602
+ this.flush();
603
+ }
604
+ this.uploader.retryFailedUploads();
605
+ }, this.config.batchInterval);
606
+ }
607
+ /**
608
+ * 上传成功回调
609
+ */
610
+ onUploadSuccess(events) {
611
+ this.log("Events uploaded successfully", events);
612
+ }
613
+ /**
614
+ * 上传失败回调
615
+ */
616
+ onUploadError(error, events) {
617
+ this.log("Failed to upload events", { error, events });
618
+ }
619
+ /**
620
+ * 生成事件ID
621
+ */
622
+ generateEventId() {
623
+ return `${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
624
+ }
625
+ /**
626
+ * 生成会话ID
627
+ */
628
+ generateSessionId() {
629
+ return `session_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
630
+ }
631
+ /**
632
+ * 获取平台标识
633
+ */
634
+ getPlatform() {
635
+ return this.config.platform || "unknown";
636
+ }
637
+ /**
638
+ * 日志输出
639
+ */
640
+ log(message, data) {
641
+ const dynamicDebug = typeof window !== "undefined" ? localStorage.getItem("analytics-debug") === "true" : false;
642
+ if (this.config.debug || dynamicDebug) {
643
+ console.log(`[Analytics] ${message}`, data ?? "");
644
+ }
645
+ }
646
+ /**
647
+ * 获取队列状态
648
+ */
649
+ getQueueStatus() {
650
+ return {
651
+ size: this.eventQueue.size(),
652
+ isFull: this.eventQueue.isFull(),
653
+ isEmpty: this.eventQueue.isEmpty()
654
+ };
655
+ }
656
+ /**
657
+ * 获取初始化状态
658
+ */
659
+ isInitialized() {
660
+ return this.initialized;
661
+ }
662
+ };
663
+
664
+ // src/analytics/client/presets.ts
665
+ function createWebConfig(appId, options = {}) {
666
+ return {
667
+ appId,
668
+ appVersion: options.appVersion || "1.0.0",
669
+ endpoint: options.endpoint || "/api/analytics/events",
670
+ platform: "web",
671
+ enableAutoPageView: options.enableAutoPageView ?? true,
672
+ debug: options.debug ?? (typeof process !== "undefined" && process.env?.NODE_ENV === "development")
673
+ // adapter 需要由调用者提供
674
+ };
675
+ }
676
+ function createMobileConfig(appId, options = {}) {
677
+ return {
678
+ appId,
679
+ appVersion: options.appVersion || "1.0.0",
680
+ endpoint: options.endpoint || "/api/analytics/events",
681
+ platform: "mobile",
682
+ enableAutoPageView: false,
683
+ debug: options.debug ?? false
684
+ // adapter 需要由调用者提供
685
+ };
686
+ }
687
+ function createMiniappConfig(appId, options = {}) {
688
+ return {
689
+ appId,
690
+ appVersion: options.appVersion || "1.0.0",
691
+ endpoint: options.endpoint || "/api/analytics/events",
692
+ platform: "miniapp",
693
+ enableAutoPageView: true,
694
+ debug: options.debug ?? false
695
+ // adapter 需要由调用者提供
696
+ };
697
+ }
698
+ function createDesktopConfig(appId, options = {}) {
699
+ return {
700
+ appId,
701
+ appVersion: options.appVersion || "1.0.0",
702
+ endpoint: options.endpoint || "/api/analytics/events",
703
+ platform: "desktop",
704
+ enableAutoPageView: false,
705
+ debug: options.debug ?? false
706
+ // adapter 需要由调用者提供
707
+ };
708
+ }
709
+
710
+ // src/analytics/client/singleton.ts
711
+ var instances = /* @__PURE__ */ new Map();
712
+ function createAnalytics(instanceKey, config) {
713
+ if (!instances.has(instanceKey)) {
714
+ instances.set(instanceKey, new Analytics(config));
715
+ }
716
+ return instances.get(instanceKey);
717
+ }
718
+ function getAnalyticsInstance(instanceKey) {
719
+ return instances.get(instanceKey) || null;
720
+ }
721
+ function resetAnalytics(instanceKey) {
722
+ instances.delete(instanceKey);
723
+ }
724
+ function resetAllAnalytics() {
725
+ instances.clear();
726
+ }
727
+ function isAnalyticsInitialized(instanceKey) {
728
+ return instances.has(instanceKey);
729
+ }
730
+ function getAllInstanceKeys() {
731
+ return Array.from(instances.keys());
732
+ }
733
+
734
+ // src/analytics/utils/helpers.ts
735
+ function throttle(func, wait) {
736
+ let timeout = null;
737
+ let previous = 0;
738
+ return function(...args) {
739
+ const now = Date.now();
740
+ const remaining = wait - (now - previous);
741
+ if (remaining <= 0 || remaining > wait) {
742
+ if (timeout) {
743
+ clearTimeout(timeout);
744
+ timeout = null;
745
+ }
746
+ previous = now;
747
+ func.apply(this, args);
748
+ } else if (!timeout) {
749
+ timeout = setTimeout(() => {
750
+ previous = Date.now();
751
+ timeout = null;
752
+ func.apply(this, args);
753
+ }, remaining);
754
+ }
755
+ };
756
+ }
757
+ function debounce(func, wait) {
758
+ let timeout = null;
759
+ return function(...args) {
760
+ if (timeout) {
761
+ clearTimeout(timeout);
762
+ }
763
+ timeout = setTimeout(() => {
764
+ func.apply(this, args);
765
+ }, wait);
766
+ };
767
+ }
768
+ function formatEvent(event) {
769
+ return JSON.stringify(event, null, 2);
770
+ }
771
+ function validateEvent(event) {
772
+ if (!event.event_id || !event.event_type || !event.event_name) {
773
+ return false;
774
+ }
775
+ if (!event.timestamp || event.timestamp <= 0) {
776
+ return false;
777
+ }
778
+ if (!event.session_id || !event.device_id) {
779
+ return false;
780
+ }
781
+ return true;
782
+ }
783
+ function validateEvents(events) {
784
+ const valid = [];
785
+ const invalid = [];
786
+ events.forEach((event) => {
787
+ if (validateEvent(event)) {
788
+ valid.push(event);
789
+ } else {
790
+ invalid.push(event);
791
+ }
792
+ });
793
+ return { valid, invalid };
794
+ }
795
+ function getEventSize(event) {
796
+ return new Blob([JSON.stringify(event)]).size;
797
+ }
798
+ function getBatchSize(events) {
799
+ return events.reduce((total, event) => total + getEventSize(event), 0);
800
+ }
801
+ function sanitizeEvent(event, sensitiveKeys = ["password", "token", "secret", "key"]) {
802
+ const sanitized = { ...event };
803
+ if (sanitized.properties) {
804
+ const cleanProperties = { ...sanitized.properties };
805
+ sensitiveKeys.forEach((key) => {
806
+ if (key in cleanProperties) {
807
+ cleanProperties[key] = "***";
808
+ }
809
+ });
810
+ sanitized.properties = cleanProperties;
811
+ }
812
+ return sanitized;
813
+ }
814
+ function mergeEventProperties(baseProperties, ...additionalProperties) {
815
+ return Object.assign({}, baseProperties, ...additionalProperties);
816
+ }
817
+ function generateUniqueId(prefix = "") {
818
+ const timestamp = Date.now();
819
+ const random = Math.random().toString(36).substring(2, 15);
820
+ return prefix ? `${prefix}_${timestamp}_${random}` : `${timestamp}_${random}`;
821
+ }
822
+ function isMobile() {
823
+ if (typeof navigator === "undefined") return false;
824
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
825
+ }
826
+ function isDevelopment() {
827
+ return process.env.NODE_ENV === "development";
828
+ }
829
+ function formatTimestamp(timestamp, format = "datetime") {
830
+ const date = new Date(timestamp);
831
+ switch (format) {
832
+ case "date":
833
+ return date.toLocaleDateString();
834
+ case "time":
835
+ return date.toLocaleTimeString();
836
+ case "datetime":
837
+ default:
838
+ return date.toLocaleString();
839
+ }
840
+ }
841
+ function deepClone(obj) {
842
+ return JSON.parse(JSON.stringify(obj));
843
+ }
844
+ function safeStringify(obj, fallback = "{}") {
845
+ try {
846
+ return JSON.stringify(obj);
847
+ } catch (error) {
848
+ console.error("Failed to stringify object:", error);
849
+ return fallback;
850
+ }
851
+ }
852
+ function safeParse(json, fallback) {
853
+ try {
854
+ return JSON.parse(json);
855
+ } catch (error) {
856
+ console.error("Failed to parse JSON:", error);
857
+ return fallback;
858
+ }
859
+ }
860
+ function getPageDuration(startTime) {
861
+ return Date.now() - startTime;
862
+ }
863
+ function getCurrentPageUrl() {
864
+ if (typeof window !== "undefined") {
865
+ return window.location.href;
866
+ }
867
+ return "";
868
+ }
869
+ function getCurrentPageTitle() {
870
+ if (typeof document !== "undefined") {
871
+ return document.title;
872
+ }
873
+ return "";
874
+ }
875
+ function getReferrer() {
876
+ if (typeof document !== "undefined") {
877
+ return document.referrer;
878
+ }
879
+ return "";
880
+ }
881
+
882
+ // src/analytics/utils/decorators.ts
883
+ function Track(eventName, priority = 1 /* NORMAL */) {
884
+ return function(_target, propertyKey, descriptor) {
885
+ const originalMethod = descriptor.value;
886
+ descriptor.value = async function(...args) {
887
+ const analytics = getAnalyticsInstance2(this);
888
+ const finalEventName = eventName || propertyKey;
889
+ const startTime = Date.now();
890
+ try {
891
+ const result = await originalMethod.apply(this, args);
892
+ analytics?.track(
893
+ "custom" /* CUSTOM */,
894
+ finalEventName,
895
+ {
896
+ success: true,
897
+ duration: Date.now() - startTime,
898
+ args: JSON.stringify(args)
899
+ },
900
+ priority
901
+ );
902
+ return result;
903
+ } catch (error) {
904
+ analytics?.track(
905
+ "error" /* ERROR */,
906
+ `${finalEventName}_error`,
907
+ {
908
+ success: false,
909
+ duration: Date.now() - startTime,
910
+ error: error instanceof Error ? error.message : String(error)
911
+ },
912
+ 2 /* HIGH */
913
+ );
914
+ throw error;
915
+ }
916
+ };
917
+ return descriptor;
918
+ };
919
+ }
920
+ function TrackClick(eventName) {
921
+ return function(_target, propertyKey, descriptor) {
922
+ const originalMethod = descriptor.value;
923
+ descriptor.value = function(...args) {
924
+ const analytics = getAnalyticsInstance2(this);
925
+ const finalEventName = eventName || propertyKey;
926
+ analytics?.track(
927
+ "click" /* CLICK */,
928
+ finalEventName,
929
+ {
930
+ args: JSON.stringify(args)
931
+ },
932
+ 0 /* LOW */
933
+ );
934
+ return originalMethod.apply(this, args);
935
+ };
936
+ return descriptor;
937
+ };
938
+ }
939
+ function TrackPerformance(metricName) {
940
+ return function(_target, propertyKey, descriptor) {
941
+ const originalMethod = descriptor.value;
942
+ descriptor.value = async function(...args) {
943
+ const analytics = getAnalyticsInstance2(this);
944
+ const finalMetricName = metricName || propertyKey;
945
+ const startTime = performance.now();
946
+ try {
947
+ const result = await originalMethod.apply(this, args);
948
+ const duration = performance.now() - startTime;
949
+ analytics?.trackPerformance(finalMetricName, duration, "ms", {
950
+ success: true
951
+ });
952
+ return result;
953
+ } catch (error) {
954
+ const duration = performance.now() - startTime;
955
+ analytics?.trackPerformance(finalMetricName, duration, "ms", {
956
+ success: false,
957
+ error: error instanceof Error ? error.message : String(error)
958
+ });
959
+ throw error;
960
+ }
961
+ };
962
+ return descriptor;
963
+ };
964
+ }
965
+ function CatchError(_eventName) {
966
+ return function(_target, propertyKey, descriptor) {
967
+ const originalMethod = descriptor.value;
968
+ descriptor.value = async function(...args) {
969
+ try {
970
+ return await originalMethod.apply(this, args);
971
+ } catch (error) {
972
+ const analytics = getAnalyticsInstance2(this);
973
+ analytics?.trackError(
974
+ error instanceof Error ? error.message : String(error),
975
+ error instanceof Error ? error.stack : void 0,
976
+ error instanceof Error ? error.name : "Error",
977
+ {
978
+ method: propertyKey,
979
+ args: JSON.stringify(args)
980
+ }
981
+ );
982
+ throw error;
983
+ }
984
+ };
985
+ return descriptor;
986
+ };
987
+ }
988
+ function getAnalyticsInstance2(instance) {
989
+ if (instance.analytics) {
990
+ return instance.analytics;
991
+ }
992
+ if (instance._analytics) {
993
+ return instance._analytics;
994
+ }
995
+ if (globalThis.__analytics__) {
996
+ return globalThis.__analytics__;
997
+ }
998
+ return null;
999
+ }
1000
+ function setGlobalAnalytics(analytics) {
1001
+ globalThis.__analytics__ = analytics;
1002
+ }
1003
+ function getGlobalAnalytics() {
1004
+ return globalThis.__analytics__ || null;
1005
+ }
1006
+ function useAnalytics(analytics) {
1007
+ return analytics;
1008
+ }
1009
+ function usePageView(analytics, pageUrl, pageTitle) {
1010
+ react.useEffect(() => {
1011
+ if (!analytics) return;
1012
+ const url = pageUrl || (typeof window !== "undefined" ? window.location.href : "");
1013
+ const title = pageTitle || (typeof document !== "undefined" ? document.title : "");
1014
+ analytics.trackPageView(url, title);
1015
+ }, [analytics, pageUrl, pageTitle]);
1016
+ }
1017
+ function useTrackEvent(analytics) {
1018
+ return react.useCallback(
1019
+ (eventType, eventName, properties, priority) => {
1020
+ if (analytics) {
1021
+ analytics.track(eventType, eventName, properties, priority);
1022
+ }
1023
+ },
1024
+ [analytics]
1025
+ );
1026
+ }
1027
+ function useTrackClick(analytics) {
1028
+ return react.useCallback(
1029
+ (elementInfo) => {
1030
+ if (analytics) {
1031
+ analytics.trackClick(elementInfo);
1032
+ }
1033
+ },
1034
+ [analytics]
1035
+ );
1036
+ }
1037
+ function usePageDuration(analytics) {
1038
+ const startTimeRef = react.useRef(Date.now());
1039
+ react.useEffect(() => {
1040
+ startTimeRef.current = Date.now();
1041
+ return () => {
1042
+ if (analytics) {
1043
+ const duration = Date.now() - startTimeRef.current;
1044
+ analytics.track(
1045
+ "page_leave" /* PAGE_LEAVE */,
1046
+ "page_leave",
1047
+ {
1048
+ duration,
1049
+ page_url: typeof window !== "undefined" ? window.location.href : ""
1050
+ },
1051
+ 1 /* NORMAL */
1052
+ );
1053
+ }
1054
+ };
1055
+ }, [analytics]);
1056
+ }
1057
+ function usePerformanceTracking(analytics) {
1058
+ react.useEffect(() => {
1059
+ if (!analytics || typeof window === "undefined") return;
1060
+ if ("performance" in window && "timing" in window.performance) {
1061
+ const timing = window.performance.timing;
1062
+ const loadTime = timing.loadEventEnd - timing.navigationStart;
1063
+ const domReadyTime = timing.domContentLoadedEventEnd - timing.navigationStart;
1064
+ const firstPaintTime = timing.responseStart - timing.navigationStart;
1065
+ analytics.trackPerformance("page_load_time", loadTime, "ms");
1066
+ analytics.trackPerformance("dom_ready_time", domReadyTime, "ms");
1067
+ analytics.trackPerformance("first_paint_time", firstPaintTime, "ms");
1068
+ }
1069
+ if ("PerformanceObserver" in window) {
1070
+ const observer = new PerformanceObserver((list) => {
1071
+ list.getEntries().forEach((entry) => {
1072
+ if (entry.entryType === "resource") {
1073
+ analytics.trackPerformance("resource_load_time", entry.duration, "ms", {
1074
+ resource_name: entry.name
1075
+ });
1076
+ }
1077
+ });
1078
+ });
1079
+ observer.observe({ entryTypes: ["resource"] });
1080
+ return () => observer.disconnect();
1081
+ }
1082
+ return void 0;
1083
+ }, [analytics]);
1084
+ }
1085
+ function useErrorTracking(analytics) {
1086
+ react.useEffect(() => {
1087
+ if (!analytics || typeof window === "undefined") return;
1088
+ const handleError = (event) => {
1089
+ analytics.trackError(event.message, event.error?.stack, event.error?.name, {
1090
+ filename: event.filename,
1091
+ lineno: event.lineno,
1092
+ colno: event.colno
1093
+ });
1094
+ };
1095
+ const handleUnhandledRejection = (event) => {
1096
+ analytics.trackError("Unhandled Promise Rejection", void 0, "PromiseRejection", {
1097
+ reason: String(event.reason)
1098
+ });
1099
+ };
1100
+ window.addEventListener("error", handleError);
1101
+ window.addEventListener("unhandledrejection", handleUnhandledRejection);
1102
+ return () => {
1103
+ window.removeEventListener("error", handleError);
1104
+ window.removeEventListener("unhandledrejection", handleUnhandledRejection);
1105
+ };
1106
+ }, [analytics]);
1107
+ }
1108
+ function useAutoTracking(analytics, options = {}) {
1109
+ const {
1110
+ trackPageView = true,
1111
+ trackPageDuration = true,
1112
+ trackPerformance = true,
1113
+ trackErrors = true
1114
+ } = options;
1115
+ react.useEffect(() => {
1116
+ if (trackPageView && analytics) {
1117
+ const url = typeof window !== "undefined" ? window.location.href : "";
1118
+ const title = typeof document !== "undefined" ? document.title : "";
1119
+ analytics.trackPageView(url, title);
1120
+ }
1121
+ }, [analytics, trackPageView]);
1122
+ const startTimeRef = react.useRef(Date.now());
1123
+ react.useEffect(() => {
1124
+ if (!trackPageDuration || !analytics) return;
1125
+ startTimeRef.current = Date.now();
1126
+ return () => {
1127
+ const duration = Date.now() - startTimeRef.current;
1128
+ analytics.track(
1129
+ "page_leave" /* PAGE_LEAVE */,
1130
+ "page_leave",
1131
+ {
1132
+ duration,
1133
+ page_url: typeof window !== "undefined" ? window.location.href : ""
1134
+ },
1135
+ 1 /* NORMAL */
1136
+ );
1137
+ };
1138
+ }, [analytics, trackPageDuration]);
1139
+ react.useEffect(() => {
1140
+ if (!trackPerformance || !analytics || typeof window === "undefined") return;
1141
+ const handleLoad = () => {
1142
+ if ("performance" in window && "timing" in window.performance) {
1143
+ const timing = window.performance.timing;
1144
+ const loadTime = timing.loadEventEnd - timing.navigationStart;
1145
+ analytics.trackPerformance("page_load_time", loadTime, "ms");
1146
+ }
1147
+ };
1148
+ window.addEventListener("load", handleLoad);
1149
+ return () => window.removeEventListener("load", handleLoad);
1150
+ }, [analytics, trackPerformance]);
1151
+ react.useEffect(() => {
1152
+ if (!trackErrors || !analytics || typeof window === "undefined") return;
1153
+ const handleError = (event) => {
1154
+ analytics.trackError(event.message, event.error?.stack, event.error?.name);
1155
+ };
1156
+ window.addEventListener("error", handleError);
1157
+ return () => window.removeEventListener("error", handleError);
1158
+ }, [analytics, trackErrors]);
1159
+ }
1160
+
1161
+ // src/analytics/index.ts
1162
+ var ANALYTICS_VERSION = "1.0.0";
1163
+
1164
+ exports.ANALYTICS_VERSION = ANALYTICS_VERSION;
1165
+ exports.Analytics = Analytics;
1166
+ exports.CatchError = CatchError;
1167
+ exports.EventPriority = EventPriority;
1168
+ exports.EventQueue = EventQueue;
1169
+ exports.EventType = EventType;
1170
+ exports.Track = Track;
1171
+ exports.TrackClick = TrackClick;
1172
+ exports.TrackPerformance = TrackPerformance;
1173
+ exports.Uploader = Uploader;
1174
+ exports.createAnalytics = createAnalytics;
1175
+ exports.createDesktopConfig = createDesktopConfig;
1176
+ exports.createMiniappConfig = createMiniappConfig;
1177
+ exports.createMobileConfig = createMobileConfig;
1178
+ exports.createWebConfig = createWebConfig;
1179
+ exports.debounce = debounce;
1180
+ exports.deepClone = deepClone;
1181
+ exports.formatEvent = formatEvent;
1182
+ exports.formatTimestamp = formatTimestamp;
1183
+ exports.generateUniqueId = generateUniqueId;
1184
+ exports.getAllInstanceKeys = getAllInstanceKeys;
1185
+ exports.getAnalyticsInstance = getAnalyticsInstance;
1186
+ exports.getBatchSize = getBatchSize;
1187
+ exports.getCurrentPageTitle = getCurrentPageTitle;
1188
+ exports.getCurrentPageUrl = getCurrentPageUrl;
1189
+ exports.getEventSize = getEventSize;
1190
+ exports.getGlobalAnalytics = getGlobalAnalytics;
1191
+ exports.getPageDuration = getPageDuration;
1192
+ exports.getReferrer = getReferrer;
1193
+ exports.isAnalyticsInitialized = isAnalyticsInitialized;
1194
+ exports.isDevelopment = isDevelopment;
1195
+ exports.isMobile = isMobile;
1196
+ exports.mergeEventProperties = mergeEventProperties;
1197
+ exports.resetAllAnalytics = resetAllAnalytics;
1198
+ exports.resetAnalytics = resetAnalytics;
1199
+ exports.safeParse = safeParse;
1200
+ exports.safeStringify = safeStringify;
1201
+ exports.sanitizeEvent = sanitizeEvent;
1202
+ exports.setGlobalAnalytics = setGlobalAnalytics;
1203
+ exports.throttle = throttle;
1204
+ exports.useAnalytics = useAnalytics;
1205
+ exports.useAutoTracking = useAutoTracking;
1206
+ exports.useErrorTracking = useErrorTracking;
1207
+ exports.usePageDuration = usePageDuration;
1208
+ exports.usePageView = usePageView;
1209
+ exports.usePerformanceTracking = usePerformanceTracking;
1210
+ exports.useTrackClick = useTrackClick;
1211
+ exports.useTrackEvent = useTrackEvent;
1212
+ exports.validateEvent = validateEvent;
1213
+ exports.validateEvents = validateEvents;
1214
+ //# sourceMappingURL=index.js.map
1215
+ //# sourceMappingURL=index.js.map