sa2kit 1.0.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.
Files changed (218) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +298 -0
  3. package/dist/AliyunOSSProvider-7JLMJDXK.js +15 -0
  4. package/dist/AliyunOSSProvider-7JLMJDXK.js.map +1 -0
  5. package/dist/AliyunOSSProvider-GQMSDJGZ.mjs +6 -0
  6. package/dist/AliyunOSSProvider-GQMSDJGZ.mjs.map +1 -0
  7. package/dist/LocalStorageProvider-FVLLHBHO.mjs +6 -0
  8. package/dist/LocalStorageProvider-FVLLHBHO.mjs.map +1 -0
  9. package/dist/LocalStorageProvider-NBNHHWLY.js +15 -0
  10. package/dist/LocalStorageProvider-NBNHHWLY.js.map +1 -0
  11. package/dist/analytics/index.d.mts +1084 -0
  12. package/dist/analytics/index.d.ts +1084 -0
  13. package/dist/analytics/index.js +2595 -0
  14. package/dist/analytics/index.js.map +1 -0
  15. package/dist/analytics/index.mjs +2518 -0
  16. package/dist/analytics/index.mjs.map +1 -0
  17. package/dist/analytics/server/index.d.mts +499 -0
  18. package/dist/analytics/server/index.d.ts +499 -0
  19. package/dist/analytics/server/index.js +529 -0
  20. package/dist/analytics/server/index.js.map +1 -0
  21. package/dist/analytics/server/index.mjs +525 -0
  22. package/dist/analytics/server/index.mjs.map +1 -0
  23. package/dist/auth/client/index.d.mts +104 -0
  24. package/dist/auth/client/index.d.ts +104 -0
  25. package/dist/auth/client/index.js +21 -0
  26. package/dist/auth/client/index.js.map +1 -0
  27. package/dist/auth/client/index.mjs +4 -0
  28. package/dist/auth/client/index.mjs.map +1 -0
  29. package/dist/auth/components/index.d.mts +82 -0
  30. package/dist/auth/components/index.d.ts +82 -0
  31. package/dist/auth/components/index.js +93 -0
  32. package/dist/auth/components/index.js.map +1 -0
  33. package/dist/auth/components/index.mjs +86 -0
  34. package/dist/auth/components/index.mjs.map +1 -0
  35. package/dist/auth/hooks/index.d.mts +2 -0
  36. package/dist/auth/hooks/index.d.ts +2 -0
  37. package/dist/auth/hooks/index.js +17 -0
  38. package/dist/auth/hooks/index.js.map +1 -0
  39. package/dist/auth/hooks/index.mjs +4 -0
  40. package/dist/auth/hooks/index.mjs.map +1 -0
  41. package/dist/auth/index.d.mts +15 -0
  42. package/dist/auth/index.d.ts +15 -0
  43. package/dist/auth/index.js +110 -0
  44. package/dist/auth/index.js.map +1 -0
  45. package/dist/auth/index.mjs +9 -0
  46. package/dist/auth/index.mjs.map +1 -0
  47. package/dist/auth/middleware/index.d.mts +75 -0
  48. package/dist/auth/middleware/index.d.ts +75 -0
  49. package/dist/auth/middleware/index.js +15 -0
  50. package/dist/auth/middleware/index.js.map +1 -0
  51. package/dist/auth/middleware/index.mjs +6 -0
  52. package/dist/auth/middleware/index.mjs.map +1 -0
  53. package/dist/auth/routes/index.d.mts +163 -0
  54. package/dist/auth/routes/index.d.ts +163 -0
  55. package/dist/auth/routes/index.js +27 -0
  56. package/dist/auth/routes/index.js.map +1 -0
  57. package/dist/auth/routes/index.mjs +6 -0
  58. package/dist/auth/routes/index.mjs.map +1 -0
  59. package/dist/auth/schema/index.d.mts +789 -0
  60. package/dist/auth/schema/index.d.ts +789 -0
  61. package/dist/auth/schema/index.js +41 -0
  62. package/dist/auth/schema/index.js.map +1 -0
  63. package/dist/auth/schema/index.mjs +4 -0
  64. package/dist/auth/schema/index.mjs.map +1 -0
  65. package/dist/auth/services/index.d.mts +47 -0
  66. package/dist/auth/services/index.d.ts +47 -0
  67. package/dist/auth/services/index.js +34 -0
  68. package/dist/auth/services/index.js.map +1 -0
  69. package/dist/auth/services/index.mjs +5 -0
  70. package/dist/auth/services/index.mjs.map +1 -0
  71. package/dist/chunk-3RFBUDRA.js +507 -0
  72. package/dist/chunk-3RFBUDRA.js.map +1 -0
  73. package/dist/chunk-3XG5OHFD.mjs +37 -0
  74. package/dist/chunk-3XG5OHFD.mjs.map +1 -0
  75. package/dist/chunk-6BL3AZGD.js +285 -0
  76. package/dist/chunk-6BL3AZGD.js.map +1 -0
  77. package/dist/chunk-6FNUWAIV.js +394 -0
  78. package/dist/chunk-6FNUWAIV.js.map +1 -0
  79. package/dist/chunk-6PRFP5EG.js +171 -0
  80. package/dist/chunk-6PRFP5EG.js.map +1 -0
  81. package/dist/chunk-6VHWOPRR.mjs +90 -0
  82. package/dist/chunk-6VHWOPRR.mjs.map +1 -0
  83. package/dist/chunk-AIKEVVDR.mjs +122 -0
  84. package/dist/chunk-AIKEVVDR.mjs.map +1 -0
  85. package/dist/chunk-APY57REU.js +300 -0
  86. package/dist/chunk-APY57REU.js.map +1 -0
  87. package/dist/chunk-BJTO5JO5.mjs +10 -0
  88. package/dist/chunk-BJTO5JO5.mjs.map +1 -0
  89. package/dist/chunk-C64RY2OW.mjs +295 -0
  90. package/dist/chunk-C64RY2OW.mjs.map +1 -0
  91. package/dist/chunk-DGUM43GV.js +12 -0
  92. package/dist/chunk-DGUM43GV.js.map +1 -0
  93. package/dist/chunk-FV3FNHQY.js +92 -0
  94. package/dist/chunk-FV3FNHQY.js.map +1 -0
  95. package/dist/chunk-GSTLV3MB.mjs +316 -0
  96. package/dist/chunk-GSTLV3MB.mjs.map +1 -0
  97. package/dist/chunk-HEMA7SWK.mjs +212 -0
  98. package/dist/chunk-HEMA7SWK.mjs.map +1 -0
  99. package/dist/chunk-HWJ34NL6.js +43 -0
  100. package/dist/chunk-HWJ34NL6.js.map +1 -0
  101. package/dist/chunk-HXFFYNIF.mjs +385 -0
  102. package/dist/chunk-HXFFYNIF.mjs.map +1 -0
  103. package/dist/chunk-KGRQNEIR.mjs +183 -0
  104. package/dist/chunk-KGRQNEIR.mjs.map +1 -0
  105. package/dist/chunk-KH6RQ4J5.js +28 -0
  106. package/dist/chunk-KH6RQ4J5.js.map +1 -0
  107. package/dist/chunk-KQGP6BTS.mjs +165 -0
  108. package/dist/chunk-KQGP6BTS.mjs.map +1 -0
  109. package/dist/chunk-NMF4ANIC.js +365 -0
  110. package/dist/chunk-NMF4ANIC.js.map +1 -0
  111. package/dist/chunk-O26VCNS3.js +216 -0
  112. package/dist/chunk-O26VCNS3.js.map +1 -0
  113. package/dist/chunk-OLHGZXN3.mjs +86 -0
  114. package/dist/chunk-OLHGZXN3.mjs.map +1 -0
  115. package/dist/chunk-QU5OT4DF.js +88 -0
  116. package/dist/chunk-QU5OT4DF.js.map +1 -0
  117. package/dist/chunk-RCNNVNLT.mjs +356 -0
  118. package/dist/chunk-RCNNVNLT.mjs.map +1 -0
  119. package/dist/chunk-ROEYW4A7.js +186 -0
  120. package/dist/chunk-ROEYW4A7.js.map +1 -0
  121. package/dist/chunk-SVWQN2LR.js +131 -0
  122. package/dist/chunk-SVWQN2LR.js.map +1 -0
  123. package/dist/chunk-TKCYPDWU.js +338 -0
  124. package/dist/chunk-TKCYPDWU.js.map +1 -0
  125. package/dist/chunk-U2L6V7KD.mjs +273 -0
  126. package/dist/chunk-U2L6V7KD.mjs.map +1 -0
  127. package/dist/chunk-YVBU7QDJ.mjs +505 -0
  128. package/dist/chunk-YVBU7QDJ.mjs.map +1 -0
  129. package/dist/chunk-ZGVB35L2.mjs +25 -0
  130. package/dist/chunk-ZGVB35L2.mjs.map +1 -0
  131. package/dist/config/index.d.mts +64 -0
  132. package/dist/config/index.d.ts +64 -0
  133. package/dist/config/index.js +136 -0
  134. package/dist/config/index.js.map +1 -0
  135. package/dist/config/index.mjs +128 -0
  136. package/dist/config/index.mjs.map +1 -0
  137. package/dist/drizzle-auth-service-Bxlovhv8.d.ts +145 -0
  138. package/dist/drizzle-auth-service-DZY2F1sv.d.mts +145 -0
  139. package/dist/enums-Dume-V5Y.d.mts +16 -0
  140. package/dist/enums-Dume-V5Y.d.ts +16 -0
  141. package/dist/i18n/index.d.mts +416 -0
  142. package/dist/i18n/index.d.ts +416 -0
  143. package/dist/i18n/index.js +671 -0
  144. package/dist/i18n/index.js.map +1 -0
  145. package/dist/i18n/index.mjs +650 -0
  146. package/dist/i18n/index.mjs.map +1 -0
  147. package/dist/index-8VoHap_4.d.mts +105 -0
  148. package/dist/index-8VoHap_4.d.ts +105 -0
  149. package/dist/index.d.mts +4 -0
  150. package/dist/index.d.ts +4 -0
  151. package/dist/index.js +84 -0
  152. package/dist/index.js.map +1 -0
  153. package/dist/index.mjs +7 -0
  154. package/dist/index.mjs.map +1 -0
  155. package/dist/logger/index.d.mts +125 -0
  156. package/dist/logger/index.d.ts +125 -0
  157. package/dist/logger/index.js +29 -0
  158. package/dist/logger/index.js.map +1 -0
  159. package/dist/logger/index.mjs +4 -0
  160. package/dist/logger/index.mjs.map +1 -0
  161. package/dist/request/index.d.mts +51 -0
  162. package/dist/request/index.d.ts +51 -0
  163. package/dist/request/index.js +85 -0
  164. package/dist/request/index.js.map +1 -0
  165. package/dist/request/index.mjs +82 -0
  166. package/dist/request/index.mjs.map +1 -0
  167. package/dist/storage/index.d.mts +74 -0
  168. package/dist/storage/index.d.ts +74 -0
  169. package/dist/storage/index.js +46 -0
  170. package/dist/storage/index.js.map +1 -0
  171. package/dist/storage/index.mjs +5 -0
  172. package/dist/storage/index.mjs.map +1 -0
  173. package/dist/types-BINlP9MK.d.mts +286 -0
  174. package/dist/types-BINlP9MK.d.ts +286 -0
  175. package/dist/types-BaZccpvk.d.mts +48 -0
  176. package/dist/types-BaZccpvk.d.ts +48 -0
  177. package/dist/types-CbTsi9CZ.d.mts +31 -0
  178. package/dist/types-CbTsi9CZ.d.ts +31 -0
  179. package/dist/types-CoGG1rNV.d.mts +258 -0
  180. package/dist/types-CoGG1rNV.d.ts +258 -0
  181. package/dist/types-DAxQ1MeY.d.ts +70 -0
  182. package/dist/types-DT8LVCvE.d.mts +70 -0
  183. package/dist/types-DW9qar-w.d.mts +52 -0
  184. package/dist/types-DW9qar-w.d.ts +52 -0
  185. package/dist/universalExport/index.d.mts +235 -0
  186. package/dist/universalExport/index.d.ts +235 -0
  187. package/dist/universalExport/index.js +621 -0
  188. package/dist/universalExport/index.js.map +1 -0
  189. package/dist/universalExport/index.mjs +580 -0
  190. package/dist/universalExport/index.mjs.map +1 -0
  191. package/dist/universalExport/server/index.d.mts +429 -0
  192. package/dist/universalExport/server/index.d.ts +429 -0
  193. package/dist/universalExport/server/index.js +263 -0
  194. package/dist/universalExport/server/index.js.map +1 -0
  195. package/dist/universalExport/server/index.mjs +242 -0
  196. package/dist/universalExport/server/index.mjs.map +1 -0
  197. package/dist/universalFile/index.d.mts +310 -0
  198. package/dist/universalFile/index.d.ts +310 -0
  199. package/dist/universalFile/index.js +811 -0
  200. package/dist/universalFile/index.js.map +1 -0
  201. package/dist/universalFile/index.mjs +736 -0
  202. package/dist/universalFile/index.mjs.map +1 -0
  203. package/dist/universalFile/server/index.d.mts +2428 -0
  204. package/dist/universalFile/server/index.d.ts +2428 -0
  205. package/dist/universalFile/server/index.js +4578 -0
  206. package/dist/universalFile/server/index.js.map +1 -0
  207. package/dist/universalFile/server/index.mjs +4518 -0
  208. package/dist/universalFile/server/index.mjs.map +1 -0
  209. package/dist/useElectronStorage-Dj0rcorG.d.mts +65 -0
  210. package/dist/useElectronStorage-DwnNfIhl.d.ts +65 -0
  211. package/dist/utils/index.d.mts +188 -0
  212. package/dist/utils/index.d.ts +188 -0
  213. package/dist/utils/index.js +42 -0
  214. package/dist/utils/index.js.map +1 -0
  215. package/dist/utils/index.mjs +5 -0
  216. package/dist/utils/index.mjs.map +1 -0
  217. package/package.json +220 -0
  218. package/tailwind.animations.js +34 -0
@@ -0,0 +1,2518 @@
1
+ import { WebStorageAdapter } from '../chunk-OLHGZXN3.mjs';
2
+ import '../chunk-BJTO5JO5.mjs';
3
+ import React5, { useEffect, useCallback, useRef, useState } from '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
+ 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 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 useCallback(
1029
+ (elementInfo) => {
1030
+ if (analytics) {
1031
+ analytics.trackClick(elementInfo);
1032
+ }
1033
+ },
1034
+ [analytics]
1035
+ );
1036
+ }
1037
+ function usePageDuration(analytics) {
1038
+ const startTimeRef = useRef(Date.now());
1039
+ 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
+ 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
+ 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
+ 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 = useRef(Date.now());
1123
+ 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
+ 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
+ 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/adapters/web.ts
1162
+ var WebEventStorageAdapter = class {
1163
+ constructor() {
1164
+ this.storage = new WebStorageAdapter();
1165
+ this.EVENTS_KEY = "analytics:events";
1166
+ this.DEVICE_INFO_KEY = "analytics:device_info";
1167
+ this.SESSION_ID_KEY = "analytics:session_id";
1168
+ }
1169
+ async saveEvents(events) {
1170
+ try {
1171
+ await this.storage.setItem(this.EVENTS_KEY, JSON.stringify(events));
1172
+ } catch (error) {
1173
+ console.error("Failed to save events:", error);
1174
+ }
1175
+ }
1176
+ async getEvents() {
1177
+ try {
1178
+ const data = await this.storage.getItem(this.EVENTS_KEY);
1179
+ return data ? JSON.parse(data) : [];
1180
+ } catch (error) {
1181
+ console.error("Failed to get events:", error);
1182
+ return [];
1183
+ }
1184
+ }
1185
+ async clearEvents() {
1186
+ try {
1187
+ await this.storage.removeItem(this.EVENTS_KEY);
1188
+ } catch (error) {
1189
+ console.error("Failed to clear events:", error);
1190
+ }
1191
+ }
1192
+ async saveDeviceInfo(info) {
1193
+ try {
1194
+ await this.storage.setItem(this.DEVICE_INFO_KEY, JSON.stringify(info));
1195
+ } catch (error) {
1196
+ console.error("Failed to save device info:", error);
1197
+ }
1198
+ }
1199
+ async getDeviceInfo() {
1200
+ try {
1201
+ const data = await this.storage.getItem(this.DEVICE_INFO_KEY);
1202
+ return data ? JSON.parse(data) : null;
1203
+ } catch (error) {
1204
+ console.error("Failed to get device info:", error);
1205
+ return null;
1206
+ }
1207
+ }
1208
+ async saveSessionId(sessionId) {
1209
+ try {
1210
+ if (typeof window !== "undefined" && typeof sessionStorage !== "undefined") {
1211
+ sessionStorage.setItem(this.SESSION_ID_KEY, sessionId);
1212
+ }
1213
+ } catch (error) {
1214
+ console.error("Failed to save session ID:", error);
1215
+ }
1216
+ }
1217
+ async getSessionId() {
1218
+ try {
1219
+ if (typeof window !== "undefined" && typeof sessionStorage !== "undefined") {
1220
+ return sessionStorage.getItem(this.SESSION_ID_KEY);
1221
+ }
1222
+ return null;
1223
+ } catch (error) {
1224
+ console.error("Failed to get session ID:", error);
1225
+ return null;
1226
+ }
1227
+ }
1228
+ };
1229
+ var WebNetworkAdapter = class {
1230
+ async upload(url, events) {
1231
+ try {
1232
+ const response = await fetch(url, {
1233
+ method: "POST",
1234
+ headers: {
1235
+ "Content-Type": "application/json"
1236
+ },
1237
+ body: JSON.stringify({
1238
+ events,
1239
+ timestamp: Date.now()
1240
+ }),
1241
+ // 使用 keepalive 确保页面关闭时也能发送
1242
+ keepalive: true
1243
+ });
1244
+ if (response.ok) {
1245
+ const data = await response.json();
1246
+ return {
1247
+ success: true,
1248
+ message: data.message,
1249
+ code: response.status
1250
+ };
1251
+ } else {
1252
+ return {
1253
+ success: false,
1254
+ message: `HTTP ${response.status}`,
1255
+ code: response.status
1256
+ };
1257
+ }
1258
+ } catch (error) {
1259
+ return {
1260
+ success: false,
1261
+ message: error instanceof Error ? error.message : "Network error",
1262
+ code: 0
1263
+ };
1264
+ }
1265
+ }
1266
+ async isOnline() {
1267
+ if (typeof navigator !== "undefined" && "onLine" in navigator) {
1268
+ return navigator.onLine;
1269
+ }
1270
+ return true;
1271
+ }
1272
+ };
1273
+ var WebDeviceAdapter = class {
1274
+ async getDeviceInfo() {
1275
+ return {
1276
+ device_id: await this.generateDeviceId(),
1277
+ os_name: this.getOSName(),
1278
+ os_version: this.getOSVersion(),
1279
+ screen_width: typeof window !== "undefined" ? window.screen.width : 0,
1280
+ screen_height: typeof window !== "undefined" ? window.screen.height : 0,
1281
+ language: typeof navigator !== "undefined" ? navigator.language : "en",
1282
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
1283
+ device_model: this.getBrowserName(),
1284
+ device_brand: this.getBrowserVersion()
1285
+ };
1286
+ }
1287
+ async generateDeviceId() {
1288
+ if (typeof window !== "undefined") {
1289
+ let deviceId = localStorage.getItem("analytics:device_id");
1290
+ if (!deviceId) {
1291
+ deviceId = `web_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
1292
+ localStorage.setItem("analytics:device_id", deviceId);
1293
+ }
1294
+ return deviceId;
1295
+ }
1296
+ return `web_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
1297
+ }
1298
+ getOSName() {
1299
+ if (typeof navigator === "undefined") return "unknown";
1300
+ const userAgent = navigator.userAgent;
1301
+ if (userAgent.indexOf("Win") !== -1) return "Windows";
1302
+ if (userAgent.indexOf("Mac") !== -1) return "MacOS";
1303
+ if (userAgent.indexOf("Linux") !== -1) return "Linux";
1304
+ if (userAgent.indexOf("Android") !== -1) return "Android";
1305
+ if (userAgent.indexOf("iOS") !== -1) return "iOS";
1306
+ return "unknown";
1307
+ }
1308
+ getOSVersion() {
1309
+ if (typeof navigator === "undefined") return "unknown";
1310
+ const userAgent = navigator.userAgent;
1311
+ const match = userAgent.match(/\(([^)]+)\)/);
1312
+ return match && match[1] ? match[1] : "unknown";
1313
+ }
1314
+ getBrowserName() {
1315
+ if (typeof navigator === "undefined") return "unknown";
1316
+ const userAgent = navigator.userAgent;
1317
+ if (userAgent.indexOf("Chrome") !== -1) return "Chrome";
1318
+ if (userAgent.indexOf("Safari") !== -1) return "Safari";
1319
+ if (userAgent.indexOf("Firefox") !== -1) return "Firefox";
1320
+ if (userAgent.indexOf("Edge") !== -1) return "Edge";
1321
+ return "unknown";
1322
+ }
1323
+ getBrowserVersion() {
1324
+ if (typeof navigator === "undefined") return "unknown";
1325
+ const userAgent = navigator.userAgent;
1326
+ const match = userAgent.match(/(Chrome|Safari|Firefox|Edge)\/(\d+)/);
1327
+ return match && match[2] ? match[2] : "unknown";
1328
+ }
1329
+ };
1330
+ var webAdapter = {
1331
+ storage: new WebEventStorageAdapter(),
1332
+ network: new WebNetworkAdapter(),
1333
+ device: new WebDeviceAdapter()
1334
+ };
1335
+
1336
+ // src/analytics/adapters/mobile.ts
1337
+ var MobileEventStorageAdapter = class {
1338
+ // 需要注入具体的存储实现
1339
+ constructor(storage) {
1340
+ this.storage = storage;
1341
+ this.EVENTS_KEY = "@analytics:events";
1342
+ this.DEVICE_INFO_KEY = "@analytics:device_info";
1343
+ this.SESSION_ID_KEY = "@analytics:session_id";
1344
+ }
1345
+ async saveEvents(events) {
1346
+ try {
1347
+ await this.storage.setItem(this.EVENTS_KEY, JSON.stringify(events));
1348
+ } catch (error) {
1349
+ console.error("Failed to save events:", error);
1350
+ }
1351
+ }
1352
+ async getEvents() {
1353
+ try {
1354
+ const data = await this.storage.getItem(this.EVENTS_KEY);
1355
+ return data ? JSON.parse(data) : [];
1356
+ } catch (error) {
1357
+ console.error("Failed to get events:", error);
1358
+ return [];
1359
+ }
1360
+ }
1361
+ async clearEvents() {
1362
+ try {
1363
+ await this.storage.removeItem(this.EVENTS_KEY);
1364
+ } catch (error) {
1365
+ console.error("Failed to clear events:", error);
1366
+ }
1367
+ }
1368
+ async saveDeviceInfo(info) {
1369
+ try {
1370
+ await this.storage.setItem(this.DEVICE_INFO_KEY, JSON.stringify(info));
1371
+ } catch (error) {
1372
+ console.error("Failed to save device info:", error);
1373
+ }
1374
+ }
1375
+ async getDeviceInfo() {
1376
+ try {
1377
+ const data = await this.storage.getItem(this.DEVICE_INFO_KEY);
1378
+ return data ? JSON.parse(data) : null;
1379
+ } catch (error) {
1380
+ console.error("Failed to get device info:", error);
1381
+ return null;
1382
+ }
1383
+ }
1384
+ async saveSessionId(sessionId) {
1385
+ try {
1386
+ await this.storage.setItem(this.SESSION_ID_KEY, sessionId);
1387
+ } catch (error) {
1388
+ console.error("Failed to save session ID:", error);
1389
+ }
1390
+ }
1391
+ async getSessionId() {
1392
+ try {
1393
+ return await this.storage.getItem(this.SESSION_ID_KEY);
1394
+ } catch (error) {
1395
+ console.error("Failed to get session ID:", error);
1396
+ return null;
1397
+ }
1398
+ }
1399
+ };
1400
+ var MobileNetworkAdapter = class {
1401
+ // 需要注入网络检查函数
1402
+ constructor(netInfo) {
1403
+ this.netInfo = netInfo;
1404
+ }
1405
+ async upload(url, events) {
1406
+ try {
1407
+ const response = await fetch(url, {
1408
+ method: "POST",
1409
+ headers: {
1410
+ "Content-Type": "application/json"
1411
+ },
1412
+ body: JSON.stringify({
1413
+ events,
1414
+ timestamp: Date.now()
1415
+ })
1416
+ });
1417
+ if (response.ok) {
1418
+ const data = await response.json();
1419
+ return {
1420
+ success: true,
1421
+ message: data.message,
1422
+ code: response.status
1423
+ };
1424
+ } else {
1425
+ return {
1426
+ success: false,
1427
+ message: `HTTP ${response.status}`,
1428
+ code: response.status
1429
+ };
1430
+ }
1431
+ } catch (error) {
1432
+ return {
1433
+ success: false,
1434
+ message: error instanceof Error ? error.message : "Network error",
1435
+ code: 0
1436
+ };
1437
+ }
1438
+ }
1439
+ async isOnline() {
1440
+ try {
1441
+ if (this.netInfo) {
1442
+ const state = await this.netInfo.fetch();
1443
+ return state.isConnected ?? false;
1444
+ }
1445
+ return true;
1446
+ } catch (error) {
1447
+ return true;
1448
+ }
1449
+ }
1450
+ };
1451
+ var MobileDeviceAdapter = class {
1452
+ // 需要注入设备信息获取函数
1453
+ constructor(deviceInfoModule) {
1454
+ this.deviceInfoModule = deviceInfoModule;
1455
+ }
1456
+ async getDeviceInfo() {
1457
+ try {
1458
+ const info = this.deviceInfoModule;
1459
+ return {
1460
+ device_id: info ? await info.getUniqueId() : await this.generateDeviceId(),
1461
+ device_model: info ? await info.getModel() : "unknown",
1462
+ device_brand: info ? await info.getBrand() : "unknown",
1463
+ os_name: info ? await info.getSystemName() : "unknown",
1464
+ os_version: info ? await info.getSystemVersion() : "unknown",
1465
+ screen_width: typeof window !== "undefined" ? window.screen.width : 0,
1466
+ screen_height: typeof window !== "undefined" ? window.screen.height : 0,
1467
+ language: typeof navigator !== "undefined" ? navigator.language : "en",
1468
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
1469
+ };
1470
+ } catch (error) {
1471
+ console.error("Failed to get device info:", error);
1472
+ return this.getDefaultDeviceInfo();
1473
+ }
1474
+ }
1475
+ async generateDeviceId() {
1476
+ const timestamp = Date.now();
1477
+ const random = Math.random().toString(36).substring(2, 15);
1478
+ return `mobile_${timestamp}_${random}`;
1479
+ }
1480
+ getDefaultDeviceInfo() {
1481
+ return {
1482
+ device_id: `mobile_${Date.now()}`,
1483
+ os_name: "unknown",
1484
+ os_version: "unknown",
1485
+ screen_width: 0,
1486
+ screen_height: 0,
1487
+ language: "en",
1488
+ timezone: "UTC"
1489
+ };
1490
+ }
1491
+ };
1492
+ var MobileStorageAdapter = MobileEventStorageAdapter;
1493
+
1494
+ // src/analytics/adapters/miniapp.ts
1495
+ var MiniappEventStorageAdapter = class {
1496
+ // 需要注入 Taro 的存储 API
1497
+ constructor(storage) {
1498
+ this.storage = storage;
1499
+ this.EVENTS_KEY = "analytics_events";
1500
+ this.DEVICE_INFO_KEY = "analytics_device_info";
1501
+ this.SESSION_ID_KEY = "analytics_session_id";
1502
+ }
1503
+ async saveEvents(events) {
1504
+ try {
1505
+ if (this.storage) {
1506
+ this.storage.setStorageSync(this.EVENTS_KEY, events);
1507
+ }
1508
+ } catch (error) {
1509
+ console.error("Failed to save events:", error);
1510
+ }
1511
+ }
1512
+ async getEvents() {
1513
+ try {
1514
+ if (this.storage) {
1515
+ const data = this.storage.getStorageSync(this.EVENTS_KEY);
1516
+ return data || [];
1517
+ }
1518
+ return [];
1519
+ } catch (error) {
1520
+ console.error("Failed to get events:", error);
1521
+ return [];
1522
+ }
1523
+ }
1524
+ async clearEvents() {
1525
+ try {
1526
+ if (this.storage) {
1527
+ this.storage.removeStorageSync(this.EVENTS_KEY);
1528
+ }
1529
+ } catch (error) {
1530
+ console.error("Failed to clear events:", error);
1531
+ }
1532
+ }
1533
+ async saveDeviceInfo(info) {
1534
+ try {
1535
+ if (this.storage) {
1536
+ this.storage.setStorageSync(this.DEVICE_INFO_KEY, info);
1537
+ }
1538
+ } catch (error) {
1539
+ console.error("Failed to save device info:", error);
1540
+ }
1541
+ }
1542
+ async getDeviceInfo() {
1543
+ try {
1544
+ if (this.storage) {
1545
+ return this.storage.getStorageSync(this.DEVICE_INFO_KEY) || null;
1546
+ }
1547
+ return null;
1548
+ } catch (error) {
1549
+ console.error("Failed to get device info:", error);
1550
+ return null;
1551
+ }
1552
+ }
1553
+ async saveSessionId(sessionId) {
1554
+ try {
1555
+ if (this.storage) {
1556
+ this.storage.setStorageSync(this.SESSION_ID_KEY, sessionId);
1557
+ }
1558
+ } catch (error) {
1559
+ console.error("Failed to save session ID:", error);
1560
+ }
1561
+ }
1562
+ async getSessionId() {
1563
+ try {
1564
+ if (this.storage) {
1565
+ return this.storage.getStorageSync(this.SESSION_ID_KEY) || null;
1566
+ }
1567
+ return null;
1568
+ } catch (error) {
1569
+ console.error("Failed to get session ID:", error);
1570
+ return null;
1571
+ }
1572
+ }
1573
+ };
1574
+ var MiniappNetworkAdapter = class {
1575
+ // 需要注入 Taro 的网络 API
1576
+ constructor(request, netInfo) {
1577
+ this.request = request;
1578
+ this.netInfo = netInfo;
1579
+ }
1580
+ async upload(url, events) {
1581
+ try {
1582
+ if (!this.request) {
1583
+ throw new Error("Request module not provided");
1584
+ }
1585
+ const response = await this.request.request({
1586
+ url,
1587
+ method: "POST",
1588
+ data: {
1589
+ events,
1590
+ timestamp: Date.now()
1591
+ },
1592
+ header: {
1593
+ "Content-Type": "application/json"
1594
+ }
1595
+ });
1596
+ if (response.statusCode === 200) {
1597
+ return {
1598
+ success: true,
1599
+ message: response.data.message,
1600
+ code: response.statusCode
1601
+ };
1602
+ } else {
1603
+ return {
1604
+ success: false,
1605
+ message: `HTTP ${response.statusCode}`,
1606
+ code: response.statusCode
1607
+ };
1608
+ }
1609
+ } catch (error) {
1610
+ return {
1611
+ success: false,
1612
+ message: error instanceof Error ? error.message : "Network error",
1613
+ code: 0
1614
+ };
1615
+ }
1616
+ }
1617
+ async isOnline() {
1618
+ try {
1619
+ if (this.netInfo) {
1620
+ const { networkType } = await this.netInfo.getNetworkType();
1621
+ return networkType !== "none";
1622
+ }
1623
+ return true;
1624
+ } catch (error) {
1625
+ return true;
1626
+ }
1627
+ }
1628
+ };
1629
+ var MiniappDeviceAdapter = class {
1630
+ // 需要注入 Taro 的系统信息 API
1631
+ constructor(systemInfo) {
1632
+ this.systemInfo = systemInfo;
1633
+ }
1634
+ async getDeviceInfo() {
1635
+ try {
1636
+ if (this.systemInfo) {
1637
+ const info = this.systemInfo.getSystemInfoSync();
1638
+ return {
1639
+ device_id: await this.generateDeviceId(),
1640
+ device_model: info.model || "unknown",
1641
+ device_brand: info.brand || "unknown",
1642
+ os_name: info.platform || "unknown",
1643
+ os_version: info.system || "unknown",
1644
+ screen_width: info.screenWidth || 0,
1645
+ screen_height: info.screenHeight || 0,
1646
+ language: info.language || "zh_CN",
1647
+ timezone: "Asia/Shanghai",
1648
+ network_type: info.networkType
1649
+ };
1650
+ }
1651
+ return this.getDefaultDeviceInfo();
1652
+ } catch (error) {
1653
+ console.error("Failed to get device info:", error);
1654
+ return this.getDefaultDeviceInfo();
1655
+ }
1656
+ }
1657
+ async generateDeviceId() {
1658
+ const timestamp = Date.now();
1659
+ const random = Math.random().toString(36).substring(2, 15);
1660
+ return `miniapp_${timestamp}_${random}`;
1661
+ }
1662
+ getDefaultDeviceInfo() {
1663
+ return {
1664
+ device_id: `miniapp_${Date.now()}`,
1665
+ os_name: "wechat",
1666
+ os_version: "unknown",
1667
+ screen_width: 0,
1668
+ screen_height: 0,
1669
+ language: "zh_CN",
1670
+ timezone: "Asia/Shanghai"
1671
+ };
1672
+ }
1673
+ };
1674
+ var MiniappStorageAdapter = MiniappEventStorageAdapter;
1675
+
1676
+ // src/analytics/adapters/desktop.ts
1677
+ var DesktopEventStorageAdapter = class {
1678
+ constructor() {
1679
+ this.EVENTS_KEY = "analytics:events";
1680
+ this.DEVICE_INFO_KEY = "analytics:device_info";
1681
+ this.SESSION_ID_KEY = "analytics:session_id";
1682
+ }
1683
+ async saveEvents(events) {
1684
+ try {
1685
+ if (typeof window !== "undefined") {
1686
+ localStorage.setItem(this.EVENTS_KEY, JSON.stringify(events));
1687
+ }
1688
+ } catch (error) {
1689
+ console.error("Failed to save events:", error);
1690
+ }
1691
+ }
1692
+ async getEvents() {
1693
+ try {
1694
+ if (typeof window !== "undefined") {
1695
+ const data = localStorage.getItem(this.EVENTS_KEY);
1696
+ return data ? JSON.parse(data) : [];
1697
+ }
1698
+ return [];
1699
+ } catch (error) {
1700
+ console.error("Failed to get events:", error);
1701
+ return [];
1702
+ }
1703
+ }
1704
+ async clearEvents() {
1705
+ try {
1706
+ if (typeof window !== "undefined") {
1707
+ localStorage.removeItem(this.EVENTS_KEY);
1708
+ }
1709
+ } catch (error) {
1710
+ console.error("Failed to clear events:", error);
1711
+ }
1712
+ }
1713
+ async saveDeviceInfo(info) {
1714
+ try {
1715
+ if (typeof window !== "undefined") {
1716
+ localStorage.setItem(this.DEVICE_INFO_KEY, JSON.stringify(info));
1717
+ }
1718
+ } catch (error) {
1719
+ console.error("Failed to save device info:", error);
1720
+ }
1721
+ }
1722
+ async getDeviceInfo() {
1723
+ try {
1724
+ if (typeof window !== "undefined") {
1725
+ const data = localStorage.getItem(this.DEVICE_INFO_KEY);
1726
+ return data ? JSON.parse(data) : null;
1727
+ }
1728
+ return null;
1729
+ } catch (error) {
1730
+ console.error("Failed to get device info:", error);
1731
+ return null;
1732
+ }
1733
+ }
1734
+ async saveSessionId(sessionId) {
1735
+ try {
1736
+ if (typeof window !== "undefined") {
1737
+ localStorage.setItem(this.SESSION_ID_KEY, sessionId);
1738
+ }
1739
+ } catch (error) {
1740
+ console.error("Failed to save session ID:", error);
1741
+ }
1742
+ }
1743
+ async getSessionId() {
1744
+ try {
1745
+ if (typeof window !== "undefined") {
1746
+ return localStorage.getItem(this.SESSION_ID_KEY);
1747
+ }
1748
+ return null;
1749
+ } catch (error) {
1750
+ console.error("Failed to get session ID:", error);
1751
+ return null;
1752
+ }
1753
+ }
1754
+ };
1755
+ var DesktopNetworkAdapter = class {
1756
+ async upload(url, events) {
1757
+ try {
1758
+ const response = await fetch(url, {
1759
+ method: "POST",
1760
+ headers: {
1761
+ "Content-Type": "application/json"
1762
+ },
1763
+ body: JSON.stringify({
1764
+ events,
1765
+ timestamp: Date.now()
1766
+ })
1767
+ });
1768
+ if (response.ok) {
1769
+ const data = await response.json();
1770
+ return {
1771
+ success: true,
1772
+ message: data.message,
1773
+ code: response.status
1774
+ };
1775
+ } else {
1776
+ return {
1777
+ success: false,
1778
+ message: `HTTP ${response.status}`,
1779
+ code: response.status
1780
+ };
1781
+ }
1782
+ } catch (error) {
1783
+ return {
1784
+ success: false,
1785
+ message: error instanceof Error ? error.message : "Network error",
1786
+ code: 0
1787
+ };
1788
+ }
1789
+ }
1790
+ async isOnline() {
1791
+ if (typeof navigator !== "undefined" && "onLine" in navigator) {
1792
+ return navigator.onLine;
1793
+ }
1794
+ return true;
1795
+ }
1796
+ };
1797
+ var DesktopDeviceAdapter = class {
1798
+ async getDeviceInfo() {
1799
+ return {
1800
+ device_id: await this.generateDeviceId(),
1801
+ os_name: this.getOSName(),
1802
+ os_version: this.getOSVersion(),
1803
+ screen_width: typeof window !== "undefined" ? window.screen.width : 0,
1804
+ screen_height: typeof window !== "undefined" ? window.screen.height : 0,
1805
+ language: typeof navigator !== "undefined" ? navigator.language : "en",
1806
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
1807
+ device_model: "Desktop",
1808
+ device_brand: "Electron"
1809
+ };
1810
+ }
1811
+ async generateDeviceId() {
1812
+ if (typeof window !== "undefined") {
1813
+ let deviceId = localStorage.getItem("analytics:device_id");
1814
+ if (!deviceId) {
1815
+ deviceId = `desktop_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
1816
+ localStorage.setItem("analytics:device_id", deviceId);
1817
+ }
1818
+ return deviceId;
1819
+ }
1820
+ return `desktop_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
1821
+ }
1822
+ getOSName() {
1823
+ if (typeof navigator === "undefined") return "unknown";
1824
+ const platform = navigator.platform.toLowerCase();
1825
+ if (platform.indexOf("win") !== -1) return "Windows";
1826
+ if (platform.indexOf("mac") !== -1) return "MacOS";
1827
+ if (platform.indexOf("linux") !== -1) return "Linux";
1828
+ return "unknown";
1829
+ }
1830
+ getOSVersion() {
1831
+ if (typeof navigator === "undefined") return "unknown";
1832
+ return navigator.userAgent;
1833
+ }
1834
+ };
1835
+ var DesktopStorageAdapter = DesktopEventStorageAdapter;
1836
+ var StatCard = ({
1837
+ title,
1838
+ value,
1839
+ subtitle,
1840
+ icon,
1841
+ trend,
1842
+ className = ""
1843
+ }) => {
1844
+ return /* @__PURE__ */ React5.createElement(
1845
+ "div",
1846
+ {
1847
+ className: `bg-white rounded-lg shadow-sm p-6 border border-gray-200 hover:shadow-md transition-shadow ${className}`
1848
+ },
1849
+ /* @__PURE__ */ React5.createElement("div", { className: "flex items-start justify-between" }, /* @__PURE__ */ React5.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-gray-600 mb-1" }, title), /* @__PURE__ */ React5.createElement("p", { className: "text-3xl font-bold text-gray-900" }, value.toLocaleString()), subtitle && /* @__PURE__ */ React5.createElement("p", { className: "text-sm text-gray-500 mt-2" }, subtitle), trend && /* @__PURE__ */ React5.createElement("div", { className: "flex items-center mt-2" }, /* @__PURE__ */ React5.createElement(
1850
+ "span",
1851
+ {
1852
+ className: `text-sm font-medium ${trend.isPositive ? "text-green-600" : "text-red-600"}`
1853
+ },
1854
+ trend.isPositive ? "\u2191" : "\u2193",
1855
+ " ",
1856
+ Math.abs(trend.value),
1857
+ "%"
1858
+ ), /* @__PURE__ */ React5.createElement("span", { className: "text-sm text-gray-500 ml-2" }, "vs \u4E0A\u5468"))), icon && /* @__PURE__ */ React5.createElement("div", { className: "flex-shrink-0 bg-gradient-to-br from-purple-500 to-indigo-600 rounded-lg p-3" }, /* @__PURE__ */ React5.createElement("div", { className: "text-white text-2xl" }, icon)))
1859
+ );
1860
+ };
1861
+ var EventList = ({
1862
+ events,
1863
+ loading = false,
1864
+ onEventClick,
1865
+ className = ""
1866
+ }) => {
1867
+ if (loading) {
1868
+ return /* @__PURE__ */ React5.createElement("div", { className: `bg-white rounded-lg shadow-sm p-6 ${className}` }, /* @__PURE__ */ React5.createElement("div", { className: "animate-pulse space-y-4" }, [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ React5.createElement("div", { key: i, className: "h-16 bg-gray-200 rounded" }))));
1869
+ }
1870
+ if (events.length === 0) {
1871
+ return /* @__PURE__ */ React5.createElement("div", { className: `bg-white rounded-lg shadow-sm p-12 text-center ${className}` }, /* @__PURE__ */ React5.createElement("div", { className: "text-gray-400 text-5xl mb-4" }, "\u{1F4CA}"), /* @__PURE__ */ React5.createElement("p", { className: "text-gray-600 text-lg font-medium" }, "\u6682\u65E0\u6570\u636E"), /* @__PURE__ */ React5.createElement("p", { className: "text-gray-500 text-sm mt-2" }, "\u5C1D\u8BD5\u8C03\u6574\u7B5B\u9009\u6761\u4EF6"));
1872
+ }
1873
+ const getPlatformColor = (platform) => {
1874
+ const colors = {
1875
+ web: "bg-blue-100 text-blue-800",
1876
+ mobile: "bg-green-100 text-green-800",
1877
+ miniapp: "bg-purple-100 text-purple-800",
1878
+ desktop: "bg-gray-100 text-gray-800"
1879
+ };
1880
+ return colors[platform] || "bg-gray-100 text-gray-800";
1881
+ };
1882
+ const getEventTypeColor = (eventType) => {
1883
+ const colors = {
1884
+ page_view: "bg-indigo-100 text-indigo-800",
1885
+ click: "bg-amber-100 text-amber-800",
1886
+ error: "bg-red-100 text-red-800",
1887
+ performance: "bg-cyan-100 text-cyan-800",
1888
+ api_call: "bg-emerald-100 text-emerald-800"
1889
+ };
1890
+ return colors[eventType] || "bg-gray-100 text-gray-800";
1891
+ };
1892
+ const formatTimestamp2 = (timestamp) => {
1893
+ const date = new Date(timestamp);
1894
+ const now = /* @__PURE__ */ new Date();
1895
+ const diff = now.getTime() - date.getTime();
1896
+ const minutes = Math.floor(diff / 6e4);
1897
+ const hours = Math.floor(minutes / 60);
1898
+ const days = Math.floor(hours / 24);
1899
+ if (minutes < 1) return "\u521A\u521A";
1900
+ if (minutes < 60) return `${minutes}\u5206\u949F\u524D`;
1901
+ if (hours < 24) return `${hours}\u5C0F\u65F6\u524D`;
1902
+ if (days < 7) return `${days}\u5929\u524D`;
1903
+ return date.toLocaleDateString("zh-CN");
1904
+ };
1905
+ return /* @__PURE__ */ React5.createElement("div", { className: `bg-white rounded-lg shadow-sm overflow-hidden ${className}` }, /* @__PURE__ */ React5.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React5.createElement("table", { className: "min-w-full divide-y divide-gray-200" }, /* @__PURE__ */ React5.createElement("thead", { className: "bg-gray-50" }, /* @__PURE__ */ React5.createElement("tr", null, /* @__PURE__ */ React5.createElement("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" }, "\u4E8B\u4EF6"), /* @__PURE__ */ React5.createElement("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" }, "\u5E73\u53F0"), /* @__PURE__ */ React5.createElement("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" }, "\u9875\u9762"), /* @__PURE__ */ React5.createElement("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" }, "\u7528\u6237"), /* @__PURE__ */ React5.createElement("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" }, "\u65F6\u95F4"), /* @__PURE__ */ React5.createElement("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider" }, "\u64CD\u4F5C"))), /* @__PURE__ */ React5.createElement("tbody", { className: "bg-white divide-y divide-gray-200" }, events.map((event) => /* @__PURE__ */ React5.createElement(
1906
+ "tr",
1907
+ {
1908
+ key: event.id,
1909
+ className: "hover:bg-gray-50 cursor-pointer transition-colors",
1910
+ onClick: () => onEventClick?.(event)
1911
+ },
1912
+ /* @__PURE__ */ React5.createElement("td", { className: "px-6 py-4 whitespace-nowrap" }, /* @__PURE__ */ React5.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React5.createElement(
1913
+ "span",
1914
+ {
1915
+ className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getEventTypeColor(
1916
+ event.eventType
1917
+ )} w-fit mb-1`
1918
+ },
1919
+ event.eventType
1920
+ ), /* @__PURE__ */ React5.createElement("span", { className: "text-sm text-gray-900" }, event.eventName))),
1921
+ /* @__PURE__ */ React5.createElement("td", { className: "px-6 py-4 whitespace-nowrap" }, /* @__PURE__ */ React5.createElement(
1922
+ "span",
1923
+ {
1924
+ className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getPlatformColor(
1925
+ event.platform
1926
+ )}`
1927
+ },
1928
+ event.platform
1929
+ )),
1930
+ /* @__PURE__ */ React5.createElement("td", { className: "px-6 py-4" }, /* @__PURE__ */ React5.createElement("div", { className: "text-sm text-gray-900 max-w-xs truncate" }, event.pageUrl || "-"), event.pageTitle && /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-gray-500 max-w-xs truncate" }, event.pageTitle)),
1931
+ /* @__PURE__ */ React5.createElement("td", { className: "px-6 py-4 whitespace-nowrap" }, /* @__PURE__ */ React5.createElement("div", { className: "text-sm text-gray-900" }, event.userId || "\u6E38\u5BA2"), /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-gray-500" }, event.deviceId.slice(0, 8), "...")),
1932
+ /* @__PURE__ */ React5.createElement("td", { className: "px-6 py-4 whitespace-nowrap" }, /* @__PURE__ */ React5.createElement("div", { className: "text-sm text-gray-900" }, formatTimestamp2(event.timestamp)), /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-gray-500" }, new Date(event.timestamp).toLocaleTimeString("zh-CN"))),
1933
+ /* @__PURE__ */ React5.createElement("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm font-medium" }, /* @__PURE__ */ React5.createElement(
1934
+ "button",
1935
+ {
1936
+ onClick: (e) => {
1937
+ e.stopPropagation();
1938
+ onEventClick?.(event);
1939
+ },
1940
+ className: "text-indigo-600 hover:text-indigo-900"
1941
+ },
1942
+ "\u8BE6\u60C5"
1943
+ ))
1944
+ ))))));
1945
+ };
1946
+ var FilterPanel = ({
1947
+ onFilterChange,
1948
+ initialFilters = {},
1949
+ className = ""
1950
+ }) => {
1951
+ const [filters, setFilters] = useState(initialFilters);
1952
+ const handleDatePreset = (preset) => {
1953
+ const now = /* @__PURE__ */ new Date();
1954
+ let startDate = /* @__PURE__ */ new Date();
1955
+ switch (preset) {
1956
+ case "today":
1957
+ startDate.setHours(0, 0, 0, 0);
1958
+ break;
1959
+ case "yesterday":
1960
+ startDate.setDate(now.getDate() - 1);
1961
+ startDate.setHours(0, 0, 0, 0);
1962
+ now.setDate(now.getDate() - 1);
1963
+ now.setHours(23, 59, 59, 999);
1964
+ break;
1965
+ case "week":
1966
+ startDate.setDate(now.getDate() - 7);
1967
+ break;
1968
+ case "month":
1969
+ startDate.setMonth(now.getMonth() - 1);
1970
+ break;
1971
+ default:
1972
+ return;
1973
+ }
1974
+ const newFilters = {
1975
+ ...filters,
1976
+ dateRange: {
1977
+ startDate: startDate.toISOString(),
1978
+ endDate: now.toISOString()
1979
+ }
1980
+ };
1981
+ setFilters(newFilters);
1982
+ onFilterChange(newFilters);
1983
+ };
1984
+ const handleFilterChange = (key, value) => {
1985
+ const newFilters = {
1986
+ ...filters,
1987
+ [key]: value || void 0
1988
+ };
1989
+ setFilters(newFilters);
1990
+ onFilterChange(newFilters);
1991
+ };
1992
+ const handleReset = () => {
1993
+ const emptyFilters = {};
1994
+ setFilters(emptyFilters);
1995
+ onFilterChange(emptyFilters);
1996
+ };
1997
+ return /* @__PURE__ */ React5.createElement("div", { className: `bg-white rounded-xl shadow-sm border border-gray-200 ${className}` }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center justify-between mb-4 p-4 bg-gradient-to-r from-gray-50 to-gray-100 -m-6 mb-4 rounded-t-xl border-b border-gray-200" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React5.createElement(
1998
+ "svg",
1999
+ {
2000
+ className: "w-5 h-5 text-indigo-600",
2001
+ fill: "none",
2002
+ stroke: "currentColor",
2003
+ viewBox: "0 0 24 24"
2004
+ },
2005
+ /* @__PURE__ */ React5.createElement(
2006
+ "path",
2007
+ {
2008
+ strokeLinecap: "round",
2009
+ strokeLinejoin: "round",
2010
+ strokeWidth: 2,
2011
+ d: "M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
2012
+ }
2013
+ )
2014
+ ), /* @__PURE__ */ React5.createElement("h3", { className: "text-sm font-semibold text-gray-900" }, "\u7B5B\u9009\u6761\u4EF6")), /* @__PURE__ */ React5.createElement(
2015
+ "button",
2016
+ {
2017
+ onClick: handleReset,
2018
+ className: "text-xs px-3 py-1 text-gray-600 hover:text-indigo-600 hover:bg-indigo-50 rounded-lg transition-colors"
2019
+ },
2020
+ "\u91CD\u7F6E"
2021
+ )), /* @__PURE__ */ React5.createElement("div", { className: "p-4 space-y-4" }, /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("label", { className: "block text-xs font-semibold text-gray-700 mb-2" }, "\u65F6\u95F4\u8303\u56F4"), /* @__PURE__ */ React5.createElement("div", { className: "grid grid-cols-2 gap-2" }, [
2022
+ { label: "\u4ECA\u5929", value: "today" },
2023
+ { label: "\u6628\u5929", value: "yesterday" },
2024
+ { label: "7\u5929", value: "week" },
2025
+ { label: "30\u5929", value: "month" }
2026
+ ].map((preset) => /* @__PURE__ */ React5.createElement(
2027
+ "button",
2028
+ {
2029
+ key: preset.value,
2030
+ onClick: () => handleDatePreset(preset.value),
2031
+ className: "px-3 py-1.5 text-xs font-medium border border-gray-300 rounded-lg hover:bg-indigo-50 hover:border-indigo-500 hover:text-indigo-700 transition-colors"
2032
+ },
2033
+ preset.label
2034
+ )))), /* @__PURE__ */ React5.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("label", { className: "block text-xs font-medium text-gray-700 mb-1.5" }, "\u5F00\u59CB\u65F6\u95F4"), /* @__PURE__ */ React5.createElement(
2035
+ "input",
2036
+ {
2037
+ type: "datetime-local",
2038
+ value: filters.dateRange?.startDate ? new Date(filters.dateRange.startDate).toISOString().slice(0, 16) : "",
2039
+ onChange: (e) => handleFilterChange("dateRange", {
2040
+ ...filters.dateRange,
2041
+ startDate: e.target.value ? new Date(e.target.value).toISOString() : void 0
2042
+ }),
2043
+ className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
2044
+ }
2045
+ )), /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("label", { className: "block text-xs font-medium text-gray-700 mb-1.5" }, "\u7ED3\u675F\u65F6\u95F4"), /* @__PURE__ */ React5.createElement(
2046
+ "input",
2047
+ {
2048
+ type: "datetime-local",
2049
+ value: filters.dateRange?.endDate ? new Date(filters.dateRange.endDate).toISOString().slice(0, 16) : "",
2050
+ onChange: (e) => handleFilterChange("dateRange", {
2051
+ ...filters.dateRange,
2052
+ endDate: e.target.value ? new Date(e.target.value).toISOString() : void 0
2053
+ }),
2054
+ className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
2055
+ }
2056
+ ))), /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("label", { className: "block text-xs font-semibold text-gray-700 mb-2" }, "\u4E8B\u4EF6\u7C7B\u578B"), /* @__PURE__ */ React5.createElement(
2057
+ "select",
2058
+ {
2059
+ value: filters.eventType || "",
2060
+ onChange: (e) => handleFilterChange("eventType", e.target.value),
2061
+ className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
2062
+ },
2063
+ /* @__PURE__ */ React5.createElement("option", { value: "" }, "\u5168\u90E8"),
2064
+ /* @__PURE__ */ React5.createElement("option", { value: "page_view" }, "\u9875\u9762\u6D4F\u89C8"),
2065
+ /* @__PURE__ */ React5.createElement("option", { value: "click" }, "\u70B9\u51FB"),
2066
+ /* @__PURE__ */ React5.createElement("option", { value: "search" }, "\u641C\u7D22"),
2067
+ /* @__PURE__ */ React5.createElement("option", { value: "login" }, "\u767B\u5F55"),
2068
+ /* @__PURE__ */ React5.createElement("option", { value: "logout" }, "\u767B\u51FA"),
2069
+ /* @__PURE__ */ React5.createElement("option", { value: "error" }, "\u9519\u8BEF"),
2070
+ /* @__PURE__ */ React5.createElement("option", { value: "performance" }, "\u6027\u80FD"),
2071
+ /* @__PURE__ */ React5.createElement("option", { value: "api_call" }, "API \u8C03\u7528"),
2072
+ /* @__PURE__ */ React5.createElement("option", { value: "custom" }, "\u81EA\u5B9A\u4E49")
2073
+ )), /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("label", { className: "block text-xs font-semibold text-gray-700 mb-2" }, "\u5E73\u53F0"), /* @__PURE__ */ React5.createElement(
2074
+ "select",
2075
+ {
2076
+ value: filters.platform || "",
2077
+ onChange: (e) => handleFilterChange("platform", e.target.value),
2078
+ className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
2079
+ },
2080
+ /* @__PURE__ */ React5.createElement("option", { value: "" }, "\u5168\u90E8"),
2081
+ /* @__PURE__ */ React5.createElement("option", { value: "web" }, "Web"),
2082
+ /* @__PURE__ */ React5.createElement("option", { value: "mobile" }, "Mobile"),
2083
+ /* @__PURE__ */ React5.createElement("option", { value: "miniapp" }, "\u5C0F\u7A0B\u5E8F"),
2084
+ /* @__PURE__ */ React5.createElement("option", { value: "desktop" }, "Desktop")
2085
+ )), /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("label", { className: "block text-xs font-semibold text-gray-700 mb-2" }, "\u7528\u6237ID"), /* @__PURE__ */ React5.createElement(
2086
+ "input",
2087
+ {
2088
+ type: "text",
2089
+ value: filters.userId || "",
2090
+ onChange: (e) => handleFilterChange("userId", e.target.value),
2091
+ placeholder: "\u8F93\u5165\u7528\u6237ID",
2092
+ className: "w-full px-2 py-1.5 text-xs border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 placeholder:text-gray-400"
2093
+ }
2094
+ ))), /* @__PURE__ */ React5.createElement("div", { className: "px-4 py-3 bg-gray-50 -mx-6 -mb-6 mt-4 rounded-b-xl border-t border-gray-200" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React5.createElement(
2095
+ "div",
2096
+ {
2097
+ className: `w-2 h-2 rounded-full ${Object.keys(filters).length > 0 ? "bg-green-500" : "bg-gray-300"}`
2098
+ }
2099
+ ), /* @__PURE__ */ React5.createElement("p", { className: "text-xs text-gray-600" }, Object.keys(filters).length === 0 ? "\u672A\u5E94\u7528\u7B5B\u9009" : `${Object.keys(filters).length} \u4E2A\u7B5B\u9009`))));
2100
+ };
2101
+ var PieChart = ({ data, title, className = "" }) => {
2102
+ const total = data.reduce((sum, item) => sum + item.value, 0);
2103
+ const colors = [
2104
+ "bg-blue-500",
2105
+ "bg-green-500",
2106
+ "bg-purple-500",
2107
+ "bg-yellow-500",
2108
+ "bg-pink-500",
2109
+ "bg-indigo-500",
2110
+ "bg-red-500",
2111
+ "bg-cyan-500"
2112
+ ];
2113
+ return /* @__PURE__ */ React5.createElement("div", { className: `bg-white rounded-lg shadow-sm p-6 ${className}` }, title && /* @__PURE__ */ React5.createElement("h3", { className: "text-lg font-semibold text-gray-900 mb-4" }, title), /* @__PURE__ */ React5.createElement("div", { className: "space-y-3" }, data.map((item, index) => {
2114
+ const percentage = total > 0 ? (item.value / total * 100).toFixed(1) : 0;
2115
+ return /* @__PURE__ */ React5.createElement("div", { key: index }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center justify-between text-sm mb-1" }, /* @__PURE__ */ React5.createElement("span", { className: "text-gray-700 font-medium" }, item.name), /* @__PURE__ */ React5.createElement("span", { className: "text-gray-600" }, item.value.toLocaleString(), " (", percentage, "%)")), /* @__PURE__ */ React5.createElement("div", { className: "w-full bg-gray-200 rounded-full h-2" }, /* @__PURE__ */ React5.createElement(
2116
+ "div",
2117
+ {
2118
+ className: `h-2 rounded-full ${colors[index % colors.length]}`,
2119
+ style: { width: `${percentage}%` }
2120
+ }
2121
+ )));
2122
+ })));
2123
+ };
2124
+ var BarChart = ({ data, title, className = "" }) => {
2125
+ const maxValue = Math.max(...data.map((item) => item.value), 1);
2126
+ return /* @__PURE__ */ React5.createElement("div", { className: `bg-white rounded-lg shadow-sm p-6 ${className}` }, title && /* @__PURE__ */ React5.createElement("h3", { className: "text-lg font-semibold text-gray-900 mb-4" }, title), /* @__PURE__ */ React5.createElement("div", { className: "space-y-4" }, data.map((item, index) => {
2127
+ const percentage = item.value / maxValue * 100;
2128
+ return /* @__PURE__ */ React5.createElement("div", { key: index, className: "flex items-center gap-4" }, /* @__PURE__ */ React5.createElement("div", { className: "w-32 text-sm text-gray-700 font-medium truncate" }, item.name), /* @__PURE__ */ React5.createElement("div", { className: "flex-1 flex items-center gap-2" }, /* @__PURE__ */ React5.createElement("div", { className: "flex-1 bg-gray-200 rounded-full h-8 relative overflow-hidden" }, /* @__PURE__ */ React5.createElement(
2129
+ "div",
2130
+ {
2131
+ className: "h-full bg-gradient-to-r from-indigo-500 to-purple-600 flex items-center justify-end px-3",
2132
+ style: { width: `${percentage}%`, minWidth: "40px" }
2133
+ },
2134
+ /* @__PURE__ */ React5.createElement("span", { className: "text-xs text-white font-semibold" }, item.value.toLocaleString())
2135
+ ))));
2136
+ })));
2137
+ };
2138
+ var AnalyticsDashboard = ({
2139
+ apiBaseUrl = "/api/analytics",
2140
+ className = ""
2141
+ }) => {
2142
+ const [stats, setStats] = useState(null);
2143
+ const [events, setEvents] = useState([]);
2144
+ const [filters, setFilters] = useState({});
2145
+ const [loading, setLoading] = useState(true);
2146
+ const [selectedEvent, setSelectedEvent] = useState(null);
2147
+ const [currentPage, setCurrentPage] = useState(1);
2148
+ const [totalEvents, setTotalEvents] = useState(0);
2149
+ const pageSize = 20;
2150
+ const loadStats = async () => {
2151
+ try {
2152
+ const params = new URLSearchParams();
2153
+ if (filters.dateRange?.startDate) {
2154
+ params.append("startDate", filters.dateRange.startDate);
2155
+ }
2156
+ if (filters.dateRange?.endDate) {
2157
+ params.append("endDate", filters.dateRange.endDate);
2158
+ }
2159
+ if (filters.platform) {
2160
+ params.append("platform", filters.platform);
2161
+ }
2162
+ const response = await fetch(`${apiBaseUrl}/stats?${params}`);
2163
+ const data = await response.json();
2164
+ if (data.success) {
2165
+ setStats(data.data);
2166
+ }
2167
+ } catch (error) {
2168
+ console.error("Failed to load stats:", error);
2169
+ }
2170
+ };
2171
+ const loadEvents = async () => {
2172
+ try {
2173
+ setLoading(true);
2174
+ const params = new URLSearchParams();
2175
+ if (filters.dateRange?.startDate) {
2176
+ params.append("startDate", filters.dateRange.startDate);
2177
+ }
2178
+ if (filters.dateRange?.endDate) {
2179
+ params.append("endDate", filters.dateRange.endDate);
2180
+ }
2181
+ if (filters.eventType) {
2182
+ params.append("eventType", filters.eventType);
2183
+ }
2184
+ if (filters.platform) {
2185
+ params.append("platform", filters.platform);
2186
+ }
2187
+ if (filters.userId) {
2188
+ params.append("userId", filters.userId);
2189
+ }
2190
+ params.append("limit", pageSize.toString());
2191
+ params.append("offset", ((currentPage - 1) * pageSize).toString());
2192
+ params.append("orderBy", "timestamp");
2193
+ params.append("orderDirection", "desc");
2194
+ const response = await fetch(`${apiBaseUrl}/query?${params}`);
2195
+ const data = await response.json();
2196
+ if (data.success) {
2197
+ setEvents(data.data);
2198
+ setTotalEvents(data.total);
2199
+ }
2200
+ } catch (error) {
2201
+ console.error("Failed to load events:", error);
2202
+ } finally {
2203
+ setLoading(false);
2204
+ }
2205
+ };
2206
+ useEffect(() => {
2207
+ loadStats();
2208
+ loadEvents();
2209
+ }, [filters, currentPage]);
2210
+ const handleFilterChange = (newFilters) => {
2211
+ setFilters(newFilters);
2212
+ setCurrentPage(1);
2213
+ };
2214
+ const handleEventClick = (event) => {
2215
+ setSelectedEvent(event);
2216
+ };
2217
+ const closeEventDetail = () => {
2218
+ setSelectedEvent(null);
2219
+ };
2220
+ const totalPages = Math.ceil(totalEvents / pageSize);
2221
+ return /* @__PURE__ */ React5.createElement("div", { className: `space-y-6 ${className}` }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 text-sm text-gray-600" }, /* @__PURE__ */ React5.createElement(
2222
+ "svg",
2223
+ {
2224
+ className: "w-5 h-5 text-indigo-600",
2225
+ fill: "none",
2226
+ stroke: "currentColor",
2227
+ viewBox: "0 0 24 24"
2228
+ },
2229
+ /* @__PURE__ */ React5.createElement(
2230
+ "path",
2231
+ {
2232
+ strokeLinecap: "round",
2233
+ strokeLinejoin: "round",
2234
+ strokeWidth: 2,
2235
+ d: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
2236
+ }
2237
+ )
2238
+ ), /* @__PURE__ */ React5.createElement("span", { className: "font-medium" }, "\u5B9E\u65F6\u6570\u636E\u76D1\u63A7")), totalEvents > 0 && /* @__PURE__ */ React5.createElement("span", { className: "text-xs px-2 py-1 bg-indigo-100 text-indigo-700 rounded-full font-medium" }, totalEvents.toLocaleString(), " \u6761\u8BB0\u5F55")), /* @__PURE__ */ React5.createElement(
2239
+ "button",
2240
+ {
2241
+ onClick: () => {
2242
+ loadStats();
2243
+ loadEvents();
2244
+ },
2245
+ className: "flex items-center gap-2 px-4 py-2 text-sm bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-lg hover:from-indigo-700 hover:to-purple-700 transition-all duration-200 shadow-sm hover:shadow-md"
2246
+ },
2247
+ /* @__PURE__ */ React5.createElement("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React5.createElement(
2248
+ "path",
2249
+ {
2250
+ strokeLinecap: "round",
2251
+ strokeLinejoin: "round",
2252
+ strokeWidth: 2,
2253
+ d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
2254
+ }
2255
+ )),
2256
+ /* @__PURE__ */ React5.createElement("span", null, "\u5237\u65B0\u6570\u636E")
2257
+ )), /* @__PURE__ */ React5.createElement("div", { className: "grid grid-cols-12 gap-6" }, /* @__PURE__ */ React5.createElement("div", { className: "col-span-12 xl:col-span-3" }, /* @__PURE__ */ React5.createElement(FilterPanel, { onFilterChange: handleFilterChange, initialFilters: filters })), /* @__PURE__ */ React5.createElement("div", { className: "col-span-12 xl:col-span-9 space-y-6" }, loading && !stats ? /* @__PURE__ */ React5.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4" }, [1, 2, 3, 4].map((i) => /* @__PURE__ */ React5.createElement(
2258
+ "div",
2259
+ {
2260
+ key: i,
2261
+ className: "bg-white rounded-xl shadow-sm border border-gray-200 p-6 animate-pulse"
2262
+ },
2263
+ /* @__PURE__ */ React5.createElement("div", { className: "h-4 bg-gray-200 rounded w-20 mb-3" }),
2264
+ /* @__PURE__ */ React5.createElement("div", { className: "h-8 bg-gray-200 rounded w-24 mb-2" }),
2265
+ /* @__PURE__ */ React5.createElement("div", { className: "h-3 bg-gray-200 rounded w-16" })
2266
+ ))) : stats ? /* @__PURE__ */ React5.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4 animate-fade-in" }, /* @__PURE__ */ React5.createElement(
2267
+ StatCard,
2268
+ {
2269
+ title: "\u603B\u4E8B\u4EF6\u6570",
2270
+ value: stats.totalEvents,
2271
+ subtitle: "\u6240\u6709\u5E73\u53F0",
2272
+ icon: "\u{1F4C8}",
2273
+ className: "hover:scale-105 transition-transform duration-200"
2274
+ }
2275
+ ), /* @__PURE__ */ React5.createElement(
2276
+ StatCard,
2277
+ {
2278
+ title: "\u72EC\u7ACB\u7528\u6237",
2279
+ value: stats.uniqueUsers,
2280
+ subtitle: "\u53BB\u91CD\u7EDF\u8BA1",
2281
+ icon: "\u{1F465}",
2282
+ className: "hover:scale-105 transition-transform duration-200"
2283
+ }
2284
+ ), /* @__PURE__ */ React5.createElement(
2285
+ StatCard,
2286
+ {
2287
+ title: "\u4F1A\u8BDD\u6570",
2288
+ value: stats.uniqueSessions,
2289
+ subtitle: "\u7528\u6237\u4F1A\u8BDD",
2290
+ icon: "\u{1F517}",
2291
+ className: "hover:scale-105 transition-transform duration-200"
2292
+ }
2293
+ ), /* @__PURE__ */ React5.createElement(
2294
+ StatCard,
2295
+ {
2296
+ title: "\u8BBE\u5907\u6570",
2297
+ value: stats.uniqueDevices,
2298
+ subtitle: "\u72EC\u7ACB\u8BBE\u5907",
2299
+ icon: "\u{1F4F1}",
2300
+ className: "hover:scale-105 transition-transform duration-200"
2301
+ }
2302
+ )) : null, stats && /* @__PURE__ */ React5.createElement("div", { className: "space-y-6 animate-fade-in" }, /* @__PURE__ */ React5.createElement("div", { className: "grid grid-cols-1 xl:grid-cols-2 gap-6" }, /* @__PURE__ */ React5.createElement("div", { className: "transform hover:scale-[1.02] transition-transform duration-200" }, /* @__PURE__ */ React5.createElement(
2303
+ PieChart,
2304
+ {
2305
+ title: "\u5E73\u53F0\u5206\u5E03",
2306
+ data: stats.eventsByPlatform.map((item) => ({
2307
+ name: item.platform,
2308
+ value: item.count
2309
+ }))
2310
+ }
2311
+ )), /* @__PURE__ */ React5.createElement("div", { className: "transform hover:scale-[1.02] transition-transform duration-200" }, /* @__PURE__ */ React5.createElement(
2312
+ BarChart,
2313
+ {
2314
+ title: "\u4E8B\u4EF6\u7C7B\u578B Top 10",
2315
+ data: stats.eventsByType.slice(0, 10).map((item) => ({
2316
+ name: item.eventType,
2317
+ value: item.count
2318
+ }))
2319
+ }
2320
+ ))), stats.topPages.length > 0 && /* @__PURE__ */ React5.createElement("div", { className: "transform hover:scale-[1.02] transition-transform duration-200" }, /* @__PURE__ */ React5.createElement(
2321
+ BarChart,
2322
+ {
2323
+ title: "\u70ED\u95E8\u9875\u9762 Top 10",
2324
+ data: stats.topPages.slice(0, 10).map((item) => ({
2325
+ name: item.pageUrl,
2326
+ value: item.count
2327
+ }))
2328
+ }
2329
+ ))), /* @__PURE__ */ React5.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React5.createElement("h3", { className: "text-lg font-semibold text-gray-900 flex items-center gap-2" }, /* @__PURE__ */ React5.createElement(
2330
+ "svg",
2331
+ {
2332
+ className: "w-5 h-5 text-indigo-600",
2333
+ fill: "none",
2334
+ stroke: "currentColor",
2335
+ viewBox: "0 0 24 24"
2336
+ },
2337
+ /* @__PURE__ */ React5.createElement(
2338
+ "path",
2339
+ {
2340
+ strokeLinecap: "round",
2341
+ strokeLinejoin: "round",
2342
+ strokeWidth: 2,
2343
+ d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
2344
+ }
2345
+ )
2346
+ ), "\u4E8B\u4EF6\u8BB0\u5F55"), totalEvents > 0 && !loading && /* @__PURE__ */ React5.createElement("span", { className: "text-sm text-gray-500" }, "\u663E\u793A ", (currentPage - 1) * pageSize + 1, "-", Math.min(currentPage * pageSize, totalEvents), " / ", totalEvents.toLocaleString())), /* @__PURE__ */ React5.createElement("div", { className: "animate-fade-in" }, /* @__PURE__ */ React5.createElement(EventList, { events, loading, onEventClick: handleEventClick })), totalPages > 1 && /* @__PURE__ */ React5.createElement("div", { className: "flex items-center justify-between bg-gradient-to-r from-gray-50 to-gray-100 px-6 py-4 rounded-xl border border-gray-200" }, /* @__PURE__ */ React5.createElement("div", { className: "hidden sm:block text-sm text-gray-600" }, "\u7B2C ", /* @__PURE__ */ React5.createElement("span", { className: "font-semibold text-gray-900" }, currentPage), " \u9875\uFF0C\u5171", " ", /* @__PURE__ */ React5.createElement("span", { className: "font-semibold text-gray-900" }, totalPages), " \u9875"), /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React5.createElement(
2347
+ "button",
2348
+ {
2349
+ onClick: () => setCurrentPage((p) => Math.max(1, p - 1)),
2350
+ disabled: currentPage === 1,
2351
+ className: "flex items-center gap-1 px-4 py-2 text-sm font-medium bg-white border border-gray-300 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 hover:border-indigo-500 transition-all duration-200"
2352
+ },
2353
+ /* @__PURE__ */ React5.createElement("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React5.createElement(
2354
+ "path",
2355
+ {
2356
+ strokeLinecap: "round",
2357
+ strokeLinejoin: "round",
2358
+ strokeWidth: 2,
2359
+ d: "M15 19l-7-7 7-7"
2360
+ }
2361
+ )),
2362
+ /* @__PURE__ */ React5.createElement("span", { className: "hidden sm:inline" }, "\u4E0A\u4E00\u9875")
2363
+ ), /* @__PURE__ */ React5.createElement("div", { className: "sm:hidden px-4 py-2 text-sm text-gray-600" }, currentPage, " / ", totalPages), /* @__PURE__ */ React5.createElement(
2364
+ "button",
2365
+ {
2366
+ onClick: () => setCurrentPage((p) => Math.min(totalPages, p + 1)),
2367
+ disabled: currentPage === totalPages,
2368
+ className: "flex items-center gap-1 px-4 py-2 text-sm font-medium bg-white border border-gray-300 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 hover:border-indigo-500 transition-all duration-200"
2369
+ },
2370
+ /* @__PURE__ */ React5.createElement("span", { className: "hidden sm:inline" }, "\u4E0B\u4E00\u9875"),
2371
+ /* @__PURE__ */ React5.createElement("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React5.createElement(
2372
+ "path",
2373
+ {
2374
+ strokeLinecap: "round",
2375
+ strokeLinejoin: "round",
2376
+ strokeWidth: 2,
2377
+ d: "M9 5l7 7-7 7"
2378
+ }
2379
+ ))
2380
+ )))))), selectedEvent && /* @__PURE__ */ React5.createElement(
2381
+ "div",
2382
+ {
2383
+ className: "fixed inset-0 bg-black bg-opacity-60 backdrop-blur-sm flex items-center justify-center z-50 p-4 animate-fade-in",
2384
+ onClick: closeEventDetail
2385
+ },
2386
+ /* @__PURE__ */ React5.createElement(
2387
+ "div",
2388
+ {
2389
+ className: "bg-white rounded-2xl shadow-2xl max-w-3xl w-full max-h-[85vh] overflow-hidden animate-slide-up",
2390
+ onClick: (e) => e.stopPropagation()
2391
+ },
2392
+ /* @__PURE__ */ React5.createElement("div", { className: "bg-gradient-to-r from-indigo-600 to-purple-600 px-6 py-4 flex items-center justify-between" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-3 text-white" }, /* @__PURE__ */ React5.createElement("svg", { className: "w-6 h-6", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React5.createElement(
2393
+ "path",
2394
+ {
2395
+ strokeLinecap: "round",
2396
+ strokeLinejoin: "round",
2397
+ strokeWidth: 2,
2398
+ d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
2399
+ }
2400
+ )), /* @__PURE__ */ React5.createElement("h3", { className: "text-xl font-semibold" }, "\u4E8B\u4EF6\u8BE6\u60C5")), /* @__PURE__ */ React5.createElement(
2401
+ "button",
2402
+ {
2403
+ onClick: closeEventDetail,
2404
+ className: "text-white hover:bg-white hover:bg-opacity-20 rounded-full p-2 transition-all duration-200"
2405
+ },
2406
+ /* @__PURE__ */ React5.createElement("svg", { className: "w-6 h-6", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React5.createElement(
2407
+ "path",
2408
+ {
2409
+ strokeLinecap: "round",
2410
+ strokeLinejoin: "round",
2411
+ strokeWidth: 2,
2412
+ d: "M6 18L18 6M6 6l12 12"
2413
+ }
2414
+ ))
2415
+ )),
2416
+ /* @__PURE__ */ React5.createElement("div", { className: "p-6 overflow-auto max-h-[calc(85vh-80px)]" }, /* @__PURE__ */ React5.createElement("div", { className: "space-y-5" }, /* @__PURE__ */ React5.createElement("div", { className: "bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl p-4 border border-blue-200" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 mb-3" }, /* @__PURE__ */ React5.createElement(
2417
+ "svg",
2418
+ {
2419
+ className: "w-5 h-5 text-indigo-600",
2420
+ fill: "none",
2421
+ stroke: "currentColor",
2422
+ viewBox: "0 0 24 24"
2423
+ },
2424
+ /* @__PURE__ */ React5.createElement(
2425
+ "path",
2426
+ {
2427
+ strokeLinecap: "round",
2428
+ strokeLinejoin: "round",
2429
+ strokeWidth: 2,
2430
+ d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
2431
+ }
2432
+ )
2433
+ ), /* @__PURE__ */ React5.createElement("h4", { className: "font-semibold text-gray-900" }, "\u57FA\u7840\u4FE1\u606F")), /* @__PURE__ */ React5.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600" }, "\u4E8B\u4EF6ID"), /* @__PURE__ */ React5.createElement("code", { className: "text-xs bg-white px-2 py-1 rounded border border-blue-200 font-mono text-gray-800" }, selectedEvent.id.slice(0, 16), "...")), /* @__PURE__ */ React5.createElement("div", { className: "grid grid-cols-2 gap-3 pt-2" }, /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u4E8B\u4EF6\u7C7B\u578B"), /* @__PURE__ */ React5.createElement("span", { className: "inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800" }, selectedEvent.eventType)), /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u4E8B\u4EF6\u540D\u79F0"), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-gray-900 truncate" }, selectedEvent.eventName))))), /* @__PURE__ */ React5.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React5.createElement("div", { className: "bg-gray-50 rounded-lg p-3 border border-gray-200" }, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u5E73\u53F0"), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-semibold text-gray-900" }, selectedEvent.platform)), /* @__PURE__ */ React5.createElement("div", { className: "bg-gray-50 rounded-lg p-3 border border-gray-200" }, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u5E94\u7528\u7248\u672C"), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-semibold text-gray-900" }, selectedEvent.appVersion))), /* @__PURE__ */ React5.createElement("div", { className: "bg-gradient-to-br from-purple-50 to-pink-50 rounded-xl p-4 border border-purple-200" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 mb-3" }, /* @__PURE__ */ React5.createElement(
2434
+ "svg",
2435
+ {
2436
+ className: "w-5 h-5 text-purple-600",
2437
+ fill: "none",
2438
+ stroke: "currentColor",
2439
+ viewBox: "0 0 24 24"
2440
+ },
2441
+ /* @__PURE__ */ React5.createElement(
2442
+ "path",
2443
+ {
2444
+ strokeLinecap: "round",
2445
+ strokeLinejoin: "round",
2446
+ strokeWidth: 2,
2447
+ d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
2448
+ }
2449
+ )
2450
+ ), /* @__PURE__ */ React5.createElement("h4", { className: "font-semibold text-gray-900" }, "\u7528\u6237\u4FE1\u606F")), /* @__PURE__ */ React5.createElement("div", { className: "grid grid-cols-2 gap-3" }, /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u7528\u6237ID"), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-gray-900" }, selectedEvent.userId || /* @__PURE__ */ React5.createElement("span", { className: "text-gray-400" }, "\u6E38\u5BA2"))), /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u4F1A\u8BDDID"), /* @__PURE__ */ React5.createElement("code", { className: "text-xs bg-white px-2 py-1 rounded border border-purple-200 font-mono text-gray-800 block truncate" }, selectedEvent.sessionId.slice(0, 12), "..."))), /* @__PURE__ */ React5.createElement("div", { className: "mt-3 pt-3 border-t border-purple-200" }, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u8BBE\u5907ID"), /* @__PURE__ */ React5.createElement("code", { className: "text-xs bg-white px-2 py-1 rounded border border-purple-200 font-mono text-gray-800 block truncate" }, selectedEvent.deviceId))), (selectedEvent.pageUrl || selectedEvent.pageTitle) && /* @__PURE__ */ React5.createElement("div", { className: "bg-gradient-to-br from-green-50 to-emerald-50 rounded-xl p-4 border border-green-200" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 mb-3" }, /* @__PURE__ */ React5.createElement(
2451
+ "svg",
2452
+ {
2453
+ className: "w-5 h-5 text-green-600",
2454
+ fill: "none",
2455
+ stroke: "currentColor",
2456
+ viewBox: "0 0 24 24"
2457
+ },
2458
+ /* @__PURE__ */ React5.createElement(
2459
+ "path",
2460
+ {
2461
+ strokeLinecap: "round",
2462
+ strokeLinejoin: "round",
2463
+ strokeWidth: 2,
2464
+ d: "M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"
2465
+ }
2466
+ )
2467
+ ), /* @__PURE__ */ React5.createElement("h4", { className: "font-semibold text-gray-900" }, "\u9875\u9762\u4FE1\u606F")), selectedEvent.pageTitle && /* @__PURE__ */ React5.createElement("div", { className: "mb-2" }, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u9875\u9762\u6807\u9898"), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-gray-900" }, selectedEvent.pageTitle)), selectedEvent.pageUrl && /* @__PURE__ */ React5.createElement("div", null, /* @__PURE__ */ React5.createElement("span", { className: "text-xs font-medium text-gray-600 block mb-1" }, "\u9875\u9762URL"), /* @__PURE__ */ React5.createElement("code", { className: "text-xs bg-white px-2 py-1 rounded border border-green-200 font-mono text-gray-800 block break-all" }, selectedEvent.pageUrl))), /* @__PURE__ */ React5.createElement("div", { className: "bg-gradient-to-br from-amber-50 to-yellow-50 rounded-xl p-4 border border-amber-200" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React5.createElement(
2468
+ "svg",
2469
+ {
2470
+ className: "w-5 h-5 text-amber-600",
2471
+ fill: "none",
2472
+ stroke: "currentColor",
2473
+ viewBox: "0 0 24 24"
2474
+ },
2475
+ /* @__PURE__ */ React5.createElement(
2476
+ "path",
2477
+ {
2478
+ strokeLinecap: "round",
2479
+ strokeLinejoin: "round",
2480
+ strokeWidth: 2,
2481
+ d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
2482
+ }
2483
+ )
2484
+ ), /* @__PURE__ */ React5.createElement("h4", { className: "font-semibold text-gray-900" }, "\u65F6\u95F4\u4FE1\u606F")), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-gray-900" }, new Date(selectedEvent.timestamp).toLocaleString("zh-CN", {
2485
+ year: "numeric",
2486
+ month: "long",
2487
+ day: "numeric",
2488
+ hour: "2-digit",
2489
+ minute: "2-digit",
2490
+ second: "2-digit"
2491
+ }))), selectedEvent.properties && Object.keys(selectedEvent.properties).length > 0 && /* @__PURE__ */ React5.createElement("div", { className: "bg-gradient-to-br from-gray-50 to-slate-50 rounded-xl p-4 border border-gray-300" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 mb-3" }, /* @__PURE__ */ React5.createElement(
2492
+ "svg",
2493
+ {
2494
+ className: "w-5 h-5 text-gray-600",
2495
+ fill: "none",
2496
+ stroke: "currentColor",
2497
+ viewBox: "0 0 24 24"
2498
+ },
2499
+ /* @__PURE__ */ React5.createElement(
2500
+ "path",
2501
+ {
2502
+ strokeLinecap: "round",
2503
+ strokeLinejoin: "round",
2504
+ strokeWidth: 2,
2505
+ d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
2506
+ }
2507
+ )
2508
+ ), /* @__PURE__ */ React5.createElement("h4", { className: "font-semibold text-gray-900" }, "\u81EA\u5B9A\u4E49\u5C5E\u6027")), /* @__PURE__ */ React5.createElement("pre", { className: "text-xs bg-white p-4 rounded-lg border border-gray-200 overflow-auto max-h-48 font-mono text-gray-800" }, JSON.stringify(selectedEvent.properties, null, 2)))))
2509
+ )
2510
+ ));
2511
+ };
2512
+
2513
+ // src/analytics/index.ts
2514
+ var ANALYTICS_VERSION = "1.0.0";
2515
+
2516
+ export { ANALYTICS_VERSION, Analytics, AnalyticsDashboard, BarChart, CatchError, DesktopDeviceAdapter, DesktopEventStorageAdapter, DesktopNetworkAdapter, DesktopStorageAdapter, EventList, EventPriority, EventQueue, EventType, FilterPanel, MiniappDeviceAdapter, MiniappEventStorageAdapter, MiniappNetworkAdapter, MiniappStorageAdapter, MobileDeviceAdapter, MobileEventStorageAdapter, MobileNetworkAdapter, MobileStorageAdapter, PieChart, StatCard, Track, TrackClick, TrackPerformance, Uploader, WebDeviceAdapter, WebEventStorageAdapter, WebNetworkAdapter, createAnalytics, createDesktopConfig, createMiniappConfig, createMobileConfig, createWebConfig, debounce, deepClone, formatEvent, formatTimestamp, generateUniqueId, getAllInstanceKeys, getAnalyticsInstance, getBatchSize, getCurrentPageTitle, getCurrentPageUrl, getEventSize, getGlobalAnalytics, getPageDuration, getReferrer, isAnalyticsInitialized, isDevelopment, isMobile, mergeEventProperties, resetAllAnalytics, resetAnalytics, safeParse, safeStringify, sanitizeEvent, setGlobalAnalytics, throttle, useAnalytics, useAutoTracking, useErrorTracking, usePageDuration, usePageView, usePerformanceTracking, useTrackClick, useTrackEvent, validateEvent, validateEvents, webAdapter };
2517
+ //# sourceMappingURL=index.mjs.map
2518
+ //# sourceMappingURL=index.mjs.map