sunda-tracker 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,806 @@
1
+ import b from "axios";
2
+ function T(o, e = !1) {
3
+ if (!o || typeof o != "string")
4
+ return "";
5
+ const t = o.split("-");
6
+ let n = e ? t[0].charAt(0).toUpperCase() + t[0].slice(1) : t[0];
7
+ for (let r = 1; r < t.length; r++) {
8
+ const s = t[r];
9
+ s.length > 0 && (n += s.charAt(0).toUpperCase() + s.slice(1));
10
+ }
11
+ return n;
12
+ }
13
+ function E(o = /* @__PURE__ */ new Date(), e = "YYYY-MM-DD") {
14
+ const t = o instanceof Date ? o : new Date(o), n = t.getFullYear(), r = t.getMonth() + 1, s = t.getDate(), i = t.getHours(), c = t.getMinutes(), a = t.getSeconds(), l = {
15
+ YYYY: n,
16
+ YY: String(n).slice(-2),
17
+ MM: r < 10 ? `0${r}` : r,
18
+ M: r,
19
+ DD: s < 10 ? `0${s}` : s,
20
+ D: s,
21
+ HH: i < 10 ? `0${i}` : i,
22
+ H: i,
23
+ mm: c < 10 ? `0${c}` : c,
24
+ m: c,
25
+ ss: a < 10 ? `0${a}` : a,
26
+ s: a
27
+ };
28
+ let u = e;
29
+ for (const [m, f] of Object.entries(l))
30
+ u = u.replace(new RegExp(m, "g"), f);
31
+ return u;
32
+ }
33
+ function w(o) {
34
+ return {
35
+ _date: /* @__PURE__ */ new Date(),
36
+ format(e) {
37
+ return E(this._date, e);
38
+ }
39
+ };
40
+ }
41
+ function I() {
42
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(o) {
43
+ const e = Math.random() * 16 | 0;
44
+ return (o === "x" ? e : e & 3 | 8).toString(16);
45
+ });
46
+ }
47
+ function D(o, e, t = {}) {
48
+ const { leading: n = !0, trailing: r = !0 } = t;
49
+ let s = null, i = null, c = null, a = 0, l;
50
+ function u(h) {
51
+ const d = h - a;
52
+ return a === 0 || d >= e;
53
+ }
54
+ function m(h) {
55
+ const d = i, p = c;
56
+ return i = c = null, a = h, l = o.apply(p, d), l;
57
+ }
58
+ function f(h, d) {
59
+ return setTimeout(h, d);
60
+ }
61
+ function S(h) {
62
+ return s = null, r && i ? m(h) : (i = c = null, l);
63
+ }
64
+ function k() {
65
+ s !== null && clearTimeout(s), a = 0, s = i = c = null;
66
+ }
67
+ function v() {
68
+ return s === null ? l : S(Date.now());
69
+ }
70
+ function y() {
71
+ return s !== null;
72
+ }
73
+ function g(...h) {
74
+ const d = Date.now(), p = u(d);
75
+ if (i = h, c = this, p)
76
+ return a === 0 && !n ? (a = d, l) : m(d);
77
+ if (s === null && r) {
78
+ const x = e - (d - a);
79
+ s = f(() => S(Date.now()), x);
80
+ }
81
+ return l;
82
+ }
83
+ return g.cancel = k, g.flush = v, g.pending = y, g;
84
+ }
85
+ class A {
86
+ constructor(e) {
87
+ this.track = e, this.init(), this.options = e.options;
88
+ }
89
+ init() {
90
+ this.bindClickEvents(), this.observeRouteChanges();
91
+ }
92
+ bindClickEvents() {
93
+ document.addEventListener("click", (e) => {
94
+ const n = e.target.closest("[data-track-type]");
95
+ if (n) {
96
+ const r = n.getAttribute("data-track-type"), s = {};
97
+ for (const i of n.attributes)
98
+ if (i.name.startsWith("data-track-args-")) {
99
+ const c = T(
100
+ i.name.replace("data-track-args-", "")
101
+ );
102
+ s[c] = i.value;
103
+ }
104
+ r && this.track.track(r, {
105
+ ...s,
106
+ sessionId: this.track.sessionId
107
+ });
108
+ }
109
+ });
110
+ }
111
+ observeRouteChanges() {
112
+ }
113
+ }
114
+ class _ {
115
+ constructor(e) {
116
+ this.track = e, this.metrics = {}, this.isSupported = this.checkSupport(), this.isSupported && this.init();
117
+ }
118
+ checkSupport() {
119
+ return typeof window < "u" && window.performance && window.performance.timing && window.performance.getEntriesByType;
120
+ }
121
+ init() {
122
+ this.observePageLoad(), this.observeResources(), this.observeLongTasks(), this.observePaint(), this.observeErrors();
123
+ }
124
+ observePageLoad() {
125
+ window.addEventListener("load", () => {
126
+ setTimeout(() => {
127
+ const e = performance.getEntriesByType("navigation")[0], t = {
128
+ dnsTime: e.domainLookupEnd - e.domainLookupStart,
129
+ tcpTime: e.connectEnd - e.connectStart,
130
+ sslTime: e.secureConnectionStart > 0 ? e.connectEnd - e.secureConnectionStart : 0,
131
+ ttfb: e.responseStart - e.requestStart,
132
+ responseTime: e.responseEnd - e.responseStart,
133
+ domParseTime: e.domInteractive - e.responseEnd,
134
+ domReadyTime: e.domContentLoadedEventEnd - e.startTime,
135
+ loadTime: e.loadEventEnd - e.startTime
136
+ };
137
+ this.track.track("page_performance", t);
138
+ }, 0);
139
+ });
140
+ }
141
+ observeResources() {
142
+ new PerformanceObserver(
143
+ D((t) => {
144
+ t.getEntries().forEach((r) => {
145
+ r.initiatorType !== "fetch" && r.initiatorType !== "xmlhttprequest" && this.track.track("resource_performance", {
146
+ name: r.name,
147
+ type: r.initiatorType,
148
+ duration: r.duration,
149
+ size: r.transferSize,
150
+ protocol: r.nextHopProtocol
151
+ });
152
+ });
153
+ }, 3e3)
154
+ ).observe({ entryTypes: ["resource"] });
155
+ }
156
+ observeLongTasks() {
157
+ typeof PerformanceLongTaskTiming < "u" && new PerformanceObserver((t) => {
158
+ t.getEntries().forEach((n) => {
159
+ this.track.track("long_task", {
160
+ duration: n.duration,
161
+ startTime: n.startTime,
162
+ name: n.name
163
+ });
164
+ });
165
+ }).observe({ entryTypes: ["longtask"] });
166
+ }
167
+ observePaint() {
168
+ new PerformanceObserver((t) => {
169
+ t.getEntries().forEach((r) => {
170
+ this.track.track("paint_timing", {
171
+ name: r.name,
172
+ startTime: r.startTime
173
+ });
174
+ });
175
+ }).observe({ entryTypes: ["paint"] });
176
+ }
177
+ observeErrors() {
178
+ window.addEventListener(
179
+ "error",
180
+ (e) => {
181
+ this.track.track("js_error", {
182
+ message: e.message,
183
+ filename: e.filename,
184
+ lineno: e.lineno,
185
+ colno: e.colno,
186
+ error: e.error ? e.error.stack : null
187
+ });
188
+ },
189
+ !0
190
+ ), window.addEventListener("unhandledrejection", (e) => {
191
+ this.track.track("promise_error", {
192
+ message: e.reason.message || e.reason,
193
+ stack: e.reason.stack
194
+ });
195
+ });
196
+ }
197
+ getFirstScreenTime() {
198
+ return new Promise((e) => {
199
+ document.readyState === "complete" ? this.calculateFirstScreen(e) : window.addEventListener("load", () => {
200
+ this.calculateFirstScreen(e);
201
+ });
202
+ });
203
+ }
204
+ calculateFirstScreen(e) {
205
+ const t = document.querySelectorAll("img");
206
+ let n = performance.timing.domContentLoadedEventEnd;
207
+ if (t.length === 0) {
208
+ e(n);
209
+ return;
210
+ }
211
+ let r = 0;
212
+ const s = () => {
213
+ r++, r === t.length && e(n);
214
+ };
215
+ t.forEach((i) => {
216
+ i.complete ? s() : (i.addEventListener("load", () => {
217
+ const c = performance.now();
218
+ n = Math.max(n, c), s();
219
+ }), i.addEventListener("error", s));
220
+ });
221
+ }
222
+ getCurrentMetrics() {
223
+ return this.isSupported ? {
224
+ ...this.metrics,
225
+ memory: performance.memory ? {
226
+ usedJSHeapSize: performance.memory.usedJSHeapSize,
227
+ totalJSHeapSize: performance.memory.totalJSHeapSize
228
+ } : null,
229
+ navigation: performance.getEntriesByType("navigation")[0],
230
+ timing: performance.timing
231
+ } : null;
232
+ }
233
+ }
234
+ class O {
235
+ constructor() {
236
+ this.parser = this.initUAParser();
237
+ }
238
+ initUAParser() {
239
+ const e = navigator.userAgent;
240
+ return {
241
+ os: this.getOS(e),
242
+ browser: this.getBrowser(e),
243
+ device: this.getDevice(e)
244
+ };
245
+ }
246
+ generateUUID() {
247
+ return I();
248
+ }
249
+ getOS(e) {
250
+ let t = "unknown", n = "unknown";
251
+ if (e.includes("Windows"))
252
+ t = "Windows", e.includes("Windows NT 10.0") ? n = "10" : e.includes("Windows NT 6.3") ? n = "8.1" : e.includes("Windows NT 6.2") ? n = "8" : e.includes("Windows NT 6.1") && (n = "7");
253
+ else if (e.includes("Macintosh")) {
254
+ t = "macOS";
255
+ const r = e.match(/Mac OS X ([0-9_]+)/);
256
+ r && (n = r[1].replace(/_/g, "."));
257
+ } else if (e.includes("iPhone") || e.includes("iPad")) {
258
+ t = "iOS";
259
+ const r = e.match(/OS ([0-9_]+)/);
260
+ r && (n = r[1].replace(/_/g, "."));
261
+ } else if (e.includes("Android")) {
262
+ t = "Android";
263
+ const r = e.match(/Android ([0-9.]+)/);
264
+ r && (n = r[1]);
265
+ } else e.includes("Linux") && (t = "Linux");
266
+ return { name: t, version: n };
267
+ }
268
+ getBrowser(e) {
269
+ let t = "unknown", n = "unknown", r = "unknown";
270
+ const s = "unknown";
271
+ if (e.includes("Chrome/")) {
272
+ t = "Chrome";
273
+ const i = e.match(/Chrome\/([0-9.]+)/);
274
+ i && (n = i[1]), r = "Blink";
275
+ } else if (e.includes("Firefox/")) {
276
+ t = "Firefox";
277
+ const i = e.match(/Firefox\/([0-9.]+)/);
278
+ i && (n = i[1]), r = "Gecko";
279
+ } else if (e.includes("Safari/") && !e.includes("Chrome/")) {
280
+ t = "Safari";
281
+ const i = e.match(/Version\/([0-9.]+)/);
282
+ i && (n = i[1]), r = "WebKit";
283
+ } else if (e.includes("Edg/")) {
284
+ t = "Edge";
285
+ const i = e.match(/Edg\/([0-9.]+)/);
286
+ i && (n = i[1]), r = "Blink";
287
+ } else if (e.includes("Trident/")) {
288
+ t = "Internet Explorer";
289
+ const i = e.match(/rv:([0-9.]+)/);
290
+ i && (n = i[1]), r = "Trident";
291
+ }
292
+ return { name: t, version: n, engine: r, engineVersion: s };
293
+ }
294
+ getDevice(e) {
295
+ let t = "desktop", n = "unknown", r = "unknown";
296
+ const s = e.toLowerCase();
297
+ if ((s.includes("macintosh") || s.includes("mac os")) && (n = "Mac"), s.includes("windows"))
298
+ return n = "PC", { type: t, brand: n, model: r };
299
+ if (e.includes("Mobile") || e.includes("Android") || e.includes("iPhone")) {
300
+ if (t = "mobile", e.includes("iPhone"))
301
+ n = "Apple", r = "iPhone";
302
+ else if (e.includes("iPad"))
303
+ n = "Apple", r = "iPad", t = "tablet";
304
+ else if (e.includes("Android")) {
305
+ n = "Android";
306
+ const i = {
307
+ // 中国品牌
308
+ huawei: "Huawei",
309
+ honor: "Honor",
310
+ xiaomi: "Xiaomi",
311
+ redmi: "Xiaomi",
312
+ poco: "Xiaomi",
313
+ mi: "Xiaomi",
314
+ oppo: "OPPO",
315
+ oneplus: "OnePlus",
316
+ vivo: "vivo",
317
+ iqoo: "vivo",
318
+ realme: "Realme",
319
+ meizu: "Meizu",
320
+ zte: "ZTE",
321
+ lenovo: "Lenovo",
322
+ // 三星系列
323
+ samsung: "Samsung",
324
+ "sm-": "Samsung",
325
+ "gt-": "Samsung",
326
+ "sgh-": "Samsung",
327
+ galaxy: "Samsung",
328
+ // 传音系列
329
+ tecno: "TECNO",
330
+ infinix: "Infinix",
331
+ itel: "itel",
332
+ // 其他国际品牌
333
+ sony: "Sony",
334
+ "sony ericsson": "Sony",
335
+ lg: "LG",
336
+ motorola: "Motorola",
337
+ moto: "Motorola",
338
+ nokia: "Nokia",
339
+ htc: "HTC",
340
+ asus: "ASUS",
341
+ zenfone: "ASUS",
342
+ blackberry: "BlackBerry",
343
+ google: "Google",
344
+ pixel: "Google",
345
+ surface: "Surface",
346
+ // 其他中国品牌
347
+ nubia: "Nubia",
348
+ 360: "360",
349
+ smartisan: "Smartisan",
350
+ gione: "Gionee",
351
+ "k-touch": "K-Touch",
352
+ // 印度品牌
353
+ micromax: "Micromax",
354
+ lava: "Lava",
355
+ karbonn: "Karbonn",
356
+ // 其他亚洲品牌
357
+ sharp: "Sharp",
358
+ panasonic: "Panasonic"
359
+ };
360
+ for (const a in i)
361
+ if (s.includes(a)) {
362
+ n = i[a];
363
+ break;
364
+ }
365
+ if (n === "Android") {
366
+ const a = e.match(/\((.+?)\)/);
367
+ if (a) {
368
+ const l = a[1].split(";");
369
+ for (let u = 2; u < l.length; u++) {
370
+ const m = l[u] && l[u].trim().toLowerCase();
371
+ if (m) {
372
+ for (const f in i)
373
+ if (m.includes(f)) {
374
+ n = i[f];
375
+ break;
376
+ }
377
+ }
378
+ if (n !== "Android") break;
379
+ }
380
+ }
381
+ }
382
+ const c = e.match(/Build\/(.*?)[)\s]/);
383
+ c && (r = c[1].trim());
384
+ }
385
+ }
386
+ return { type: t, brand: n, model: r };
387
+ }
388
+ getInfo() {
389
+ const e = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
390
+ return {
391
+ // 基础信息
392
+ userAgent: navigator.userAgent,
393
+ platform: navigator.platform,
394
+ language: navigator.language,
395
+ languages: navigator.languages,
396
+ // 操作系统信息
397
+ os: this.parser.os,
398
+ // 浏览器信息
399
+ browser: this.parser.browser,
400
+ // 设备信息
401
+ device: this.parser.device,
402
+ // 屏幕信息
403
+ screen: {
404
+ width: window.screen.width,
405
+ height: window.screen.height,
406
+ availWidth: window.screen.availWidth,
407
+ availHeight: window.screen.availHeight,
408
+ colorDepth: window.screen.colorDepth,
409
+ pixelDepth: window.screen.pixelDepth,
410
+ orientation: window.screen.orientation ? window.screen.orientation.type : "unknown"
411
+ },
412
+ // 窗口信息
413
+ window: {
414
+ innerWidth: window.innerWidth,
415
+ innerHeight: window.innerHeight,
416
+ outerWidth: window.outerWidth,
417
+ outerHeight: window.outerHeight,
418
+ devicePixelRatio: window.devicePixelRatio
419
+ },
420
+ // 网络信息
421
+ network: e ? {
422
+ type: e.type,
423
+ effectiveType: e.effectiveType,
424
+ downlink: e.downlink,
425
+ downlinkMax: e.downlinkMax,
426
+ rtt: e.rtt,
427
+ saveData: e.saveData
428
+ } : void 0,
429
+ // 硬件信息
430
+ hardware: {
431
+ maxTouchPoints: navigator.maxTouchPoints,
432
+ hardwareConcurrency: navigator.hardwareConcurrency,
433
+ deviceMemory: navigator.deviceMemory
434
+ }
435
+ };
436
+ }
437
+ }
438
+ const C = {
439
+ serverUrl: "",
440
+ appId: "",
441
+ cacheLimit: 10,
442
+ autoTrack: !1,
443
+ enablePerformance: !1,
444
+ debug: !1,
445
+ maxRetryTimes: 3,
446
+ timeout: 5e3,
447
+ type: "es",
448
+ // es, api
449
+ collectUrl: "",
450
+ collectMethod: "post"
451
+ }, P = {
452
+ // node: import.meta.env.VITE_APP_ES_NODE,
453
+ // id: 'SEGwE48BDXHCyTKWTBXE',
454
+ // index: import.meta.env.VITE_APP_ES_INDEX_NAME,
455
+ // api_key: '3HLDi5PYTfymTnAj7MmG-w',
456
+ // encoded: import.meta.env.VITE_APP_ES_ENCODED
457
+ };
458
+ class M {
459
+ /**
460
+ * @param {Object} config 配置项
461
+ * @param {string} config.node ES服务器地址
462
+ * @param {string} config.index 索引名称
463
+ * @param {string} [config.username] 用户名
464
+ * @param {string} [config.password] 密码
465
+ * @param {number} [config.batchSize=100] 批量发送大小
466
+ * @param {number} [config.flushInterval=5000] 自动刷新间隔(ms)
467
+ */
468
+ constructor(e) {
469
+ this.config = {
470
+ batchSize: 100,
471
+ flushInterval: 5e3,
472
+ ...P,
473
+ ...e
474
+ }, this.queue = [], this.headers = this.buildHeaders(), this.timer = null, this.startAutoFlush();
475
+ }
476
+ buildHeaders() {
477
+ const e = {
478
+ "Content-Type": "application/x-ndjson",
479
+ Accept: "application/json"
480
+ }, t = this.config.encoded;
481
+ return e.Authorization = `ApiKey ${t}`, e;
482
+ }
483
+ /**
484
+ * 发送单条数据
485
+ * @param {Object} data
486
+ */
487
+ async send(e) {
488
+ try {
489
+ const t = {
490
+ ...e,
491
+ "@timestamp": (/* @__PURE__ */ new Date()).toISOString()
492
+ };
493
+ return this.queue.push(t), this.queue.length >= this.config.batchSize && await this.flush(), !0;
494
+ } catch (t) {
495
+ return console.error("Error sending data to Elasticsearch:", t), !1;
496
+ }
497
+ }
498
+ /**
499
+ * 批量发送数据
500
+ * @param {Array} dataArray 数据数组
501
+ */
502
+ async sendBulk(e) {
503
+ try {
504
+ const t = (/* @__PURE__ */ new Date()).toISOString(), n = e.map((r) => ({
505
+ ...r,
506
+ "@timestamp": t
507
+ }));
508
+ return this.queue.push(...n), this.queue.length >= this.config.batchSize && await this.flush(), !0;
509
+ } catch (t) {
510
+ return console.error("Error sending bulk data to Elasticsearch:", t), !1;
511
+ }
512
+ }
513
+ /**
514
+ * 构建批量请求体
515
+ * @param {Array} documents 文档数组
516
+ * @returns {string} NDJSON格式的请求体
517
+ */
518
+ buildBulkRequestBody(e) {
519
+ return e.flatMap((t) => [
520
+ JSON.stringify({
521
+ index: {
522
+ _index: `${this.config.index}-${w().format("YYYY.MM.DD")}`
523
+ }
524
+ }),
525
+ JSON.stringify(t)
526
+ ]).join(`
527
+ `) + `
528
+ `;
529
+ }
530
+ async flush() {
531
+ if (this.queue.length === 0) return;
532
+ const e = [...this.queue];
533
+ this.queue = [];
534
+ try {
535
+ const t = this.buildBulkRequestBody(e), n = await fetch(
536
+ `${this.config.node}/${this.config.index}-${w().format(
537
+ "YYYY.MM.DD"
538
+ )}/_bulk`,
539
+ {
540
+ method: "POST",
541
+ headers: this.headers,
542
+ body: t
543
+ }
544
+ );
545
+ if (!n.ok)
546
+ throw new Error(`HTTP error! status: ${n.status}`);
547
+ const r = await n.json();
548
+ if (r.errors) {
549
+ const s = r.items.filter(
550
+ (i) => i.index.status >= 400
551
+ );
552
+ s.length > 0 && (console.error("Some documents failed to index:", s), this.handleFailedDocuments(s, e));
553
+ }
554
+ return !0;
555
+ } catch (t) {
556
+ return this.stopAutoFlush(), console.error("Error flushing data to Elasticsearch:", t), this.queue.unshift(...e), !1;
557
+ }
558
+ }
559
+ handleFailedDocuments(e, t) {
560
+ e.forEach((n) => {
561
+ const r = t[n.index._id];
562
+ this.storeFailedDocument(r);
563
+ });
564
+ }
565
+ storeFailedDocument(e) {
566
+ try {
567
+ const t = JSON.parse(
568
+ localStorage.getItem("es_failed_docs") || "[]"
569
+ );
570
+ t.push({
571
+ document: e,
572
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
573
+ retryCount: 0
574
+ }), localStorage.setItem("es_failed_docs", JSON.stringify(t));
575
+ } catch (t) {
576
+ console.error("Error storing failed document:", t);
577
+ }
578
+ }
579
+ async retryFailedDocuments() {
580
+ try {
581
+ const e = JSON.parse(
582
+ localStorage.getItem("es_failed_docs") || "[]"
583
+ );
584
+ if (e.length === 0) return;
585
+ const t = e.filter((r) => r.retryCount < 3), n = e.filter((r) => r.retryCount >= 3);
586
+ t.forEach((r) => r.retryCount++), await this.sendBulk(t.map((r) => r.document)), localStorage.setItem("es_failed_docs", JSON.stringify(n));
587
+ } catch (e) {
588
+ console.error("Error retrying failed documents:", e);
589
+ }
590
+ }
591
+ startAutoFlush() {
592
+ this.timer = setInterval(() => {
593
+ this.queue.length > 0 && this.flush(), this.retryFailedDocuments();
594
+ }, this.config.flushInterval);
595
+ }
596
+ stopAutoFlush() {
597
+ this.timer && (clearInterval(this.timer), this.timer = null);
598
+ }
599
+ destroy() {
600
+ return this.stopAutoFlush(), this.flush();
601
+ }
602
+ }
603
+ class F {
604
+ /**
605
+ * @param {Object} config 配置项
606
+ * @param {string} config.node ES服务器地址
607
+ * @param {string} config.index 索引名称
608
+ * @param {string} [config.username] 用户名
609
+ * @param {string} [config.password] 密码
610
+ * @param {number} [config.batchSize=100] 批量发送大小
611
+ * @param {number} [config.flushInterval=5000] 自动刷新间隔(ms)
612
+ */
613
+ constructor(e) {
614
+ this.config = {
615
+ batchSize: 100,
616
+ flushInterval: 5e3,
617
+ ...e
618
+ }, this.queue = [], this.timer = null, this.genApiCall(e), this.startAutoFlush();
619
+ }
620
+ async genApiCall({ collectUrl: e, collectMethod: t }) {
621
+ this.batchSave = (n) => b({
622
+ url: e,
623
+ method: t,
624
+ data: n
625
+ });
626
+ }
627
+ /**
628
+ * 发送单条数据
629
+ * @param {Object} data
630
+ */
631
+ async send(e) {
632
+ try {
633
+ const t = {
634
+ ...e,
635
+ "@timestamp": (/* @__PURE__ */ new Date()).toISOString()
636
+ };
637
+ return this.queue.push(t), this.queue.length >= this.config.batchSize && await this.flush(), !0;
638
+ } catch (t) {
639
+ return console.error("Error sending data to Elasticsearch:", t), !1;
640
+ }
641
+ }
642
+ /**
643
+ * 批量发送数据
644
+ * @param {Array} dataArray 数据数组
645
+ */
646
+ async sendBulk(e) {
647
+ try {
648
+ const t = (/* @__PURE__ */ new Date()).toISOString(), n = e.map((r) => ({
649
+ ...r,
650
+ "@timestamp": t
651
+ }));
652
+ return this.queue.push(...n), this.queue.length >= this.config.batchSize && await this.flush(), !0;
653
+ } catch (t) {
654
+ return console.error("Error sending bulk data to Elasticsearch:", t), !1;
655
+ }
656
+ }
657
+ /**
658
+ * 构建批量请求体
659
+ * @param {Array} documents 文档数组
660
+ * @returns {string} NDJSON格式的请求体
661
+ */
662
+ buildBulkRequestBody(e) {
663
+ return e.flatMap((t) => [
664
+ JSON.stringify({
665
+ index: {
666
+ _index: `${this.config.index}-${w().format("YYYY.MM.DD")}`
667
+ }
668
+ }),
669
+ JSON.stringify(t)
670
+ ]).join(`
671
+ `) + `
672
+ `;
673
+ }
674
+ async flush() {
675
+ if (this.queue.length === 0) return;
676
+ const e = [...this.queue];
677
+ this.queue = [];
678
+ try {
679
+ return await this.batchSave(e), !0;
680
+ } catch (t) {
681
+ return this.stopAutoFlush(), console.error("Error flushing data to Elasticsearch:", t), this.queue.unshift(...e), !1;
682
+ }
683
+ }
684
+ handleFailedDocuments(e, t) {
685
+ e.forEach((n) => {
686
+ const r = t[n.index._id];
687
+ this.storeFailedDocument(r);
688
+ });
689
+ }
690
+ storeFailedDocument(e) {
691
+ try {
692
+ const t = JSON.parse(
693
+ localStorage.getItem("es_failed_docs") || "[]"
694
+ );
695
+ t.push({
696
+ document: e,
697
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
698
+ retryCount: 0
699
+ }), localStorage.setItem("es_failed_docs", JSON.stringify(t));
700
+ } catch (t) {
701
+ console.error("Error storing failed document:", t);
702
+ }
703
+ }
704
+ async retryFailedDocuments() {
705
+ try {
706
+ const e = JSON.parse(
707
+ localStorage.getItem("es_failed_docs") || "[]"
708
+ );
709
+ if (e.length === 0) return;
710
+ const t = e.filter((r) => r.retryCount < 3), n = e.filter((r) => r.retryCount >= 3);
711
+ t.forEach((r) => r.retryCount++), await this.sendBulk(t.map((r) => r.document)), localStorage.setItem("es_failed_docs", JSON.stringify(n));
712
+ } catch (e) {
713
+ console.error("Error retrying failed documents:", e);
714
+ }
715
+ }
716
+ startAutoFlush() {
717
+ this.timer = setInterval(() => {
718
+ this.queue.length > 0 && this.flush(), this.retryFailedDocuments();
719
+ }, this.config.flushInterval);
720
+ }
721
+ stopAutoFlush() {
722
+ this.timer && (clearInterval(this.timer), this.timer = null);
723
+ }
724
+ destroy() {
725
+ return this.stopAutoFlush(), this.flush();
726
+ }
727
+ }
728
+ class L {
729
+ constructor({ prefix: e = "track_" }) {
730
+ this.prefix = e;
731
+ }
732
+ setItem(e, t) {
733
+ localStorage.setItem(this.prefix + e, JSON.stringify(t));
734
+ }
735
+ getItem(e) {
736
+ const t = localStorage.getItem(this.prefix + e);
737
+ return t ? JSON.parse(t) : null;
738
+ }
739
+ removeItem(e) {
740
+ localStorage.removeItem(this.prefix + e);
741
+ }
742
+ getSessionId() {
743
+ return this.getItem("session_id");
744
+ }
745
+ setSessionId(e) {
746
+ this.setItem("session_id", e);
747
+ }
748
+ setUser(e) {
749
+ this.setItem("user", e);
750
+ }
751
+ getUser() {
752
+ return this.getItem("user");
753
+ }
754
+ }
755
+ class q {
756
+ constructor(e) {
757
+ this.options = e, this.storage = new L(e), this.device = new O(e), this.sender = e.type === "es" ? new M(e) : new F(e), this.init();
758
+ }
759
+ init() {
760
+ this.sessionId = this.storage.getSessionId() || this.device.generateUUID(), this.storage.setSessionId(this.sessionId), this.deviceInfo = this.device.getInfo(), this.initPlugins();
761
+ }
762
+ setUser(e) {
763
+ this.storage.setUser(e);
764
+ }
765
+ initPlugins() {
766
+ this.options.autoTrack && (this.autoTrack = new A(this)), this.options.enablePerformance && (this.performance = new _(this));
767
+ }
768
+ track(e, t) {
769
+ this.sender.send({
770
+ // type,
771
+ ...t,
772
+ sessionId: this.sessionId
773
+ });
774
+ }
775
+ }
776
+ const N = (o = {}) => (window._trackInstance || (window._trackInstance = new q({
777
+ ...C,
778
+ ...o
779
+ })), window._trackInstance), Y = {
780
+ install: (o, e = {}) => {
781
+ const t = N(e);
782
+ o.config.globalProperties.$track = {
783
+ track(n, r) {
784
+ return t.track(n, r);
785
+ },
786
+ setUser(n) {
787
+ return t.setUser(n);
788
+ },
789
+ clearUser() {
790
+ return t.clearUser();
791
+ },
792
+ trackDeviceInfo() {
793
+ return t.track("device", t.deviceInfo);
794
+ }
795
+ };
796
+ }
797
+ }, H = {
798
+ created() {
799
+ this.$trackInstance = this.$track;
800
+ }
801
+ };
802
+ export {
803
+ H as TrackMixin,
804
+ Y as VueTracker,
805
+ N as default
806
+ };