sunda-tracker 1.0.8 → 1.0.10

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.
@@ -1,806 +1 @@
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
- };
1
+ import e from"axios";function t(e,t=!1){if(!e||"string"!=typeof e)return"";const n=e.split("-");let i=t?n[0].charAt(0).toUpperCase()+n[0].slice(1):n[0];for(let e=1;e<n.length;e++){const t=n[e];t.length>0&&(i+=t.charAt(0).toUpperCase()+t.slice(1))}return i}function n(e){return{_date:new Date,format(e){return function(e=new Date,t="YYYY-MM-DD"){const n=e instanceof Date?e:new Date(e),i=n.getFullYear(),s=n.getMonth()+1,r=n.getDate(),o=n.getHours(),a=n.getMinutes(),c=n.getSeconds(),l={YYYY:i,YY:String(i).slice(-2),MM:s<10?`0${s}`:s,M:s,DD:r<10?`0${r}`:r,D:r,HH:o<10?`0${o}`:o,H:o,mm:a<10?`0${a}`:a,m:a,ss:c<10?`0${c}`:c,s:c};let u=t;for(const[e,t]of Object.entries(l))u=u.replace(new RegExp(e,"g"),t);return u}(this._date,e)}}}function i(e,t,n={}){const{leading:i=!0,trailing:s=!0}=n;let r,o=null,a=null,c=null,l=0;function u(t){const n=a,i=c;return a=c=null,l=t,r=e.apply(i,n),r}function d(e){return o=null,s&&a?u(e):(a=c=null,r)}function h(...e){const n=Date.now(),h=function(e){return 0===l||e-l>=t}(n);if(a=e,c=this,h)return 0!==l||i?u(n):(l=n,r);if(null===o&&s){o=function(e,t){return setTimeout(e,t)}(()=>d(Date.now()),t-(n-l))}return r}return h.cancel=function(){null!==o&&clearTimeout(o),l=0,o=a=c=null},h.flush=function(){return null===o?r:d(Date.now())},h.pending=function(){return null!==o},h}class s{constructor(e){this.track=e,this.init(),this.options=e.options}init(){this.bindClickEvents(),this.observeRouteChanges()}bindClickEvents(){document.addEventListener("click",e=>{const n=e.target.closest("[data-track-type]");if(n){const e=n.getAttribute("data-track-type"),i={};for(const e of n.attributes)if(e.name.startsWith("data-track-args-")){i[t(e.name.replace("data-track-args-",""))]=e.value}e&&this.track.track(e,{...i,sessionId:this.track.sessionId})}})}observeRouteChanges(){}}class r{constructor(e){this.track=e,this.metrics={},this.isSupported=this.checkSupport(),this.isSupported&&this.init()}checkSupport(){return typeof window<"u"&&window.performance&&window.performance.timing&&window.performance.getEntriesByType}init(){this.observePageLoad(),this.observeResources(),this.observeLongTasks(),this.observePaint(),this.observeErrors()}observePageLoad(){window.addEventListener("load",()=>{setTimeout(()=>{const e=performance.getEntriesByType("navigation")[0],t={dnsTime:e.domainLookupEnd-e.domainLookupStart,tcpTime:e.connectEnd-e.connectStart,sslTime:e.secureConnectionStart>0?e.connectEnd-e.secureConnectionStart:0,ttfb:e.responseStart-e.requestStart,responseTime:e.responseEnd-e.responseStart,domParseTime:e.domInteractive-e.responseEnd,domReadyTime:e.domContentLoadedEventEnd-e.startTime,loadTime:e.loadEventEnd-e.startTime};this.track.track("page_performance",t)},0)})}observeResources(){new PerformanceObserver(i(e=>{e.getEntries().forEach(e=>{"fetch"!==e.initiatorType&&"xmlhttprequest"!==e.initiatorType&&this.track.track("resource_performance",{name:e.name,type:e.initiatorType,duration:e.duration,size:e.transferSize,protocol:e.nextHopProtocol})})},3e3)).observe({entryTypes:["resource"]})}observeLongTasks(){typeof PerformanceLongTaskTiming<"u"&&new PerformanceObserver(e=>{e.getEntries().forEach(e=>{this.track.track("long_task",{duration:e.duration,startTime:e.startTime,name:e.name})})}).observe({entryTypes:["longtask"]})}observePaint(){new PerformanceObserver(e=>{e.getEntries().forEach(e=>{this.track.track("paint_timing",{name:e.name,startTime:e.startTime})})}).observe({entryTypes:["paint"]})}observeErrors(){window.addEventListener("error",e=>{this.track.track("js_error",{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno,error:e.error?e.error.stack:null})},!0),window.addEventListener("unhandledrejection",e=>{this.track.track("promise_error",{message:e.reason.message||e.reason,stack:e.reason.stack})})}getFirstScreenTime(){return new Promise(e=>{"complete"===document.readyState?this.calculateFirstScreen(e):window.addEventListener("load",()=>{this.calculateFirstScreen(e)})})}calculateFirstScreen(e){const t=document.querySelectorAll("img");let n=performance.timing.domContentLoadedEventEnd;if(0===t.length)return void e(n);let i=0;const s=()=>{i++,i===t.length&&e(n)};t.forEach(e=>{e.complete?s():(e.addEventListener("load",()=>{const e=performance.now();n=Math.max(n,e),s()}),e.addEventListener("error",s))})}getCurrentMetrics(){return this.isSupported?{...this.metrics,memory:performance.memory?{usedJSHeapSize:performance.memory.usedJSHeapSize,totalJSHeapSize:performance.memory.totalJSHeapSize}:null,navigation:performance.getEntriesByType("navigation")[0],timing:performance.timing}:null}}class o{constructor(){this.parser=this.initUAParser()}initUAParser(){const e=navigator.userAgent;return{os:this.getOS(e),browser:this.getBrowser(e),device:this.getDevice(e)}}generateUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}getOS(e){let t="unknown",n="unknown";if(e.includes("Windows"))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");else if(e.includes("Macintosh")){t="macOS";const i=e.match(/Mac OS X ([0-9_]+)/);i&&(n=i[1].replace(/_/g,"."))}else if(e.includes("iPhone")||e.includes("iPad")){t="iOS";const i=e.match(/OS ([0-9_]+)/);i&&(n=i[1].replace(/_/g,"."))}else if(e.includes("Android")){t="Android";const i=e.match(/Android ([0-9.]+)/);i&&(n=i[1])}else e.includes("Linux")&&(t="Linux");return{name:t,version:n}}getBrowser(e){let t="unknown",n="unknown",i="unknown";if(e.includes("Chrome/")){t="Chrome";const s=e.match(/Chrome\/([0-9.]+)/);s&&(n=s[1]),i="Blink"}else if(e.includes("Firefox/")){t="Firefox";const s=e.match(/Firefox\/([0-9.]+)/);s&&(n=s[1]),i="Gecko"}else if(e.includes("Safari/")&&!e.includes("Chrome/")){t="Safari";const s=e.match(/Version\/([0-9.]+)/);s&&(n=s[1]),i="WebKit"}else if(e.includes("Edg/")){t="Edge";const s=e.match(/Edg\/([0-9.]+)/);s&&(n=s[1]),i="Blink"}else if(e.includes("Trident/")){t="Internet Explorer";const s=e.match(/rv:([0-9.]+)/);s&&(n=s[1]),i="Trident"}return{name:t,version:n,engine:i,engineVersion:"unknown"}}getDevice(e){let t="desktop",n="unknown",i="unknown";const s=e.toLowerCase();if((s.includes("macintosh")||s.includes("mac os"))&&(n="Mac"),s.includes("windows"))return n="PC",{type:t,brand:n,model:i};if(e.includes("Mobile")||e.includes("Android")||e.includes("iPhone"))if(t="mobile",e.includes("iPhone"))n="Apple",i="iPhone";else if(e.includes("iPad"))n="Apple",i="iPad",t="tablet";else if(e.includes("Android")){n="Android";const t={huawei:"Huawei",honor:"Honor",xiaomi:"Xiaomi",redmi:"Xiaomi",poco:"Xiaomi",mi:"Xiaomi",oppo:"OPPO",oneplus:"OnePlus",vivo:"vivo",iqoo:"vivo",realme:"Realme",meizu:"Meizu",zte:"ZTE",lenovo:"Lenovo",samsung:"Samsung","sm-":"Samsung","gt-":"Samsung","sgh-":"Samsung",galaxy:"Samsung",tecno:"TECNO",infinix:"Infinix",itel:"itel",sony:"Sony","sony ericsson":"Sony",lg:"LG",motorola:"Motorola",moto:"Motorola",nokia:"Nokia",htc:"HTC",asus:"ASUS",zenfone:"ASUS",blackberry:"BlackBerry",google:"Google",pixel:"Google",surface:"Surface",nubia:"Nubia",360:"360",smartisan:"Smartisan",gione:"Gionee","k-touch":"K-Touch",micromax:"Micromax",lava:"Lava",karbonn:"Karbonn",sharp:"Sharp",panasonic:"Panasonic"};for(const e in t)if(s.includes(e)){n=t[e];break}if("Android"===n){const i=e.match(/\((.+?)\)/);if(i){const e=i[1].split(";");for(let i=2;i<e.length;i++){const s=e[i]&&e[i].trim().toLowerCase();if(s)for(const e in t)if(s.includes(e)){n=t[e];break}if("Android"!==n)break}}}const r=e.match(/Build\/(.*?)[)\s]/);r&&(i=r[1].trim())}return{type:t,brand:n,model:i}}getInfo(){const e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return{userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language,languages:navigator.languages,os:this.parser.os,browser:this.parser.browser,device:this.parser.device,screen:{width:window.screen.width,height:window.screen.height,availWidth:window.screen.availWidth,availHeight:window.screen.availHeight,colorDepth:window.screen.colorDepth,pixelDepth:window.screen.pixelDepth,orientation:window.screen.orientation?window.screen.orientation.type:"unknown"},window:{innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio},network:e?{type:e.type,effectiveType:e.effectiveType,downlink:e.downlink,downlinkMax:e.downlinkMax,rtt:e.rtt,saveData:e.saveData}:void 0,hardware:{maxTouchPoints:navigator.maxTouchPoints,hardwareConcurrency:navigator.hardwareConcurrency,deviceMemory:navigator.deviceMemory}}}}const a="tracker_failed_docs",c="tracker_pending_queue";class l{constructor(e){this.config={batchSize:100,flushInterval:5e3,...e},this.queue=[],this.timer=null,this.online=navigator.onLine,this.restorePendingQueue(),this.bindNetworkEvents(),this.bindUnloadEvent(),this.online&&this.startAutoFlush()}async sendBatch(e){throw new Error("sendBatch() must be implemented by subclass")}bindNetworkEvents(){this._onOnline=()=>{this.online=!0,this.startAutoFlush(),this.flush(),this.retryFailedDocuments()},this._onOffline=()=>{this.online=!1,this.stopAutoFlush(),this.persistQueue()},window.addEventListener("online",this._onOnline),window.addEventListener("offline",this._onOffline)}bindUnloadEvent(){this._onBeforeUnload=()=>{this.persistQueue()},window.addEventListener("beforeunload",this._onBeforeUnload)}persistQueue(){try{if(0===this.queue.length)return void localStorage.removeItem(c);localStorage.setItem(c,JSON.stringify(this.queue))}catch(e){}}restorePendingQueue(){try{const e=localStorage.getItem(c);if(e){const t=JSON.parse(e);Array.isArray(t)&&t.length>0&&this.queue.unshift(...t),localStorage.removeItem(c)}}catch(e){}}async send(e){this.queue.push({...e,"@timestamp":(new Date).toISOString()}),this.online&&this.queue.length>=this.config.batchSize&&await this.flush()}async sendBulk(e){const t=(new Date).toISOString(),n=e.map(e=>({...e,"@timestamp":t}));this.queue.push(...n),this.online&&this.queue.length>=this.config.batchSize&&await this.flush()}async flush(){if(0===this.queue.length||!this.online)return;const e=[...this.queue];this.queue=[];try{await this.sendBatch(e),localStorage.removeItem(c)}catch(t){this.queue.unshift(...e),this.persistQueue(),this.stopAutoFlush()}}storeFailedDocument(e){try{const t=JSON.parse(localStorage.getItem(a)||"[]");t.push({document:e,timestamp:(new Date).toISOString(),retryCount:0}),localStorage.setItem(a,JSON.stringify(t))}catch(e){}}async retryFailedDocuments(){if(this.online)try{const e=JSON.parse(localStorage.getItem(a)||"[]");if(0===e.length)return;const t=e.filter(e=>e.retryCount<3),n=e.filter(e=>e.retryCount>=3);t.forEach(e=>e.retryCount++),await this.sendBulk(t.map(e=>e.document)),localStorage.setItem(a,JSON.stringify(n))}catch(e){}}startAutoFlush(){this.timer||(this.timer=setInterval(()=>{this.queue.length>0&&this.flush(),this.retryFailedDocuments()},this.config.flushInterval))}stopAutoFlush(){this.timer&&(clearInterval(this.timer),this.timer=null)}destroy(){return this.stopAutoFlush(),this.persistQueue(),window.removeEventListener("online",this._onOnline),window.removeEventListener("offline",this._onOffline),window.removeEventListener("beforeunload",this._onBeforeUnload),this.flush()}}class u extends l{constructor(e){super(e),this.headers={"Content-Type":"application/x-ndjson",Accept:"application/json",Authorization:`ApiKey ${e.encoded}`}}buildBulkBody(e){const t=`${this.config.index}-${n().format("YYYY.MM.DD")}`;return e.flatMap(e=>[JSON.stringify({index:{_index:t}}),JSON.stringify(e)]).join("\n")+"\n"}async sendBatch(e){const t=`${this.config.index}-${n().format("YYYY.MM.DD")}`,i=await fetch(`${this.config.node}/${t}/_bulk`,{method:"POST",headers:this.headers,body:this.buildBulkBody(e)});if(!i.ok)throw new Error(`ES bulk request failed: ${i.status}`);const s=await i.json();s.errors&&s.items.filter(e=>e.index.status>=400).forEach((t,n)=>this.storeFailedDocument(e[n]))}}class d extends l{constructor(e){super(e),this.url=e.collectUrl,this.method=e.collectMethod||"post"}async sendBatch(t){return e({url:this.url,method:this.method,data:t})}}class h{constructor({prefix:e="track_"}){this.prefix=e}setItem(e,t){localStorage.setItem(this.prefix+e,JSON.stringify(t))}getItem(e){const t=localStorage.getItem(this.prefix+e);return t?JSON.parse(t):null}removeItem(e){localStorage.removeItem(this.prefix+e)}getSessionId(){return this.getItem("session_id")}setSessionId(e){this.setItem("session_id",e)}setUser(e){this.setItem("user",e)}getUser(){return this.getItem("user")}}class m{constructor(e){this.options=e,this.storage=new h(e),this.device=new o,this.sender="es"===e.type?new u(e):new d(e),this.init()}init(){this.sessionId=this.storage.getSessionId()||this.device.generateUUID(),this.storage.setSessionId(this.sessionId),this.deviceInfo=this.device.getInfo(),this.initPlugins()}setUser(e){this.storage.setUser(e)}clearUser(){this.storage.removeItem("user")}initPlugins(){this.options.autoTrack&&(this.autoTrack=new s(this)),this.options.enablePerformance&&(this.performance=new r(this))}track(e,t){this.sender.send({...t,sessionId:this.sessionId})}destroy(){return this.sender.destroy()}}const g={serverUrl:"",appId:"",cacheLimit:10,autoTrack:!1,enablePerformance:!1,debug:!1,maxRetryTimes:3,timeout:5e3,type:"api",collectUrl:"",collectMethod:"post"},f=(e={})=>(window._trackInstance||(window._trackInstance=new m({...g,...e})),window._trackInstance),p={install:(e,t={})=>{const n=f(t);e.config.globalProperties.$track={track:(e,t)=>n.track(e,t),setUser:e=>n.setUser(e),clearUser:()=>n.clearUser(),trackDeviceInfo:()=>n.track("device",n.deviceInfo)}}},w={created(){this.$trackInstance=this.$track}};export{w as TrackMixin,p as VueTracker,f as default};