@tracelog/lib 0.11.3 → 0.11.4

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,3040 @@
1
+ const Yt = 120, Kt = 8192, qt = 10, Zt = 10, Jt = 20, er = 1;
2
+ const tr = 1e3, rr = 500, nr = 100;
3
+ const A = "data-tlog", He = [
4
+ "button",
5
+ "a",
6
+ 'input[type="button"]',
7
+ 'input[type="submit"]',
8
+ 'input[type="reset"]',
9
+ 'input[type="checkbox"]',
10
+ 'input[type="radio"]',
11
+ "select",
12
+ "textarea",
13
+ '[role="button"]',
14
+ '[role="link"]',
15
+ '[role="tab"]',
16
+ '[role="menuitem"]',
17
+ '[role="option"]',
18
+ '[role="checkbox"]',
19
+ '[role="radio"]',
20
+ '[role="switch"]',
21
+ "[routerLink]",
22
+ "[ng-click]",
23
+ "[data-action]",
24
+ "[data-click]",
25
+ "[data-navigate]",
26
+ "[data-toggle]",
27
+ "[onclick]",
28
+ ".btn",
29
+ ".button",
30
+ ".clickable",
31
+ ".nav-link",
32
+ ".menu-item",
33
+ "[data-testid]",
34
+ '[tabindex="0"]'
35
+ ], Ue = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"], xe = [
36
+ "token",
37
+ "auth",
38
+ "key",
39
+ "session",
40
+ "reset",
41
+ "password",
42
+ "api_key",
43
+ "apikey",
44
+ "secret",
45
+ "access_token",
46
+ "refresh_token",
47
+ "verification",
48
+ "code",
49
+ "otp"
50
+ ];
51
+ const h = {
52
+ INVALID_SESSION_TIMEOUT: "Session timeout must be between 30000ms (30 seconds) and 86400000ms (24 hours)",
53
+ INVALID_SAMPLING_RATE: "Sampling rate must be between 0 and 1",
54
+ INVALID_ERROR_SAMPLING_RATE: "Error sampling must be between 0 and 1",
55
+ INVALID_TRACELOG_PROJECT_ID: "TraceLog project ID is required when integration is enabled",
56
+ INVALID_CUSTOM_API_URL: "Custom API URL is required when integration is enabled",
57
+ INVALID_GOOGLE_ANALYTICS_ID: "Google Analytics measurement ID is required when integration is enabled",
58
+ INVALID_GLOBAL_METADATA: "Global metadata must be an object",
59
+ INVALID_SENSITIVE_QUERY_PARAMS: "Sensitive query params must be an array of strings",
60
+ INVALID_PRIMARY_SCROLL_SELECTOR: "Primary scroll selector must be a non-empty string",
61
+ INVALID_PRIMARY_SCROLL_SELECTOR_SYNTAX: "Invalid CSS selector syntax for primaryScrollSelector",
62
+ INVALID_PAGE_VIEW_THROTTLE: "Page view throttle must be a non-negative number",
63
+ INVALID_CLICK_THROTTLE: "Click throttle must be a non-negative number",
64
+ INVALID_MAX_SAME_EVENT_PER_MINUTE: "Max same event per minute must be a positive number",
65
+ INVALID_VIEWPORT_CONFIG: "Viewport config must be an object",
66
+ INVALID_VIEWPORT_ELEMENTS: "Viewport elements must be a non-empty array",
67
+ INVALID_VIEWPORT_ELEMENT: "Each viewport element must have a valid selector string",
68
+ INVALID_VIEWPORT_ELEMENT_ID: "Viewport element id must be a non-empty string",
69
+ INVALID_VIEWPORT_ELEMENT_NAME: "Viewport element name must be a non-empty string",
70
+ INVALID_VIEWPORT_THRESHOLD: "Viewport threshold must be a number between 0 and 1",
71
+ INVALID_VIEWPORT_MIN_DWELL_TIME: "Viewport minDwellTime must be a non-negative number",
72
+ INVALID_VIEWPORT_COOLDOWN_PERIOD: "Viewport cooldownPeriod must be a non-negative number",
73
+ INVALID_VIEWPORT_MAX_TRACKED_ELEMENTS: "Viewport maxTrackedElements must be a positive number"
74
+ }, Fe = [
75
+ /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
76
+ /javascript:/gi,
77
+ /on\w+\s*=/gi,
78
+ /<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,
79
+ /<embed\b[^>]*>/gi,
80
+ /<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi
81
+ ];
82
+ var q = /* @__PURE__ */ ((n) => (n.Localhost = "localhost:8080", n.Fail = "localhost:9999", n))(q || {}), y = /* @__PURE__ */ ((n) => (n.Mobile = "mobile", n.Tablet = "tablet", n.Desktop = "desktop", n.Unknown = "unknown", n))(y || {}), Z = /* @__PURE__ */ ((n) => (n.EVENT = "event", n.QUEUE = "queue", n))(Z || {});
83
+ class R extends Error {
84
+ constructor(e, t) {
85
+ super(e), this.statusCode = t, this.name = "PermanentError", Error.captureStackTrace && Error.captureStackTrace(this, R);
86
+ }
87
+ }
88
+ var u = /* @__PURE__ */ ((n) => (n.PAGE_VIEW = "page_view", n.CLICK = "click", n.SCROLL = "scroll", n.SESSION_START = "session_start", n.SESSION_END = "session_end", n.CUSTOM = "custom", n.WEB_VITALS = "web_vitals", n.ERROR = "error", n.VIEWPORT_VISIBLE = "viewport_visible", n))(u || {}), U = /* @__PURE__ */ ((n) => (n.UP = "up", n.DOWN = "down", n))(U || {}), P = /* @__PURE__ */ ((n) => (n.JS_ERROR = "js_error", n.PROMISE_REJECTION = "promise_rejection", n))(P || {}), D = /* @__PURE__ */ ((n) => (n.QA = "qa", n))(D || {});
89
+ function sr(n) {
90
+ return n.type === u.SCROLL && "scroll_data" in n && n.scroll_data.is_primary === !0;
91
+ }
92
+ function ir(n) {
93
+ return n.type === u.SCROLL && "scroll_data" in n && n.scroll_data.is_primary === !1;
94
+ }
95
+ class V extends Error {
96
+ constructor(e, t, r) {
97
+ super(e), this.errorCode = t, this.layer = r, this.name = this.constructor.name, Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
98
+ }
99
+ }
100
+ class f extends V {
101
+ constructor(e, t = "config") {
102
+ super(e, "APP_CONFIG_INVALID", t);
103
+ }
104
+ }
105
+ class Ge extends V {
106
+ constructor(e, t = "config") {
107
+ super(e, "SESSION_TIMEOUT_INVALID", t);
108
+ }
109
+ }
110
+ class he extends V {
111
+ constructor(e, t = "config") {
112
+ super(e, "SAMPLING_RATE_INVALID", t);
113
+ }
114
+ }
115
+ class M extends V {
116
+ constructor(e, t = "config") {
117
+ super(e, "INTEGRATION_INVALID", t);
118
+ }
119
+ }
120
+ class or extends V {
121
+ constructor(e, t, r = "runtime") {
122
+ super(e, "INITIALIZATION_TIMEOUT", r), this.timeoutMs = t;
123
+ }
124
+ }
125
+ const We = (n, e) => {
126
+ if (e) {
127
+ if (e instanceof Error) {
128
+ const t = e.message.replace(/\s+at\s+.*$/gm, "").replace(/\(.*?:\d+:\d+\)/g, "");
129
+ return `[TraceLog] ${n}: ${t}`;
130
+ }
131
+ return `[TraceLog] ${n}: ${e instanceof Error ? e.message : "Unknown error"}`;
132
+ }
133
+ return `[TraceLog] ${n}`;
134
+ }, a = (n, e, t) => {
135
+ const { error: r, data: s, showToClient: i = !1 } = t ?? {}, o = r ? We(e, r) : `[TraceLog] ${e}`, l = n === "error" ? "error" : n === "warn" ? "warn" : "log";
136
+ if (!(n === "debug" || n === "info" && !i))
137
+ if (s !== void 0) {
138
+ const c = Be(s);
139
+ console[l](o, c);
140
+ } else s !== void 0 ? console[l](o, s) : console[l](o);
141
+ }, Be = (n) => {
142
+ const e = {}, t = ["token", "password", "secret", "key", "apikey", "api_key", "sessionid", "session_id"];
143
+ for (const [r, s] of Object.entries(n)) {
144
+ const i = r.toLowerCase();
145
+ t.some((o) => i.includes(o)) ? e[r] = "[REDACTED]" : e[r] = s;
146
+ }
147
+ return e;
148
+ };
149
+ let J, we;
150
+ const Xe = () => {
151
+ typeof window < "u" && !J && (J = window.matchMedia("(pointer: coarse)"), we = window.matchMedia("(hover: none)"));
152
+ }, $e = () => {
153
+ try {
154
+ const n = navigator;
155
+ if (n.userAgentData && typeof n.userAgentData.mobile == "boolean")
156
+ return n.userAgentData.platform && /ipad|tablet/i.test(n.userAgentData.platform) ? y.Tablet : n.userAgentData.mobile ? y.Mobile : y.Desktop;
157
+ Xe();
158
+ const e = window.innerWidth, t = J?.matches ?? !1, r = we?.matches ?? !1, s = "ontouchstart" in window || navigator.maxTouchPoints > 0, i = navigator.userAgent.toLowerCase(), o = /mobile|android|iphone|ipod|blackberry|iemobile|opera mini/.test(i), l = /tablet|ipad|android(?!.*mobile)/.test(i);
159
+ return e <= 767 || o && s ? y.Mobile : e >= 768 && e <= 1024 || l || t && r && s ? y.Tablet : y.Desktop;
160
+ } catch (n) {
161
+ return a("warn", "Device detection failed, defaulting to desktop", { error: n }), y.Desktop;
162
+ }
163
+ }, L = "tlog", fe = `${L}:qa_mode`, ze = `${L}:uid`, Qe = (n) => n ? `${L}:${n}:queue` : `${L}:queue`, je = (n) => n ? `${L}:${n}:session` : `${L}:session`, Ye = (n) => n ? `${L}:${n}:broadcast` : `${L}:broadcast`, ye = {
164
+ LCP: 4e3,
165
+ FCP: 1800,
166
+ CLS: 0.25,
167
+ INP: 200,
168
+ TTFB: 800,
169
+ LONG_TASK: 50
170
+ }, Ke = 1e3, qe = 50, le = [
171
+ // Email addresses
172
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/gi,
173
+ // US Phone numbers (various formats)
174
+ /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g,
175
+ // Credit card numbers (16 digits with optional separators)
176
+ /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g,
177
+ // IBAN (International Bank Account Number)
178
+ /\b[A-Z]{2}\d{2}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/gi,
179
+ // API keys/tokens (sk_test_, sk_live_, pk_test_, pk_live_, etc.)
180
+ /\b[sp]k_(test|live)_[a-zA-Z0-9]{10,}\b/gi,
181
+ // Bearer tokens (JWT-like patterns - matches complete and partial tokens)
182
+ /Bearer\s+[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)?(?:\.[A-Za-z0-9_-]+)?/gi,
183
+ // Passwords in connection strings (protocol://user:password@host)
184
+ /:\/\/[^:/]+:([^@]+)@/gi
185
+ ], Ee = 500, ge = 5e3, x = 50, Ze = x * 2, Le = 1, Je = 1e3, et = 10, me = 5e3, tt = 6e4, Se = "tlog_mode", rt = "qa", nt = () => {
186
+ if (sessionStorage.getItem(fe) === "true")
187
+ return !0;
188
+ const e = new URLSearchParams(window.location.search), r = e.get(Se) === rt;
189
+ if (r) {
190
+ sessionStorage.setItem(fe, "true"), e.delete(Se);
191
+ const s = e.toString(), i = `${window.location.pathname}${s ? "?" + s : ""}${window.location.hash}`;
192
+ try {
193
+ window.history.replaceState({}, "", i);
194
+ } catch (o) {
195
+ a("warn", "History API not available, cannot replace URL", { error: o });
196
+ }
197
+ console.log(
198
+ "%c[TraceLog] QA Mode ACTIVE",
199
+ "background: #ff9800; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;"
200
+ );
201
+ }
202
+ return r;
203
+ }, Te = () => {
204
+ const n = new URLSearchParams(window.location.search), e = {};
205
+ return Ue.forEach((r) => {
206
+ const s = n.get(r);
207
+ if (s) {
208
+ const i = r.split("utm_")[1];
209
+ e[i] = s;
210
+ }
211
+ }), Object.keys(e).length ? e : void 0;
212
+ }, st = () => typeof crypto < "u" && crypto.randomUUID ? crypto.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (n) => {
213
+ const e = Math.random() * 16 | 0;
214
+ return (n === "x" ? e : e & 3 | 8).toString(16);
215
+ }), it = () => {
216
+ const n = Date.now();
217
+ let e = "";
218
+ try {
219
+ if (typeof crypto < "u" && crypto.getRandomValues) {
220
+ const t = crypto.getRandomValues(new Uint8Array(4));
221
+ t && (e = Array.from(t, (r) => r.toString(16).padStart(2, "0")).join(""));
222
+ }
223
+ } catch {
224
+ }
225
+ return e || (e = Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0")), `${n}-${e}`;
226
+ }, _e = (n, e = !1) => {
227
+ try {
228
+ const t = new URL(n), r = t.protocol === "https:", s = t.protocol === "http:";
229
+ return r || e && s;
230
+ } catch {
231
+ return !1;
232
+ }
233
+ }, ot = (n) => {
234
+ if (n.integrations?.tracelog?.projectId)
235
+ try {
236
+ const r = new URL(window.location.href).hostname;
237
+ if (!r || typeof r != "string")
238
+ throw new Error("Invalid hostname");
239
+ const s = r.split(".");
240
+ if (!s || !Array.isArray(s) || s.length === 0 || s.length === 1 && s[0] === "")
241
+ throw new Error("Invalid hostname structure");
242
+ const i = n.integrations.tracelog.projectId, o = s.slice(-2).join(".");
243
+ if (!o)
244
+ throw new Error("Invalid domain");
245
+ const l = `https://${i}.${o}/collect`;
246
+ if (!_e(l))
247
+ throw new Error("Invalid URL");
248
+ return l;
249
+ } catch (t) {
250
+ throw new Error(`Invalid URL configuration: ${t instanceof Error ? t.message : String(t)}`);
251
+ }
252
+ const e = n.integrations?.custom?.collectApiUrl;
253
+ if (e) {
254
+ const t = n.integrations?.custom?.allowHttp ?? !1;
255
+ if (!_e(e, t))
256
+ throw new Error("Invalid URL");
257
+ return e;
258
+ }
259
+ return "";
260
+ }, ee = (n, e = []) => {
261
+ if (!n || typeof n != "string")
262
+ return a("warn", "Invalid URL provided to normalizeUrl", { data: { url: String(n) } }), n || "";
263
+ try {
264
+ const t = new URL(n), r = t.searchParams, s = [.../* @__PURE__ */ new Set([...xe, ...e])];
265
+ let i = !1;
266
+ const o = [];
267
+ return s.forEach((c) => {
268
+ r.has(c) && (r.delete(c), i = !0, o.push(c));
269
+ }), !i && n.includes("?") ? n : (t.search = r.toString(), t.toString());
270
+ } catch (t) {
271
+ const r = n && typeof n == "string" ? n.slice(0, 100) : String(n);
272
+ return a("warn", "URL normalization failed, returning original", { error: t, data: { url: r } }), n;
273
+ }
274
+ }, pe = (n) => {
275
+ if (!n || typeof n != "string" || n.trim().length === 0)
276
+ return "";
277
+ let e = n;
278
+ n.length > 1e3 && (e = n.slice(0, Math.max(0, 1e3)));
279
+ let t = 0;
280
+ for (const s of Fe) {
281
+ const i = e;
282
+ e = e.replace(s, ""), i !== e && t++;
283
+ }
284
+ return t > 0 && a("warn", "XSS patterns detected and removed", {
285
+ data: {
286
+ patternMatches: t,
287
+ originalValue: n.slice(0, 100)
288
+ }
289
+ }), e = e.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#x27;").replaceAll("/", "&#x2F;"), e.trim();
290
+ }, te = (n, e = 0) => {
291
+ if (e > 3 || n == null)
292
+ return null;
293
+ if (typeof n == "string")
294
+ return pe(n);
295
+ if (typeof n == "number")
296
+ return !Number.isFinite(n) || n < -Number.MAX_SAFE_INTEGER || n > Number.MAX_SAFE_INTEGER ? 0 : n;
297
+ if (typeof n == "boolean")
298
+ return n;
299
+ if (Array.isArray(n))
300
+ return n.slice(0, 100).map((s) => te(s, e + 1)).filter((s) => s !== null);
301
+ if (typeof n == "object") {
302
+ const t = {}, s = Object.entries(n).slice(0, 20);
303
+ for (const [i, o] of s) {
304
+ const l = pe(i);
305
+ if (l) {
306
+ const c = te(o, e + 1);
307
+ c !== null && (t[l] = c);
308
+ }
309
+ }
310
+ return t;
311
+ }
312
+ return null;
313
+ }, at = (n) => {
314
+ if (typeof n != "object" || n === null)
315
+ return {};
316
+ try {
317
+ const e = te(n);
318
+ return typeof e == "object" && e !== null ? e : {};
319
+ } catch (e) {
320
+ const t = e instanceof Error ? e.message : String(e);
321
+ throw new Error(`[TraceLog] Metadata sanitization failed: ${t}`);
322
+ }
323
+ }, lt = (n) => {
324
+ if (n !== void 0 && (n === null || typeof n != "object"))
325
+ throw new f("Configuration must be an object", "config");
326
+ if (n) {
327
+ if (n.sessionTimeout !== void 0 && (typeof n.sessionTimeout != "number" || n.sessionTimeout < 3e4 || n.sessionTimeout > 864e5))
328
+ throw new Ge(h.INVALID_SESSION_TIMEOUT, "config");
329
+ if (n.globalMetadata !== void 0 && (typeof n.globalMetadata != "object" || n.globalMetadata === null))
330
+ throw new f(h.INVALID_GLOBAL_METADATA, "config");
331
+ if (n.integrations && ut(n.integrations), n.sensitiveQueryParams !== void 0) {
332
+ if (!Array.isArray(n.sensitiveQueryParams))
333
+ throw new f(h.INVALID_SENSITIVE_QUERY_PARAMS, "config");
334
+ for (const e of n.sensitiveQueryParams)
335
+ if (typeof e != "string")
336
+ throw new f("All sensitive query params must be strings", "config");
337
+ }
338
+ if (n.errorSampling !== void 0 && (typeof n.errorSampling != "number" || n.errorSampling < 0 || n.errorSampling > 1))
339
+ throw new he(h.INVALID_ERROR_SAMPLING_RATE, "config");
340
+ if (n.samplingRate !== void 0 && (typeof n.samplingRate != "number" || n.samplingRate < 0 || n.samplingRate > 1))
341
+ throw new he(h.INVALID_SAMPLING_RATE, "config");
342
+ if (n.primaryScrollSelector !== void 0) {
343
+ if (typeof n.primaryScrollSelector != "string" || !n.primaryScrollSelector.trim())
344
+ throw new f(h.INVALID_PRIMARY_SCROLL_SELECTOR, "config");
345
+ if (n.primaryScrollSelector !== "window")
346
+ try {
347
+ document.querySelector(n.primaryScrollSelector);
348
+ } catch {
349
+ throw new f(
350
+ `${h.INVALID_PRIMARY_SCROLL_SELECTOR_SYNTAX}: "${n.primaryScrollSelector}"`,
351
+ "config"
352
+ );
353
+ }
354
+ }
355
+ if (n.pageViewThrottleMs !== void 0 && (typeof n.pageViewThrottleMs != "number" || n.pageViewThrottleMs < 0))
356
+ throw new f(h.INVALID_PAGE_VIEW_THROTTLE, "config");
357
+ if (n.clickThrottleMs !== void 0 && (typeof n.clickThrottleMs != "number" || n.clickThrottleMs < 0))
358
+ throw new f(h.INVALID_CLICK_THROTTLE, "config");
359
+ if (n.maxSameEventPerMinute !== void 0 && (typeof n.maxSameEventPerMinute != "number" || n.maxSameEventPerMinute <= 0))
360
+ throw new f(h.INVALID_MAX_SAME_EVENT_PER_MINUTE, "config");
361
+ n.viewport !== void 0 && ct(n.viewport);
362
+ }
363
+ }, ct = (n) => {
364
+ if (typeof n != "object" || n === null)
365
+ throw new f(h.INVALID_VIEWPORT_CONFIG, "config");
366
+ if (!n.elements || !Array.isArray(n.elements))
367
+ throw new f(h.INVALID_VIEWPORT_ELEMENTS, "config");
368
+ if (n.elements.length === 0)
369
+ throw new f(h.INVALID_VIEWPORT_ELEMENTS, "config");
370
+ const e = /* @__PURE__ */ new Set();
371
+ for (const t of n.elements) {
372
+ if (!t.selector || typeof t.selector != "string" || !t.selector.trim())
373
+ throw new f(h.INVALID_VIEWPORT_ELEMENT, "config");
374
+ const r = t.selector.trim();
375
+ if (e.has(r))
376
+ throw new f(
377
+ `Duplicate viewport selector found: "${r}". Each selector should appear only once.`,
378
+ "config"
379
+ );
380
+ if (e.add(r), t.id !== void 0 && (typeof t.id != "string" || !t.id.trim()))
381
+ throw new f(h.INVALID_VIEWPORT_ELEMENT_ID, "config");
382
+ if (t.name !== void 0 && (typeof t.name != "string" || !t.name.trim()))
383
+ throw new f(h.INVALID_VIEWPORT_ELEMENT_NAME, "config");
384
+ }
385
+ if (n.threshold !== void 0 && (typeof n.threshold != "number" || n.threshold < 0 || n.threshold > 1))
386
+ throw new f(h.INVALID_VIEWPORT_THRESHOLD, "config");
387
+ if (n.minDwellTime !== void 0 && (typeof n.minDwellTime != "number" || n.minDwellTime < 0))
388
+ throw new f(h.INVALID_VIEWPORT_MIN_DWELL_TIME, "config");
389
+ if (n.cooldownPeriod !== void 0 && (typeof n.cooldownPeriod != "number" || n.cooldownPeriod < 0))
390
+ throw new f(h.INVALID_VIEWPORT_COOLDOWN_PERIOD, "config");
391
+ if (n.maxTrackedElements !== void 0 && (typeof n.maxTrackedElements != "number" || n.maxTrackedElements <= 0))
392
+ throw new f(h.INVALID_VIEWPORT_MAX_TRACKED_ELEMENTS, "config");
393
+ }, ut = (n) => {
394
+ if (n) {
395
+ if (n.tracelog && (!n.tracelog.projectId || typeof n.tracelog.projectId != "string" || n.tracelog.projectId.trim() === ""))
396
+ throw new M(h.INVALID_TRACELOG_PROJECT_ID, "config");
397
+ if (n.custom) {
398
+ if (!n.custom.collectApiUrl || typeof n.custom.collectApiUrl != "string" || n.custom.collectApiUrl.trim() === "")
399
+ throw new M(h.INVALID_CUSTOM_API_URL, "config");
400
+ if (n.custom.allowHttp !== void 0 && typeof n.custom.allowHttp != "boolean")
401
+ throw new M("allowHttp must be a boolean", "config");
402
+ const e = n.custom.collectApiUrl.trim();
403
+ if (!e.startsWith("http://") && !e.startsWith("https://"))
404
+ throw new M('Custom API URL must start with "http://" or "https://"', "config");
405
+ if (!(n.custom.allowHttp ?? !1) && e.startsWith("http://"))
406
+ throw new M(
407
+ "Custom API URL must use HTTPS in production. Set allowHttp: true in integration config to allow HTTP (not recommended)",
408
+ "config"
409
+ );
410
+ }
411
+ if (n.googleAnalytics) {
412
+ if (!n.googleAnalytics.measurementId || typeof n.googleAnalytics.measurementId != "string" || n.googleAnalytics.measurementId.trim() === "")
413
+ throw new M(h.INVALID_GOOGLE_ANALYTICS_ID, "config");
414
+ if (!n.googleAnalytics.measurementId.trim().match(/^(G-|UA-)/))
415
+ throw new M('Google Analytics measurement ID must start with "G-" or "UA-"', "config");
416
+ }
417
+ }
418
+ }, dt = (n) => {
419
+ lt(n);
420
+ const e = {
421
+ ...n ?? {},
422
+ sessionTimeout: n?.sessionTimeout ?? 9e5,
423
+ globalMetadata: n?.globalMetadata ?? {},
424
+ sensitiveQueryParams: n?.sensitiveQueryParams ?? [],
425
+ errorSampling: n?.errorSampling ?? Le,
426
+ samplingRate: n?.samplingRate ?? 1,
427
+ pageViewThrottleMs: n?.pageViewThrottleMs ?? 1e3,
428
+ clickThrottleMs: n?.clickThrottleMs ?? 300,
429
+ maxSameEventPerMinute: n?.maxSameEventPerMinute ?? 60
430
+ };
431
+ return e.integrations?.custom && (e.integrations.custom = {
432
+ ...e.integrations.custom,
433
+ allowHttp: e.integrations.custom.allowHttp ?? !1
434
+ }), e.viewport && (e.viewport = {
435
+ ...e.viewport,
436
+ threshold: e.viewport.threshold ?? 0.5,
437
+ minDwellTime: e.viewport.minDwellTime ?? 2e3,
438
+ cooldownPeriod: e.viewport.cooldownPeriod ?? 6e4,
439
+ maxTrackedElements: e.viewport.maxTrackedElements ?? 100
440
+ }), e;
441
+ }, ht = (n) => {
442
+ if (typeof n == "string")
443
+ return !0;
444
+ if (typeof n == "object" && n !== null && !Array.isArray(n)) {
445
+ const e = Object.entries(n);
446
+ if (e.length > 20)
447
+ return !1;
448
+ for (const [, t] of e) {
449
+ if (t == null)
450
+ continue;
451
+ const r = typeof t;
452
+ if (r !== "string" && r !== "number" && r !== "boolean")
453
+ return !1;
454
+ }
455
+ return !0;
456
+ }
457
+ return !1;
458
+ }, Me = (n, e = 0) => {
459
+ if (typeof n != "object" || n === null || e > 1)
460
+ return !1;
461
+ for (const t of Object.values(n)) {
462
+ if (t == null)
463
+ continue;
464
+ const r = typeof t;
465
+ if (!(r === "string" || r === "number" || r === "boolean")) {
466
+ if (Array.isArray(t)) {
467
+ if (t.length === 0)
468
+ continue;
469
+ if (typeof t[0] == "string") {
470
+ if (!t.every((o) => typeof o == "string"))
471
+ return !1;
472
+ } else if (!t.every((o) => ht(o)))
473
+ return !1;
474
+ continue;
475
+ }
476
+ if (r === "object" && e === 0) {
477
+ if (!Me(t, e + 1))
478
+ return !1;
479
+ continue;
480
+ }
481
+ return !1;
482
+ }
483
+ }
484
+ return !0;
485
+ }, ft = (n) => typeof n != "string" ? {
486
+ valid: !1,
487
+ error: "Event name must be a string"
488
+ } : n.length === 0 ? {
489
+ valid: !1,
490
+ error: "Event name cannot be empty"
491
+ } : n.length > 120 ? {
492
+ valid: !1,
493
+ error: "Event name is too long (max 120 characters)"
494
+ } : n.includes("<") || n.includes(">") || n.includes("&") ? {
495
+ valid: !1,
496
+ error: "Event name contains invalid characters"
497
+ } : ["constructor", "prototype", "__proto__", "eval", "function", "var", "let", "const"].includes(n.toLowerCase()) ? {
498
+ valid: !1,
499
+ error: "Event name cannot be a reserved word"
500
+ } : { valid: !0 }, Ie = (n, e, t) => {
501
+ const r = at(e), s = `${t} "${n}" metadata error`;
502
+ if (!Me(r))
503
+ return {
504
+ valid: !1,
505
+ error: `${s}: object has invalid types. Valid types are string, number, boolean or string arrays.`
506
+ };
507
+ let i;
508
+ try {
509
+ i = JSON.stringify(r);
510
+ } catch {
511
+ return {
512
+ valid: !1,
513
+ error: `${s}: object contains circular references or cannot be serialized.`
514
+ };
515
+ }
516
+ if (i.length > 8192)
517
+ return {
518
+ valid: !1,
519
+ error: `${s}: object is too large (max ${8192 / 1024} KB).`
520
+ };
521
+ if (Object.keys(r).length > 10)
522
+ return {
523
+ valid: !1,
524
+ error: `${s}: object has too many keys (max 10 keys).`
525
+ };
526
+ for (const [l, c] of Object.entries(r)) {
527
+ if (Array.isArray(c)) {
528
+ if (c.length > 10)
529
+ return {
530
+ valid: !1,
531
+ error: `${s}: array property "${l}" is too large (max 10 items).`
532
+ };
533
+ for (const d of c)
534
+ if (typeof d == "string" && d.length > 500)
535
+ return {
536
+ valid: !1,
537
+ error: `${s}: array property "${l}" contains strings that are too long (max 500 characters).`
538
+ };
539
+ }
540
+ if (typeof c == "string" && c.length > 1e3)
541
+ return {
542
+ valid: !1,
543
+ error: `${s}: property "${l}" is too long (max 1000 characters).`
544
+ };
545
+ }
546
+ return {
547
+ valid: !0,
548
+ sanitizedMetadata: r
549
+ };
550
+ }, Et = (n, e, t) => {
551
+ if (Array.isArray(e)) {
552
+ const r = [], s = `${t} "${n}" metadata error`;
553
+ for (let i = 0; i < e.length; i++) {
554
+ const o = e[i];
555
+ if (typeof o != "object" || o === null || Array.isArray(o))
556
+ return {
557
+ valid: !1,
558
+ error: `${s}: array item at index ${i} must be an object.`
559
+ };
560
+ const l = Ie(n, o, t);
561
+ if (!l.valid)
562
+ return {
563
+ valid: !1,
564
+ error: `${s}: array item at index ${i} is invalid: ${l.error}`
565
+ };
566
+ l.sanitizedMetadata && r.push(l.sanitizedMetadata);
567
+ }
568
+ return {
569
+ valid: !0,
570
+ sanitizedMetadata: r
571
+ };
572
+ }
573
+ return Ie(n, e, t);
574
+ }, gt = (n, e) => {
575
+ const t = ft(n);
576
+ if (!t.valid)
577
+ return a("error", "Event name validation failed", {
578
+ showToClient: !0,
579
+ data: { eventName: n, error: t.error }
580
+ }), t;
581
+ if (!e)
582
+ return { valid: !0 };
583
+ const r = Et(n, e, "customEvent");
584
+ return r.valid || a("error", "Event metadata validation failed", {
585
+ showToClient: !0,
586
+ data: {
587
+ eventName: n,
588
+ error: r.error
589
+ }
590
+ }), r;
591
+ };
592
+ class mt {
593
+ listeners = /* @__PURE__ */ new Map();
594
+ on(e, t) {
595
+ this.listeners.has(e) || this.listeners.set(e, []), this.listeners.get(e).push(t);
596
+ }
597
+ off(e, t) {
598
+ const r = this.listeners.get(e);
599
+ if (r) {
600
+ const s = r.indexOf(t);
601
+ s > -1 && r.splice(s, 1);
602
+ }
603
+ }
604
+ emit(e, t) {
605
+ const r = this.listeners.get(e);
606
+ r && r.forEach((s) => {
607
+ s(t);
608
+ });
609
+ }
610
+ removeAllListeners() {
611
+ this.listeners.clear();
612
+ }
613
+ }
614
+ const j = {};
615
+ class S {
616
+ get(e) {
617
+ return j[e];
618
+ }
619
+ set(e, t) {
620
+ j[e] = t;
621
+ }
622
+ getState() {
623
+ return { ...j };
624
+ }
625
+ }
626
+ class St extends S {
627
+ storeManager;
628
+ lastPermanentErrorLog = null;
629
+ recoveryInProgress = !1;
630
+ constructor(e) {
631
+ super(), this.storeManager = e;
632
+ }
633
+ getQueueStorageKey() {
634
+ const e = this.get("userId") || "anonymous";
635
+ return Qe(e);
636
+ }
637
+ sendEventsQueueSync(e) {
638
+ return this.shouldSkipSend() ? !0 : this.get("config")?.integrations?.custom?.collectApiUrl === q.Fail ? (a("warn", "Fail mode: simulating network failure (sync)", {
639
+ data: { events: e.events.length }
640
+ }), !1) : this.sendQueueSyncInternal(e);
641
+ }
642
+ async sendEventsQueue(e, t) {
643
+ try {
644
+ const r = await this.send(e);
645
+ return r ? (this.clearPersistedEvents(), t?.onSuccess?.(e.events.length, e.events, e)) : (this.persistEvents(e), t?.onFailure?.()), r;
646
+ } catch (r) {
647
+ return r instanceof R ? (this.logPermanentError("Permanent error, not retrying", r), this.clearPersistedEvents(), t?.onFailure?.(), !1) : (this.persistEvents(e), t?.onFailure?.(), !1);
648
+ }
649
+ }
650
+ async recoverPersistedEvents(e) {
651
+ if (this.recoveryInProgress) {
652
+ a("debug", "Recovery already in progress, skipping duplicate attempt");
653
+ return;
654
+ }
655
+ this.recoveryInProgress = !0;
656
+ try {
657
+ const t = this.getPersistedData();
658
+ if (!t || !this.isDataRecent(t) || t.events.length === 0) {
659
+ this.clearPersistedEvents();
660
+ return;
661
+ }
662
+ const r = this.createRecoveryBody(t);
663
+ await this.send(r) ? (this.clearPersistedEvents(), e?.onSuccess?.(t.events.length, t.events, r)) : e?.onFailure?.();
664
+ } catch (t) {
665
+ if (t instanceof R) {
666
+ this.logPermanentError("Permanent error during recovery, clearing persisted events", t), this.clearPersistedEvents(), e?.onFailure?.();
667
+ return;
668
+ }
669
+ a("error", "Failed to recover persisted events", { error: t });
670
+ } finally {
671
+ this.recoveryInProgress = !1;
672
+ }
673
+ }
674
+ stop() {
675
+ }
676
+ async send(e) {
677
+ if (this.shouldSkipSend())
678
+ return this.simulateSuccessfulSend();
679
+ if (this.get("config")?.integrations?.custom?.collectApiUrl === q.Fail)
680
+ return a("warn", "Fail mode: simulating network failure", {
681
+ data: { events: e.events.length }
682
+ }), !1;
683
+ const { url: r, payload: s } = this.prepareRequest(e);
684
+ try {
685
+ return (await this.sendWithTimeout(r, s)).ok;
686
+ } catch (i) {
687
+ if (i instanceof R)
688
+ throw i;
689
+ return a("error", "Send request failed", {
690
+ error: i,
691
+ data: {
692
+ events: e.events.length,
693
+ url: r.replace(/\/\/[^/]+/, "//[DOMAIN]")
694
+ }
695
+ }), !1;
696
+ }
697
+ }
698
+ async sendWithTimeout(e, t) {
699
+ const r = new AbortController(), s = setTimeout(() => {
700
+ r.abort();
701
+ }, 1e4);
702
+ try {
703
+ const i = await fetch(e, {
704
+ method: "POST",
705
+ body: t,
706
+ keepalive: !0,
707
+ credentials: "include",
708
+ signal: r.signal,
709
+ headers: {
710
+ "Content-Type": "application/json"
711
+ }
712
+ });
713
+ if (!i.ok)
714
+ throw i.status >= 400 && i.status < 500 ? new R(`HTTP ${i.status}: ${i.statusText}`, i.status) : new Error(`HTTP ${i.status}: ${i.statusText}`);
715
+ return i;
716
+ } finally {
717
+ clearTimeout(s);
718
+ }
719
+ }
720
+ sendQueueSyncInternal(e) {
721
+ const { url: t, payload: r } = this.prepareRequest(e);
722
+ if (r.length > 65536)
723
+ return a("warn", "Payload exceeds sendBeacon limit, persisting for recovery", {
724
+ data: {
725
+ size: r.length,
726
+ limit: 65536,
727
+ events: e.events.length
728
+ }
729
+ }), this.persistEvents(e), !1;
730
+ const s = new Blob([r], { type: "application/json" });
731
+ if (!this.isSendBeaconAvailable())
732
+ return a("warn", "sendBeacon not available, persisting events for recovery"), this.persistEvents(e), !1;
733
+ const i = navigator.sendBeacon(t, s);
734
+ return i || (a("warn", "sendBeacon rejected request, persisting events for recovery"), this.persistEvents(e)), i;
735
+ }
736
+ prepareRequest(e) {
737
+ const t = {
738
+ ...e,
739
+ _metadata: {
740
+ referer: typeof window < "u" ? window.location.href : void 0,
741
+ timestamp: Date.now()
742
+ }
743
+ };
744
+ return {
745
+ url: this.get("collectApiUrl"),
746
+ payload: JSON.stringify(t)
747
+ };
748
+ }
749
+ getPersistedData() {
750
+ try {
751
+ const e = this.getQueueStorageKey(), t = this.storeManager.getItem(e);
752
+ if (t)
753
+ return JSON.parse(t);
754
+ } catch (e) {
755
+ a("warn", "Failed to parse persisted data", { error: e }), this.clearPersistedEvents();
756
+ }
757
+ return null;
758
+ }
759
+ isDataRecent(e) {
760
+ return !e.timestamp || typeof e.timestamp != "number" ? !1 : (Date.now() - e.timestamp) / (1e3 * 60 * 60) < 2;
761
+ }
762
+ createRecoveryBody(e) {
763
+ const { timestamp: t, ...r } = e;
764
+ return r;
765
+ }
766
+ persistEvents(e) {
767
+ try {
768
+ const t = this.getPersistedData();
769
+ if (t && t.timestamp) {
770
+ const i = Date.now() - t.timestamp;
771
+ if (i < 1e3)
772
+ return a("debug", "Skipping persistence, another tab recently persisted events", {
773
+ data: { timeSinceExisting: i }
774
+ }), !0;
775
+ }
776
+ const r = {
777
+ ...e,
778
+ timestamp: Date.now()
779
+ }, s = this.getQueueStorageKey();
780
+ return this.storeManager.setItem(s, JSON.stringify(r)), !!this.storeManager.getItem(s);
781
+ } catch (t) {
782
+ return a("warn", "Failed to persist events", { error: t }), !1;
783
+ }
784
+ }
785
+ clearPersistedEvents() {
786
+ try {
787
+ const e = this.getQueueStorageKey();
788
+ this.storeManager.removeItem(e);
789
+ } catch (e) {
790
+ a("warn", "Failed to clear persisted events", { error: e });
791
+ }
792
+ }
793
+ shouldSkipSend() {
794
+ return !this.get("collectApiUrl");
795
+ }
796
+ async simulateSuccessfulSend() {
797
+ const e = Math.random() * 400 + 100;
798
+ return await new Promise((t) => setTimeout(t, e)), !0;
799
+ }
800
+ isSendBeaconAvailable() {
801
+ return typeof navigator < "u" && typeof navigator.sendBeacon == "function";
802
+ }
803
+ logPermanentError(e, t) {
804
+ const r = Date.now();
805
+ (!this.lastPermanentErrorLog || this.lastPermanentErrorLog.statusCode !== t.statusCode || r - this.lastPermanentErrorLog.timestamp >= tt) && (a("error", e, {
806
+ data: { status: t.statusCode, message: t.message }
807
+ }), this.lastPermanentErrorLog = { statusCode: t.statusCode, timestamp: r });
808
+ }
809
+ }
810
+ class Tt extends S {
811
+ googleAnalytics;
812
+ dataSender;
813
+ emitter;
814
+ eventsQueue = [];
815
+ pendingEventsBuffer = [];
816
+ recentEventFingerprints = /* @__PURE__ */ new Map();
817
+ // Time-based deduplication cache
818
+ sendIntervalId = null;
819
+ rateLimitCounter = 0;
820
+ rateLimitWindowStart = 0;
821
+ perEventRateLimits = /* @__PURE__ */ new Map();
822
+ sessionEventCounts = {
823
+ total: 0,
824
+ [u.CLICK]: 0,
825
+ [u.PAGE_VIEW]: 0,
826
+ [u.CUSTOM]: 0,
827
+ [u.VIEWPORT_VISIBLE]: 0,
828
+ [u.SCROLL]: 0
829
+ };
830
+ lastSessionId = null;
831
+ constructor(e, t = null, r = null) {
832
+ super(), this.googleAnalytics = t, this.dataSender = new St(e), this.emitter = r;
833
+ }
834
+ async recoverPersistedEvents() {
835
+ await this.dataSender.recoverPersistedEvents({
836
+ onSuccess: (e, t, r) => {
837
+ if (t && t.length > 0) {
838
+ const s = t.map((i) => i.id);
839
+ this.removeProcessedEvents(s), r && this.emitEventsQueue(r);
840
+ }
841
+ },
842
+ onFailure: () => {
843
+ a("warn", "Failed to recover persisted events");
844
+ }
845
+ });
846
+ }
847
+ track({
848
+ type: e,
849
+ page_url: t,
850
+ from_page_url: r,
851
+ scroll_data: s,
852
+ click_data: i,
853
+ custom_event: o,
854
+ web_vitals: l,
855
+ error_data: c,
856
+ session_end_reason: d,
857
+ viewport_data: T
858
+ }) {
859
+ if (!e) {
860
+ a("error", "Event type is required - event will be ignored");
861
+ return;
862
+ }
863
+ const g = this.get("sessionId");
864
+ if (!g) {
865
+ this.pendingEventsBuffer.length >= 100 && (this.pendingEventsBuffer.shift(), a("warn", "Pending events buffer full - dropping oldest event", {
866
+ data: { maxBufferSize: 100 }
867
+ })), this.pendingEventsBuffer.push({
868
+ type: e,
869
+ page_url: t,
870
+ from_page_url: r,
871
+ scroll_data: s,
872
+ click_data: i,
873
+ custom_event: o,
874
+ web_vitals: l,
875
+ error_data: c,
876
+ session_end_reason: d,
877
+ viewport_data: T
878
+ });
879
+ return;
880
+ }
881
+ this.lastSessionId !== g && (this.lastSessionId = g, this.sessionEventCounts = {
882
+ total: 0,
883
+ [u.CLICK]: 0,
884
+ [u.PAGE_VIEW]: 0,
885
+ [u.CUSTOM]: 0,
886
+ [u.VIEWPORT_VISIBLE]: 0,
887
+ [u.SCROLL]: 0
888
+ });
889
+ const v = e === u.SESSION_START || e === u.SESSION_END;
890
+ if (!v && !this.checkRateLimit())
891
+ return;
892
+ const m = e;
893
+ if (!v) {
894
+ if (this.sessionEventCounts.total >= 1e3) {
895
+ a("warn", "Session event limit reached", {
896
+ data: {
897
+ type: m,
898
+ total: this.sessionEventCounts.total,
899
+ limit: 1e3
900
+ }
901
+ });
902
+ return;
903
+ }
904
+ const w = this.getTypeLimitForEvent(m);
905
+ if (w) {
906
+ const Q = this.sessionEventCounts[m];
907
+ if (Q !== void 0 && Q >= w) {
908
+ a("warn", "Session event type limit reached", {
909
+ data: {
910
+ type: m,
911
+ count: Q,
912
+ limit: w
913
+ }
914
+ });
915
+ return;
916
+ }
917
+ }
918
+ }
919
+ if (m === u.CUSTOM && o?.name) {
920
+ const w = this.get("config")?.maxSameEventPerMinute ?? 60;
921
+ if (!this.checkPerEventRateLimit(o.name, w))
922
+ return;
923
+ }
924
+ const Ve = m === u.SESSION_START, ke = t || this.get("pageUrl"), z = this.buildEventPayload({
925
+ type: m,
926
+ page_url: ke,
927
+ from_page_url: r,
928
+ scroll_data: s,
929
+ click_data: i,
930
+ custom_event: o,
931
+ web_vitals: l,
932
+ error_data: c,
933
+ session_end_reason: d,
934
+ viewport_data: T
935
+ });
936
+ if (!(!v && !this.shouldSample())) {
937
+ if (Ve) {
938
+ const w = this.get("sessionId");
939
+ if (!w) {
940
+ a("error", "Session start event requires sessionId - event will be ignored");
941
+ return;
942
+ }
943
+ if (this.get("hasStartSession")) {
944
+ a("warn", "Duplicate session_start detected", {
945
+ data: { sessionId: w }
946
+ });
947
+ return;
948
+ }
949
+ this.set("hasStartSession", !0);
950
+ }
951
+ if (!this.isDuplicateEvent(z)) {
952
+ if (this.get("mode") === D.QA && m === u.CUSTOM && o) {
953
+ console.log("[TraceLog] Event", {
954
+ name: o.name,
955
+ ...o.metadata && { metadata: o.metadata }
956
+ }), this.emitEvent(z);
957
+ return;
958
+ }
959
+ this.addToQueue(z), v || (this.sessionEventCounts.total++, this.sessionEventCounts[m] !== void 0 && this.sessionEventCounts[m]++);
960
+ }
961
+ }
962
+ }
963
+ stop() {
964
+ this.sendIntervalId && (clearInterval(this.sendIntervalId), this.sendIntervalId = null), this.eventsQueue = [], this.pendingEventsBuffer = [], this.recentEventFingerprints.clear(), this.rateLimitCounter = 0, this.rateLimitWindowStart = 0, this.perEventRateLimits.clear(), this.sessionEventCounts = {
965
+ total: 0,
966
+ [u.CLICK]: 0,
967
+ [u.PAGE_VIEW]: 0,
968
+ [u.CUSTOM]: 0,
969
+ [u.VIEWPORT_VISIBLE]: 0,
970
+ [u.SCROLL]: 0
971
+ }, this.lastSessionId = null, this.dataSender.stop();
972
+ }
973
+ async flushImmediately() {
974
+ return this.flushEvents(!1);
975
+ }
976
+ flushImmediatelySync() {
977
+ return this.flushEvents(!0);
978
+ }
979
+ getQueueLength() {
980
+ return this.eventsQueue.length;
981
+ }
982
+ flushPendingEvents() {
983
+ if (this.pendingEventsBuffer.length === 0)
984
+ return;
985
+ if (!this.get("sessionId")) {
986
+ a("warn", "Cannot flush pending events: session not initialized - keeping in buffer", {
987
+ data: { bufferedEventCount: this.pendingEventsBuffer.length }
988
+ });
989
+ return;
990
+ }
991
+ const t = [...this.pendingEventsBuffer];
992
+ this.pendingEventsBuffer = [], t.forEach((r) => {
993
+ this.track(r);
994
+ });
995
+ }
996
+ clearSendInterval() {
997
+ this.sendIntervalId && (clearInterval(this.sendIntervalId), this.sendIntervalId = null);
998
+ }
999
+ flushEvents(e) {
1000
+ if (this.eventsQueue.length === 0)
1001
+ return e ? !0 : Promise.resolve(!0);
1002
+ const t = this.buildEventsPayload(), r = [...this.eventsQueue], s = r.map((i) => i.id);
1003
+ if (e) {
1004
+ const i = this.dataSender.sendEventsQueueSync(t);
1005
+ return i ? (this.removeProcessedEvents(s), this.clearSendInterval(), this.emitEventsQueue(t)) : (this.removeProcessedEvents(s), this.clearSendInterval()), i;
1006
+ } else
1007
+ return this.dataSender.sendEventsQueue(t, {
1008
+ onSuccess: () => {
1009
+ this.removeProcessedEvents(s), this.clearSendInterval(), this.emitEventsQueue(t);
1010
+ },
1011
+ onFailure: () => {
1012
+ this.removeProcessedEvents(s), this.eventsQueue.length === 0 && this.clearSendInterval(), a("warn", "Async flush failed, removed from queue and persisted for recovery on next page load", {
1013
+ data: { eventCount: r.length }
1014
+ });
1015
+ }
1016
+ });
1017
+ }
1018
+ async sendEventsQueue() {
1019
+ if (!this.get("sessionId") || this.eventsQueue.length === 0)
1020
+ return;
1021
+ const e = this.buildEventsPayload(), t = [...this.eventsQueue], r = t.map((s) => s.id);
1022
+ await this.dataSender.sendEventsQueue(e, {
1023
+ onSuccess: () => {
1024
+ this.removeProcessedEvents(r), this.emitEventsQueue(e);
1025
+ },
1026
+ onFailure: () => {
1027
+ this.removeProcessedEvents(r), this.eventsQueue.length === 0 && this.clearSendInterval(), a("warn", "Events send failed, removed from queue and persisted for recovery on next page load", {
1028
+ data: { eventCount: t.length }
1029
+ });
1030
+ }
1031
+ });
1032
+ }
1033
+ buildEventsPayload() {
1034
+ const e = /* @__PURE__ */ new Map(), t = [];
1035
+ for (const s of this.eventsQueue) {
1036
+ const i = this.createEventSignature(s);
1037
+ e.has(i) || t.push(i), e.set(i, s);
1038
+ }
1039
+ const r = t.map((s) => e.get(s)).filter((s) => !!s).sort((s, i) => s.timestamp - i.timestamp);
1040
+ return {
1041
+ user_id: this.get("userId"),
1042
+ session_id: this.get("sessionId"),
1043
+ device: this.get("device"),
1044
+ events: r,
1045
+ ...this.get("config")?.globalMetadata && { global_metadata: this.get("config")?.globalMetadata }
1046
+ };
1047
+ }
1048
+ buildEventPayload(e) {
1049
+ const t = e.type === u.SESSION_START, r = e.page_url ?? this.get("pageUrl");
1050
+ return {
1051
+ id: it(),
1052
+ type: e.type,
1053
+ page_url: r,
1054
+ timestamp: Date.now(),
1055
+ ...t && { referrer: document.referrer || "Direct" },
1056
+ ...e.from_page_url && { from_page_url: e.from_page_url },
1057
+ ...e.scroll_data && { scroll_data: e.scroll_data },
1058
+ ...e.click_data && { click_data: e.click_data },
1059
+ ...e.custom_event && { custom_event: e.custom_event },
1060
+ ...e.web_vitals && { web_vitals: e.web_vitals },
1061
+ ...e.error_data && { error_data: e.error_data },
1062
+ ...e.session_end_reason && { session_end_reason: e.session_end_reason },
1063
+ ...e.viewport_data && { viewport_data: e.viewport_data },
1064
+ ...t && Te() && { utm: Te() }
1065
+ };
1066
+ }
1067
+ /**
1068
+ * Checks if event is a duplicate using time-based cache
1069
+ * Tracks recent event fingerprints with timestamp-based cleanup
1070
+ */
1071
+ isDuplicateEvent(e) {
1072
+ const t = Date.now(), r = this.createEventFingerprint(e), s = this.recentEventFingerprints.get(r);
1073
+ return s && t - s < 500 ? (this.recentEventFingerprints.set(r, t), !0) : (this.recentEventFingerprints.set(r, t), this.recentEventFingerprints.size > 1e3 && this.pruneOldFingerprints(), this.recentEventFingerprints.size > 2e3 && (this.recentEventFingerprints.clear(), this.recentEventFingerprints.set(r, t), a("warn", "Event fingerprint cache exceeded hard limit, cleared", {
1074
+ data: { hardLimit: 2e3 }
1075
+ })), !1);
1076
+ }
1077
+ /**
1078
+ * Prunes old fingerprints from cache based on timestamp
1079
+ * Removes entries older than 10x the duplicate threshold (5 seconds)
1080
+ */
1081
+ pruneOldFingerprints() {
1082
+ const e = Date.now(), t = 500 * 10;
1083
+ for (const [r, s] of this.recentEventFingerprints.entries())
1084
+ e - s > t && this.recentEventFingerprints.delete(r);
1085
+ a("debug", "Pruned old event fingerprints", {
1086
+ data: {
1087
+ remaining: this.recentEventFingerprints.size,
1088
+ cutoffMs: t
1089
+ }
1090
+ });
1091
+ }
1092
+ createEventFingerprint(e) {
1093
+ let t = `${e.type}_${e.page_url}`;
1094
+ if (e.click_data) {
1095
+ const r = Math.round((e.click_data.x || 0) / 10) * 10, s = Math.round((e.click_data.y || 0) / 10) * 10;
1096
+ t += `_click_${r}_${s}`;
1097
+ }
1098
+ return e.scroll_data && (t += `_scroll_${e.scroll_data.depth}_${e.scroll_data.direction}`), e.custom_event && (t += `_custom_${e.custom_event.name}`), e.web_vitals && (t += `_vitals_${e.web_vitals.type}`), e.error_data && (t += `_error_${e.error_data.type}_${e.error_data.message}`), t;
1099
+ }
1100
+ createEventSignature(e) {
1101
+ return this.createEventFingerprint(e);
1102
+ }
1103
+ addToQueue(e) {
1104
+ if (this.eventsQueue.push(e), this.emitEvent(e), this.eventsQueue.length > 100) {
1105
+ const t = this.eventsQueue.findIndex(
1106
+ (s) => s.type !== u.SESSION_START && s.type !== u.SESSION_END
1107
+ ), r = t >= 0 ? this.eventsQueue.splice(t, 1)[0] : this.eventsQueue.shift();
1108
+ a("warn", "Event queue overflow, oldest non-critical event removed", {
1109
+ data: {
1110
+ maxLength: 100,
1111
+ currentLength: this.eventsQueue.length,
1112
+ removedEventType: r?.type,
1113
+ wasCritical: r?.type === u.SESSION_START || r?.type === u.SESSION_END
1114
+ }
1115
+ });
1116
+ }
1117
+ this.sendIntervalId || this.startSendInterval(), this.eventsQueue.length >= 50 && this.sendEventsQueue(), this.handleGoogleAnalyticsIntegration(e);
1118
+ }
1119
+ startSendInterval() {
1120
+ this.sendIntervalId = window.setInterval(() => {
1121
+ this.eventsQueue.length > 0 && this.sendEventsQueue();
1122
+ }, 1e4);
1123
+ }
1124
+ handleGoogleAnalyticsIntegration(e) {
1125
+ if (this.googleAnalytics && e.type === u.CUSTOM && e.custom_event) {
1126
+ if (this.get("mode") === D.QA)
1127
+ return;
1128
+ this.googleAnalytics.trackEvent(e.custom_event.name, e.custom_event.metadata ?? {});
1129
+ }
1130
+ }
1131
+ shouldSample() {
1132
+ const e = this.get("config")?.samplingRate ?? 1;
1133
+ return Math.random() < e;
1134
+ }
1135
+ checkRateLimit() {
1136
+ const e = Date.now();
1137
+ return e - this.rateLimitWindowStart > 1e3 && (this.rateLimitCounter = 0, this.rateLimitWindowStart = e), this.rateLimitCounter >= 50 ? !1 : (this.rateLimitCounter++, !0);
1138
+ }
1139
+ /**
1140
+ * Checks per-event-name rate limiting to prevent infinite loops in user code
1141
+ * Tracks timestamps per event name and limits to maxSameEventPerMinute per minute
1142
+ */
1143
+ checkPerEventRateLimit(e, t) {
1144
+ const r = Date.now(), i = (this.perEventRateLimits.get(e) ?? []).filter((o) => r - o < 6e4);
1145
+ return i.length >= t ? (a("warn", "Per-event rate limit exceeded for custom event", {
1146
+ data: {
1147
+ eventName: e,
1148
+ limit: t,
1149
+ window: `${6e4 / 1e3}s`
1150
+ }
1151
+ }), !1) : (i.push(r), this.perEventRateLimits.set(e, i), !0);
1152
+ }
1153
+ /**
1154
+ * Gets the per-session limit for a specific event type (Phase 3)
1155
+ */
1156
+ getTypeLimitForEvent(e) {
1157
+ return {
1158
+ [u.CLICK]: 500,
1159
+ [u.PAGE_VIEW]: 100,
1160
+ [u.CUSTOM]: 500,
1161
+ [u.VIEWPORT_VISIBLE]: 200,
1162
+ [u.SCROLL]: 120
1163
+ }[e] ?? null;
1164
+ }
1165
+ removeProcessedEvents(e) {
1166
+ const t = new Set(e);
1167
+ this.eventsQueue = this.eventsQueue.filter((r) => !t.has(r.id));
1168
+ }
1169
+ emitEvent(e) {
1170
+ this.emitter && this.emitter.emit(Z.EVENT, e);
1171
+ }
1172
+ emitEventsQueue(e) {
1173
+ this.emitter && this.emitter.emit(Z.QUEUE, e);
1174
+ }
1175
+ }
1176
+ class _t {
1177
+ /**
1178
+ * Gets or creates a unique user ID for the given project.
1179
+ * The user ID is persisted in localStorage and reused across sessions.
1180
+ *
1181
+ * @param storageManager - Storage manager instance
1182
+ * @param projectId - Project identifier for namespacing
1183
+ * @returns Persistent unique user ID
1184
+ */
1185
+ static getId(e) {
1186
+ const t = ze, r = e.getItem(t);
1187
+ if (r)
1188
+ return r;
1189
+ const s = st();
1190
+ return e.setItem(t, s), s;
1191
+ }
1192
+ }
1193
+ class pt extends S {
1194
+ storageManager;
1195
+ eventManager;
1196
+ projectId;
1197
+ sessionTimeoutId = null;
1198
+ broadcastChannel = null;
1199
+ activityHandler = null;
1200
+ visibilityChangeHandler = null;
1201
+ beforeUnloadHandler = null;
1202
+ isTracking = !1;
1203
+ constructor(e, t, r) {
1204
+ super(), this.storageManager = e, this.eventManager = t, this.projectId = r;
1205
+ }
1206
+ initCrossTabSync() {
1207
+ if (typeof BroadcastChannel > "u") {
1208
+ a("warn", "BroadcastChannel not supported");
1209
+ return;
1210
+ }
1211
+ const e = this.getProjectId();
1212
+ this.broadcastChannel = new BroadcastChannel(Ye(e)), this.broadcastChannel.onmessage = (t) => {
1213
+ const { action: r, sessionId: s, timestamp: i, projectId: o } = t.data ?? {};
1214
+ if (o === e) {
1215
+ if (r === "session_end") {
1216
+ this.resetSessionState();
1217
+ return;
1218
+ }
1219
+ s && typeof i == "number" && i > Date.now() - 5e3 && (this.set("sessionId", s), this.set("hasStartSession", !0), this.persistSession(s, i), this.isTracking && this.setupSessionTimeout());
1220
+ }
1221
+ };
1222
+ }
1223
+ shareSession(e) {
1224
+ this.broadcastChannel && typeof this.broadcastChannel.postMessage == "function" && this.broadcastChannel.postMessage({
1225
+ action: "session_start",
1226
+ projectId: this.getProjectId(),
1227
+ sessionId: e,
1228
+ timestamp: Date.now()
1229
+ });
1230
+ }
1231
+ broadcastSessionEnd(e, t) {
1232
+ if (e && this.broadcastChannel && typeof this.broadcastChannel.postMessage == "function")
1233
+ try {
1234
+ this.broadcastChannel.postMessage({
1235
+ action: "session_end",
1236
+ projectId: this.getProjectId(),
1237
+ sessionId: e,
1238
+ reason: t,
1239
+ timestamp: Date.now()
1240
+ });
1241
+ } catch (r) {
1242
+ a("warn", "Failed to broadcast session end", { error: r, data: { sessionId: e, reason: t } });
1243
+ }
1244
+ }
1245
+ cleanupCrossTabSync() {
1246
+ this.broadcastChannel && (typeof this.broadcastChannel.close == "function" && this.broadcastChannel.close(), this.broadcastChannel = null);
1247
+ }
1248
+ recoverSession() {
1249
+ const e = this.loadStoredSession();
1250
+ if (!e)
1251
+ return null;
1252
+ const t = this.get("config")?.sessionTimeout ?? 9e5;
1253
+ return Date.now() - e.lastActivity > t ? (this.clearStoredSession(), null) : e.id;
1254
+ }
1255
+ persistSession(e, t = Date.now()) {
1256
+ this.saveStoredSession({
1257
+ id: e,
1258
+ lastActivity: t
1259
+ });
1260
+ }
1261
+ clearStoredSession() {
1262
+ const e = this.getSessionStorageKey();
1263
+ this.storageManager.removeItem(e);
1264
+ }
1265
+ loadStoredSession() {
1266
+ const e = this.getSessionStorageKey(), t = this.storageManager.getItem(e);
1267
+ if (!t)
1268
+ return null;
1269
+ try {
1270
+ const r = JSON.parse(t);
1271
+ return !r.id || typeof r.lastActivity != "number" ? null : r;
1272
+ } catch {
1273
+ return this.storageManager.removeItem(e), null;
1274
+ }
1275
+ }
1276
+ saveStoredSession(e) {
1277
+ const t = this.getSessionStorageKey();
1278
+ this.storageManager.setItem(t, JSON.stringify(e));
1279
+ }
1280
+ getSessionStorageKey() {
1281
+ return je(this.getProjectId());
1282
+ }
1283
+ getProjectId() {
1284
+ return this.projectId;
1285
+ }
1286
+ startTracking() {
1287
+ if (this.isTracking) {
1288
+ a("warn", "Session tracking already active");
1289
+ return;
1290
+ }
1291
+ const e = this.recoverSession(), t = e ?? this.generateSessionId(), r = !!e;
1292
+ this.isTracking = !0;
1293
+ try {
1294
+ this.set("sessionId", t), this.persistSession(t), r || this.eventManager.track({
1295
+ type: u.SESSION_START
1296
+ }), this.initCrossTabSync(), this.shareSession(t), this.setupSessionTimeout(), this.setupActivityListeners(), this.setupLifecycleListeners();
1297
+ } catch (s) {
1298
+ throw this.isTracking = !1, this.clearSessionTimeout(), this.cleanupActivityListeners(), this.cleanupLifecycleListeners(), this.cleanupCrossTabSync(), this.set("sessionId", null), s;
1299
+ }
1300
+ }
1301
+ generateSessionId() {
1302
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
1303
+ }
1304
+ setupSessionTimeout() {
1305
+ this.clearSessionTimeout();
1306
+ const e = this.get("config")?.sessionTimeout ?? 9e5;
1307
+ this.sessionTimeoutId = setTimeout(() => {
1308
+ this.endSession("inactivity");
1309
+ }, e);
1310
+ }
1311
+ resetSessionTimeout() {
1312
+ this.setupSessionTimeout();
1313
+ const e = this.get("sessionId");
1314
+ e && this.persistSession(e);
1315
+ }
1316
+ clearSessionTimeout() {
1317
+ this.sessionTimeoutId && (clearTimeout(this.sessionTimeoutId), this.sessionTimeoutId = null);
1318
+ }
1319
+ setupActivityListeners() {
1320
+ this.activityHandler = () => {
1321
+ this.resetSessionTimeout();
1322
+ }, document.addEventListener("click", this.activityHandler, { passive: !0 }), document.addEventListener("keydown", this.activityHandler, { passive: !0 }), document.addEventListener("scroll", this.activityHandler, { passive: !0 });
1323
+ }
1324
+ cleanupActivityListeners() {
1325
+ this.activityHandler && (document.removeEventListener("click", this.activityHandler), document.removeEventListener("keydown", this.activityHandler), document.removeEventListener("scroll", this.activityHandler), this.activityHandler = null);
1326
+ }
1327
+ setupLifecycleListeners() {
1328
+ this.visibilityChangeHandler || this.beforeUnloadHandler || (this.visibilityChangeHandler = () => {
1329
+ document.hidden ? this.clearSessionTimeout() : this.get("sessionId") && this.setupSessionTimeout();
1330
+ }, this.beforeUnloadHandler = () => {
1331
+ this.endSession("page_unload");
1332
+ }, document.addEventListener("visibilitychange", this.visibilityChangeHandler), window.addEventListener("beforeunload", this.beforeUnloadHandler));
1333
+ }
1334
+ cleanupLifecycleListeners() {
1335
+ this.visibilityChangeHandler && (document.removeEventListener("visibilitychange", this.visibilityChangeHandler), this.visibilityChangeHandler = null), this.beforeUnloadHandler && (window.removeEventListener("beforeunload", this.beforeUnloadHandler), this.beforeUnloadHandler = null);
1336
+ }
1337
+ endSession(e) {
1338
+ const t = this.get("sessionId");
1339
+ if (!t) {
1340
+ a("warn", "endSession called without active session", { data: { reason: e } }), this.resetSessionState(e);
1341
+ return;
1342
+ }
1343
+ this.eventManager.track({
1344
+ type: u.SESSION_END,
1345
+ session_end_reason: e
1346
+ }), this.eventManager.flushImmediatelySync() || a("warn", "Sync flush failed during session end, events persisted for recovery", {
1347
+ data: { reason: e, sessionId: t }
1348
+ }), this.broadcastSessionEnd(t, e), this.resetSessionState(e);
1349
+ }
1350
+ resetSessionState(e) {
1351
+ this.clearSessionTimeout(), this.cleanupActivityListeners(), this.cleanupLifecycleListeners(), this.cleanupCrossTabSync(), e !== "page_unload" && this.clearStoredSession(), this.set("sessionId", null), this.set("hasStartSession", !1), this.isTracking = !1;
1352
+ }
1353
+ stopTracking() {
1354
+ this.endSession("manual_stop");
1355
+ }
1356
+ destroy() {
1357
+ this.clearSessionTimeout(), this.cleanupActivityListeners(), this.cleanupCrossTabSync(), this.cleanupLifecycleListeners(), this.isTracking = !1, this.set("hasStartSession", !1);
1358
+ }
1359
+ }
1360
+ class It extends S {
1361
+ eventManager;
1362
+ storageManager;
1363
+ sessionManager = null;
1364
+ destroyed = !1;
1365
+ constructor(e, t) {
1366
+ super(), this.eventManager = t, this.storageManager = e;
1367
+ }
1368
+ startTracking() {
1369
+ if (this.isActive())
1370
+ return;
1371
+ if (this.destroyed) {
1372
+ a("warn", "Cannot start tracking on destroyed handler");
1373
+ return;
1374
+ }
1375
+ const e = this.get("config"), t = e?.integrations?.tracelog?.projectId ?? e?.integrations?.custom?.collectApiUrl ?? "default";
1376
+ if (!t)
1377
+ throw new Error("Cannot start session tracking: config not available");
1378
+ try {
1379
+ this.sessionManager = new pt(this.storageManager, this.eventManager, t), this.sessionManager.startTracking(), this.eventManager.flushPendingEvents();
1380
+ } catch (r) {
1381
+ if (this.sessionManager) {
1382
+ try {
1383
+ this.sessionManager.destroy();
1384
+ } catch {
1385
+ }
1386
+ this.sessionManager = null;
1387
+ }
1388
+ throw a("error", "Failed to start session tracking", { error: r }), r;
1389
+ }
1390
+ }
1391
+ isActive() {
1392
+ return this.sessionManager !== null && !this.destroyed;
1393
+ }
1394
+ cleanupSessionManager() {
1395
+ this.sessionManager && (this.sessionManager.stopTracking(), this.sessionManager.destroy(), this.sessionManager = null);
1396
+ }
1397
+ stopTracking() {
1398
+ this.cleanupSessionManager();
1399
+ }
1400
+ destroy() {
1401
+ this.destroyed || (this.sessionManager && (this.sessionManager.destroy(), this.sessionManager = null), this.destroyed = !0, this.set("hasStartSession", !1));
1402
+ }
1403
+ }
1404
+ class vt extends S {
1405
+ eventManager;
1406
+ onTrack;
1407
+ originalPushState;
1408
+ originalReplaceState;
1409
+ lastPageViewTime = 0;
1410
+ constructor(e, t) {
1411
+ super(), this.eventManager = e, this.onTrack = t;
1412
+ }
1413
+ startTracking() {
1414
+ this.trackInitialPageView(), window.addEventListener("popstate", this.trackCurrentPage, !0), window.addEventListener("hashchange", this.trackCurrentPage, !0), this.patchHistory("pushState"), this.patchHistory("replaceState");
1415
+ }
1416
+ stopTracking() {
1417
+ window.removeEventListener("popstate", this.trackCurrentPage, !0), window.removeEventListener("hashchange", this.trackCurrentPage, !0), this.originalPushState && (window.history.pushState = this.originalPushState), this.originalReplaceState && (window.history.replaceState = this.originalReplaceState), this.lastPageViewTime = 0;
1418
+ }
1419
+ patchHistory(e) {
1420
+ const t = window.history[e];
1421
+ e === "pushState" && !this.originalPushState ? this.originalPushState = t : e === "replaceState" && !this.originalReplaceState && (this.originalReplaceState = t), window.history[e] = (...r) => {
1422
+ t.apply(window.history, r), this.trackCurrentPage();
1423
+ };
1424
+ }
1425
+ trackCurrentPage = () => {
1426
+ const e = window.location.href, t = ee(e, this.get("config").sensitiveQueryParams);
1427
+ if (this.get("pageUrl") === t)
1428
+ return;
1429
+ const r = Date.now(), s = this.get("config").pageViewThrottleMs ?? 1e3;
1430
+ if (r - this.lastPageViewTime < s)
1431
+ return;
1432
+ this.lastPageViewTime = r, this.onTrack();
1433
+ const i = this.get("pageUrl");
1434
+ this.set("pageUrl", t);
1435
+ const o = this.extractPageViewData();
1436
+ this.eventManager.track({
1437
+ type: u.PAGE_VIEW,
1438
+ page_url: this.get("pageUrl"),
1439
+ from_page_url: i,
1440
+ ...o && { page_view: o }
1441
+ });
1442
+ };
1443
+ trackInitialPageView() {
1444
+ const e = ee(window.location.href, this.get("config").sensitiveQueryParams), t = this.extractPageViewData();
1445
+ this.lastPageViewTime = Date.now(), this.eventManager.track({
1446
+ type: u.PAGE_VIEW,
1447
+ page_url: e,
1448
+ ...t && { page_view: t }
1449
+ }), this.onTrack();
1450
+ }
1451
+ extractPageViewData() {
1452
+ const { pathname: e, search: t, hash: r } = window.location, { referrer: s } = document, { title: i } = document;
1453
+ return !s && !i && !e && !t && !r ? void 0 : {
1454
+ ...s && { referrer: s },
1455
+ ...i && { title: i },
1456
+ ...e && { pathname: e },
1457
+ ...t && { search: t },
1458
+ ...r && { hash: r }
1459
+ };
1460
+ }
1461
+ }
1462
+ class At extends S {
1463
+ eventManager;
1464
+ lastClickTimes = /* @__PURE__ */ new Map();
1465
+ clickHandler;
1466
+ lastPruneTime = 0;
1467
+ constructor(e) {
1468
+ super(), this.eventManager = e;
1469
+ }
1470
+ startTracking() {
1471
+ this.clickHandler || (this.clickHandler = (e) => {
1472
+ const t = e, r = t.target, s = typeof HTMLElement < "u" && r instanceof HTMLElement ? r : typeof HTMLElement < "u" && r instanceof Node && r.parentElement instanceof HTMLElement ? r.parentElement : null;
1473
+ if (!s) {
1474
+ a("warn", "Click target not found or not an element");
1475
+ return;
1476
+ }
1477
+ if (this.shouldIgnoreElement(s))
1478
+ return;
1479
+ const i = this.get("config")?.clickThrottleMs ?? 300;
1480
+ if (i > 0 && !this.checkClickThrottle(s, i))
1481
+ return;
1482
+ const o = this.findTrackingElement(s), l = this.getRelevantClickElement(s), c = this.calculateClickCoordinates(t, s);
1483
+ if (o) {
1484
+ const T = this.extractTrackingData(o);
1485
+ if (T) {
1486
+ const g = this.createCustomEventData(T);
1487
+ this.eventManager.track({
1488
+ type: u.CUSTOM,
1489
+ custom_event: {
1490
+ name: g.name,
1491
+ ...g.value && { metadata: { value: g.value } }
1492
+ }
1493
+ });
1494
+ }
1495
+ }
1496
+ const d = this.generateClickData(s, l, c);
1497
+ this.eventManager.track({
1498
+ type: u.CLICK,
1499
+ click_data: d
1500
+ });
1501
+ }, window.addEventListener("click", this.clickHandler, !0));
1502
+ }
1503
+ stopTracking() {
1504
+ this.clickHandler && (window.removeEventListener("click", this.clickHandler, !0), this.clickHandler = void 0), this.lastClickTimes.clear(), this.lastPruneTime = 0;
1505
+ }
1506
+ shouldIgnoreElement(e) {
1507
+ return e.hasAttribute(`${A}-ignore`) ? !0 : e.closest(`[${A}-ignore]`) !== null;
1508
+ }
1509
+ /**
1510
+ * Checks per-element click throttling to prevent double-clicks and rapid spam
1511
+ * Returns true if the click should be tracked, false if throttled
1512
+ */
1513
+ checkClickThrottle(e, t) {
1514
+ const r = this.getElementSignature(e), s = Date.now();
1515
+ this.pruneThrottleCache(s);
1516
+ const i = this.lastClickTimes.get(r);
1517
+ return i !== void 0 && s - i < t ? (a("debug", "ClickHandler: Click suppressed by throttle", {
1518
+ data: {
1519
+ signature: r,
1520
+ throttleRemaining: t - (s - i)
1521
+ }
1522
+ }), !1) : (this.lastClickTimes.set(r, s), !0);
1523
+ }
1524
+ /**
1525
+ * Prunes stale entries from the throttle cache to prevent memory leaks
1526
+ * Uses TTL-based eviction (5 minutes) and enforces max size limit
1527
+ * Called during checkClickThrottle with built-in rate limiting (every 30 seconds)
1528
+ */
1529
+ pruneThrottleCache(e) {
1530
+ if (e - this.lastPruneTime < 3e4)
1531
+ return;
1532
+ this.lastPruneTime = e;
1533
+ const t = e - 3e5;
1534
+ for (const [r, s] of this.lastClickTimes.entries())
1535
+ s < t && this.lastClickTimes.delete(r);
1536
+ if (this.lastClickTimes.size > 1e3) {
1537
+ const r = Array.from(this.lastClickTimes.entries()).sort((o, l) => o[1] - l[1]), s = this.lastClickTimes.size - 1e3, i = r.slice(0, s);
1538
+ for (const [o] of i)
1539
+ this.lastClickTimes.delete(o);
1540
+ a("debug", "ClickHandler: Pruned throttle cache", {
1541
+ data: {
1542
+ removed: i.length,
1543
+ remaining: this.lastClickTimes.size
1544
+ }
1545
+ });
1546
+ }
1547
+ }
1548
+ /**
1549
+ * Creates a stable signature for an element to track throttling
1550
+ * Priority: id > data-testid > data-tlog-name > DOM path
1551
+ */
1552
+ getElementSignature(e) {
1553
+ if (e.id)
1554
+ return `#${e.id}`;
1555
+ const t = e.getAttribute("data-testid");
1556
+ if (t)
1557
+ return `[data-testid="${t}"]`;
1558
+ const r = e.getAttribute(`${A}-name`);
1559
+ return r ? `[${A}-name="${r}"]` : this.getElementPath(e);
1560
+ }
1561
+ /**
1562
+ * Generates a DOM path for an element (e.g., "body>div>button")
1563
+ */
1564
+ getElementPath(e) {
1565
+ const t = [];
1566
+ let r = e;
1567
+ for (; r && r !== document.body; ) {
1568
+ let s = r.tagName.toLowerCase();
1569
+ if (r.className) {
1570
+ const i = r.className.split(" ")[0];
1571
+ i && (s += `.${i}`);
1572
+ }
1573
+ t.unshift(s), r = r.parentElement;
1574
+ }
1575
+ return t.join(">") || "unknown";
1576
+ }
1577
+ findTrackingElement(e) {
1578
+ return e.hasAttribute(`${A}-name`) ? e : e.closest(`[${A}-name]`);
1579
+ }
1580
+ getRelevantClickElement(e) {
1581
+ for (const t of He)
1582
+ try {
1583
+ if (e.matches(t))
1584
+ return e;
1585
+ const r = e.closest(t);
1586
+ if (r)
1587
+ return r;
1588
+ } catch (r) {
1589
+ a("warn", "Invalid selector in element search", { error: r, data: { selector: t } });
1590
+ continue;
1591
+ }
1592
+ return e;
1593
+ }
1594
+ clamp(e) {
1595
+ return Math.max(0, Math.min(1, Number(e.toFixed(3))));
1596
+ }
1597
+ calculateClickCoordinates(e, t) {
1598
+ const r = t.getBoundingClientRect(), s = e.clientX, i = e.clientY, o = r.width > 0 ? this.clamp((s - r.left) / r.width) : 0, l = r.height > 0 ? this.clamp((i - r.top) / r.height) : 0;
1599
+ return { x: s, y: i, relativeX: o, relativeY: l };
1600
+ }
1601
+ extractTrackingData(e) {
1602
+ const t = e.getAttribute(`${A}-name`), r = e.getAttribute(`${A}-value`);
1603
+ if (t)
1604
+ return {
1605
+ element: e,
1606
+ name: t,
1607
+ ...r && { value: r }
1608
+ };
1609
+ }
1610
+ generateClickData(e, t, r) {
1611
+ const { x: s, y: i, relativeX: o, relativeY: l } = r, c = this.getRelevantText(e, t), d = this.extractElementAttributes(t);
1612
+ return {
1613
+ x: s,
1614
+ y: i,
1615
+ relativeX: o,
1616
+ relativeY: l,
1617
+ tag: t.tagName.toLowerCase(),
1618
+ ...t.id && { id: t.id },
1619
+ ...t.className && { class: t.className },
1620
+ ...c && { text: c },
1621
+ ...d.href && { href: d.href },
1622
+ ...d.title && { title: d.title },
1623
+ ...d.alt && { alt: d.alt },
1624
+ ...d.role && { role: d.role },
1625
+ ...d["aria-label"] && { ariaLabel: d["aria-label"] },
1626
+ ...Object.keys(d).length > 0 && { dataAttributes: d }
1627
+ };
1628
+ }
1629
+ sanitizeText(e) {
1630
+ let t = e;
1631
+ for (const r of le) {
1632
+ const s = new RegExp(r.source, r.flags);
1633
+ t = t.replace(s, "[REDACTED]");
1634
+ }
1635
+ return t;
1636
+ }
1637
+ getRelevantText(e, t) {
1638
+ const r = e.textContent?.trim() ?? "", s = t.textContent?.trim() ?? "";
1639
+ if (!r && !s)
1640
+ return "";
1641
+ let i = "";
1642
+ return r && r.length <= 255 ? i = r : s.length <= 255 ? i = s : i = s.slice(0, 252) + "...", this.sanitizeText(i);
1643
+ }
1644
+ extractElementAttributes(e) {
1645
+ const t = [
1646
+ "id",
1647
+ "class",
1648
+ "data-testid",
1649
+ "aria-label",
1650
+ "title",
1651
+ "href",
1652
+ "type",
1653
+ "name",
1654
+ "alt",
1655
+ "role"
1656
+ ], r = {};
1657
+ for (const s of t) {
1658
+ const i = e.getAttribute(s);
1659
+ i && (r[s] = i);
1660
+ }
1661
+ return r;
1662
+ }
1663
+ createCustomEventData(e) {
1664
+ return {
1665
+ name: e.name,
1666
+ ...e.value && { value: e.value }
1667
+ };
1668
+ }
1669
+ }
1670
+ class wt extends S {
1671
+ eventManager;
1672
+ containers = [];
1673
+ limitWarningLogged = !1;
1674
+ minDepthChange = 5;
1675
+ minIntervalMs = 500;
1676
+ maxEventsPerSession = 120;
1677
+ containerDiscoveryTimeoutId = null;
1678
+ constructor(e) {
1679
+ super(), this.eventManager = e;
1680
+ }
1681
+ startTracking() {
1682
+ this.limitWarningLogged = !1, this.applyConfigOverrides(), this.set("scrollEventCount", 0), this.tryDetectScrollContainers(0);
1683
+ }
1684
+ stopTracking() {
1685
+ this.containerDiscoveryTimeoutId !== null && (clearTimeout(this.containerDiscoveryTimeoutId), this.containerDiscoveryTimeoutId = null);
1686
+ for (const e of this.containers)
1687
+ this.clearContainerTimer(e), e.element === window ? window.removeEventListener("scroll", e.listener) : e.element.removeEventListener("scroll", e.listener);
1688
+ this.containers.length = 0, this.set("scrollEventCount", 0), this.limitWarningLogged = !1;
1689
+ }
1690
+ tryDetectScrollContainers(e) {
1691
+ const t = this.findScrollableElements();
1692
+ if (this.isWindowScrollable() && this.setupScrollContainer(window, "window"), t.length > 0) {
1693
+ for (const r of t) {
1694
+ const s = this.getElementSelector(r);
1695
+ this.setupScrollContainer(r, s);
1696
+ }
1697
+ this.applyPrimaryScrollSelectorIfConfigured();
1698
+ return;
1699
+ }
1700
+ if (e < 5) {
1701
+ this.containerDiscoveryTimeoutId = window.setTimeout(() => {
1702
+ this.containerDiscoveryTimeoutId = null, this.tryDetectScrollContainers(e + 1);
1703
+ }, 200);
1704
+ return;
1705
+ }
1706
+ this.containers.length === 0 && this.setupScrollContainer(window, "window"), this.applyPrimaryScrollSelectorIfConfigured();
1707
+ }
1708
+ applyPrimaryScrollSelectorIfConfigured() {
1709
+ const e = this.get("config");
1710
+ e?.primaryScrollSelector && this.applyPrimaryScrollSelector(e.primaryScrollSelector);
1711
+ }
1712
+ findScrollableElements() {
1713
+ if (!document.body)
1714
+ return [];
1715
+ const e = [], t = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
1716
+ acceptNode: (s) => {
1717
+ const i = s;
1718
+ if (!i.isConnected || !i.offsetParent)
1719
+ return NodeFilter.FILTER_SKIP;
1720
+ const o = getComputedStyle(i);
1721
+ return o.overflowY === "auto" || o.overflowY === "scroll" || o.overflow === "auto" || o.overflow === "scroll" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
1722
+ }
1723
+ });
1724
+ let r;
1725
+ for (; (r = t.nextNode()) && e.length < 10; ) {
1726
+ const s = r;
1727
+ this.isElementScrollable(s) && e.push(s);
1728
+ }
1729
+ return e;
1730
+ }
1731
+ getElementSelector(e) {
1732
+ if (e === window)
1733
+ return "window";
1734
+ const t = e;
1735
+ if (t.id)
1736
+ return `#${t.id}`;
1737
+ if (t.className && typeof t.className == "string") {
1738
+ const r = t.className.split(" ").filter((s) => s.trim())[0];
1739
+ if (r)
1740
+ return `.${r}`;
1741
+ }
1742
+ return t.tagName.toLowerCase();
1743
+ }
1744
+ determineIfPrimary(e) {
1745
+ return this.isWindowScrollable() ? e === window : this.containers.length === 0;
1746
+ }
1747
+ setupScrollContainer(e, t) {
1748
+ if (this.containers.some((d) => d.element === e) || e !== window && !this.isElementScrollable(e))
1749
+ return;
1750
+ const s = this.getScrollTop(e), i = this.calculateScrollDepth(
1751
+ s,
1752
+ this.getScrollHeight(e),
1753
+ this.getViewportHeight(e)
1754
+ ), o = this.determineIfPrimary(e), l = {
1755
+ element: e,
1756
+ selector: t,
1757
+ isPrimary: o,
1758
+ lastScrollPos: s,
1759
+ lastDepth: i,
1760
+ lastDirection: U.DOWN,
1761
+ lastEventTime: 0,
1762
+ firstScrollEventTime: null,
1763
+ maxDepthReached: i,
1764
+ debounceTimer: null,
1765
+ listener: null
1766
+ }, c = () => {
1767
+ this.get("suppressNextScroll") || (l.firstScrollEventTime === null && (l.firstScrollEventTime = Date.now()), this.clearContainerTimer(l), l.debounceTimer = window.setTimeout(() => {
1768
+ const d = this.calculateScrollData(l);
1769
+ if (d) {
1770
+ const T = Date.now();
1771
+ this.processScrollEvent(l, d, T);
1772
+ }
1773
+ l.debounceTimer = null;
1774
+ }, 250));
1775
+ };
1776
+ l.listener = c, this.containers.push(l), e === window ? window.addEventListener("scroll", c, { passive: !0 }) : e.addEventListener("scroll", c, { passive: !0 });
1777
+ }
1778
+ processScrollEvent(e, t, r) {
1779
+ if (!this.shouldEmitScrollEvent(e, t, r))
1780
+ return;
1781
+ e.lastEventTime = r, e.lastDepth = t.depth, e.lastDirection = t.direction;
1782
+ const s = this.get("scrollEventCount") ?? 0;
1783
+ this.set("scrollEventCount", s + 1), this.eventManager.track({
1784
+ type: u.SCROLL,
1785
+ scroll_data: {
1786
+ ...t,
1787
+ container_selector: e.selector,
1788
+ is_primary: e.isPrimary
1789
+ }
1790
+ });
1791
+ }
1792
+ shouldEmitScrollEvent(e, t, r) {
1793
+ return this.hasReachedSessionLimit() ? (this.logLimitOnce(), !1) : !(!this.hasElapsedMinimumInterval(e, r) || !this.hasSignificantDepthChange(e, t.depth));
1794
+ }
1795
+ hasReachedSessionLimit() {
1796
+ return (this.get("scrollEventCount") ?? 0) >= this.maxEventsPerSession;
1797
+ }
1798
+ hasElapsedMinimumInterval(e, t) {
1799
+ return e.lastEventTime === 0 ? !0 : t - e.lastEventTime >= this.minIntervalMs;
1800
+ }
1801
+ hasSignificantDepthChange(e, t) {
1802
+ return Math.abs(t - e.lastDepth) >= this.minDepthChange;
1803
+ }
1804
+ logLimitOnce() {
1805
+ this.limitWarningLogged || (this.limitWarningLogged = !0, a("warn", "Max scroll events per session reached", {
1806
+ data: { limit: this.maxEventsPerSession }
1807
+ }));
1808
+ }
1809
+ applyConfigOverrides() {
1810
+ this.minDepthChange = 5, this.minIntervalMs = 500, this.maxEventsPerSession = 120;
1811
+ }
1812
+ isWindowScrollable() {
1813
+ return document.documentElement.scrollHeight > window.innerHeight;
1814
+ }
1815
+ clearContainerTimer(e) {
1816
+ e.debounceTimer !== null && (clearTimeout(e.debounceTimer), e.debounceTimer = null);
1817
+ }
1818
+ getScrollDirection(e, t) {
1819
+ return e > t ? U.DOWN : U.UP;
1820
+ }
1821
+ calculateScrollDepth(e, t, r) {
1822
+ if (t <= r)
1823
+ return 0;
1824
+ const s = t - r;
1825
+ return Math.min(100, Math.max(0, Math.floor(e / s * 100)));
1826
+ }
1827
+ calculateScrollData(e) {
1828
+ const { element: t, lastScrollPos: r, lastEventTime: s } = e, i = this.getScrollTop(t), o = Date.now(), l = Math.abs(i - r);
1829
+ if (l < 10 || t === window && !this.isWindowScrollable())
1830
+ return null;
1831
+ const c = this.getViewportHeight(t), d = this.getScrollHeight(t), T = this.getScrollDirection(i, r), g = this.calculateScrollDepth(i, d, c);
1832
+ let v;
1833
+ s > 0 ? v = o - s : e.firstScrollEventTime !== null ? v = o - e.firstScrollEventTime : v = 250;
1834
+ const m = Math.round(l / v * 1e3);
1835
+ return g > e.maxDepthReached && (e.maxDepthReached = g), e.lastScrollPos = i, {
1836
+ depth: g,
1837
+ direction: T,
1838
+ velocity: m,
1839
+ max_depth_reached: e.maxDepthReached
1840
+ };
1841
+ }
1842
+ getScrollTop(e) {
1843
+ return e === window ? window.scrollY : e.scrollTop;
1844
+ }
1845
+ getViewportHeight(e) {
1846
+ return e === window ? window.innerHeight : e.clientHeight;
1847
+ }
1848
+ getScrollHeight(e) {
1849
+ return e === window ? document.documentElement.scrollHeight : e.scrollHeight;
1850
+ }
1851
+ isElementScrollable(e) {
1852
+ const t = getComputedStyle(e), r = t.overflowY === "auto" || t.overflowY === "scroll" || t.overflow === "auto" || t.overflow === "scroll", s = e.scrollHeight > e.clientHeight;
1853
+ return r && s;
1854
+ }
1855
+ applyPrimaryScrollSelector(e) {
1856
+ let t;
1857
+ if (e === "window")
1858
+ t = window;
1859
+ else {
1860
+ const s = document.querySelector(e);
1861
+ if (!(s instanceof HTMLElement)) {
1862
+ a("warn", `Selector "${e}" did not match an HTMLElement`);
1863
+ return;
1864
+ }
1865
+ t = s;
1866
+ }
1867
+ this.containers.forEach((s) => {
1868
+ this.updateContainerPrimary(s, s.element === t);
1869
+ }), !this.containers.some((s) => s.element === t) && t instanceof HTMLElement && this.isElementScrollable(t) && this.setupScrollContainer(t, e);
1870
+ }
1871
+ updateContainerPrimary(e, t) {
1872
+ e.isPrimary = t;
1873
+ }
1874
+ }
1875
+ class yt extends S {
1876
+ eventManager;
1877
+ trackedElements = /* @__PURE__ */ new Map();
1878
+ observer = null;
1879
+ mutationObserver = null;
1880
+ mutationDebounceTimer = null;
1881
+ config = null;
1882
+ constructor(e) {
1883
+ super(), this.eventManager = e;
1884
+ }
1885
+ /**
1886
+ * Starts tracking viewport visibility for configured elements
1887
+ */
1888
+ startTracking() {
1889
+ const e = this.get("config");
1890
+ if (this.config = e.viewport ?? null, !this.config?.elements || this.config.elements.length === 0)
1891
+ return;
1892
+ const t = this.config.threshold ?? 0.5, r = this.config.minDwellTime ?? 1e3;
1893
+ if (t < 0 || t > 1) {
1894
+ a("warn", "ViewportHandler: Invalid threshold, must be between 0 and 1");
1895
+ return;
1896
+ }
1897
+ if (r < 0) {
1898
+ a("warn", "ViewportHandler: Invalid minDwellTime, must be non-negative");
1899
+ return;
1900
+ }
1901
+ if (typeof IntersectionObserver > "u") {
1902
+ a("warn", "ViewportHandler: IntersectionObserver not supported in this browser");
1903
+ return;
1904
+ }
1905
+ this.observer = new IntersectionObserver(this.handleIntersection, {
1906
+ threshold: t
1907
+ }), this.observeElements(), this.setupMutationObserver();
1908
+ }
1909
+ /**
1910
+ * Stops tracking and cleans up resources
1911
+ */
1912
+ stopTracking() {
1913
+ this.observer && (this.observer.disconnect(), this.observer = null), this.mutationObserver && (this.mutationObserver.disconnect(), this.mutationObserver = null), this.mutationDebounceTimer !== null && (window.clearTimeout(this.mutationDebounceTimer), this.mutationDebounceTimer = null);
1914
+ for (const e of this.trackedElements.values())
1915
+ e.timeoutId !== null && window.clearTimeout(e.timeoutId);
1916
+ this.trackedElements.clear();
1917
+ }
1918
+ /**
1919
+ * Query and observe all elements matching configured elements
1920
+ */
1921
+ observeElements() {
1922
+ if (!this.config || !this.observer) return;
1923
+ const e = this.config.maxTrackedElements ?? 100;
1924
+ let t = this.trackedElements.size;
1925
+ for (const r of this.config.elements)
1926
+ try {
1927
+ const s = document.querySelectorAll(r.selector);
1928
+ for (const i of Array.from(s)) {
1929
+ if (t >= e) {
1930
+ a("warn", "ViewportHandler: Maximum tracked elements reached", {
1931
+ data: {
1932
+ limit: e,
1933
+ selector: r.selector,
1934
+ message: "Some elements will not be tracked. Consider more specific selectors."
1935
+ }
1936
+ });
1937
+ return;
1938
+ }
1939
+ i.hasAttribute(`${A}-ignore`) || this.trackedElements.has(i) || (this.trackedElements.set(i, {
1940
+ element: i,
1941
+ selector: r.selector,
1942
+ id: r.id,
1943
+ name: r.name,
1944
+ startTime: null,
1945
+ timeoutId: null,
1946
+ lastFiredTime: null
1947
+ }), this.observer?.observe(i), t++);
1948
+ }
1949
+ } catch (s) {
1950
+ a("warn", `ViewportHandler: Invalid selector "${r.selector}"`, { error: s });
1951
+ }
1952
+ a("debug", "ViewportHandler: Elements tracked", {
1953
+ data: { count: t, limit: e }
1954
+ });
1955
+ }
1956
+ /**
1957
+ * Handles intersection events from IntersectionObserver
1958
+ */
1959
+ handleIntersection = (e) => {
1960
+ if (!this.config) return;
1961
+ const t = this.config.minDwellTime ?? 1e3;
1962
+ for (const r of e) {
1963
+ const s = this.trackedElements.get(r.target);
1964
+ s && (r.isIntersecting ? s.startTime === null && (s.startTime = performance.now(), s.timeoutId = window.setTimeout(() => {
1965
+ const i = Math.round(r.intersectionRatio * 100) / 100;
1966
+ this.fireViewportEvent(s, i);
1967
+ }, t)) : s.startTime !== null && (s.timeoutId !== null && (window.clearTimeout(s.timeoutId), s.timeoutId = null), s.startTime = null));
1968
+ }
1969
+ };
1970
+ /**
1971
+ * Fires a viewport visible event
1972
+ */
1973
+ fireViewportEvent(e, t) {
1974
+ if (e.startTime === null) return;
1975
+ const r = Math.round(performance.now() - e.startTime);
1976
+ if (e.element.hasAttribute("data-tlog-ignore"))
1977
+ return;
1978
+ const s = this.config?.cooldownPeriod ?? 6e4, i = Date.now();
1979
+ if (e.lastFiredTime !== null && i - e.lastFiredTime < s) {
1980
+ a("debug", "ViewportHandler: Event suppressed by cooldown period", {
1981
+ data: {
1982
+ selector: e.selector,
1983
+ cooldownRemaining: s - (i - e.lastFiredTime)
1984
+ }
1985
+ }), e.startTime = null, e.timeoutId = null;
1986
+ return;
1987
+ }
1988
+ const o = {
1989
+ selector: e.selector,
1990
+ dwellTime: r,
1991
+ visibilityRatio: t,
1992
+ ...e.id !== void 0 && { id: e.id },
1993
+ ...e.name !== void 0 && { name: e.name }
1994
+ };
1995
+ this.eventManager.track({
1996
+ type: u.VIEWPORT_VISIBLE,
1997
+ viewport_data: o
1998
+ }), e.startTime = null, e.timeoutId = null, e.lastFiredTime = i;
1999
+ }
2000
+ /**
2001
+ * Sets up MutationObserver to detect dynamically added elements
2002
+ */
2003
+ setupMutationObserver() {
2004
+ if (!(!this.config || typeof MutationObserver > "u")) {
2005
+ if (!document.body) {
2006
+ a("warn", "ViewportHandler: document.body not available, skipping MutationObserver setup");
2007
+ return;
2008
+ }
2009
+ this.mutationObserver = new MutationObserver((e) => {
2010
+ let t = !1;
2011
+ for (const r of e)
2012
+ r.type === "childList" && (r.addedNodes.length > 0 && (t = !0), r.removedNodes.length > 0 && this.cleanupRemovedNodes(r.removedNodes));
2013
+ t && (this.mutationDebounceTimer !== null && window.clearTimeout(this.mutationDebounceTimer), this.mutationDebounceTimer = window.setTimeout(() => {
2014
+ this.observeElements(), this.mutationDebounceTimer = null;
2015
+ }, 100));
2016
+ }), this.mutationObserver.observe(document.body, {
2017
+ childList: !0,
2018
+ subtree: !0
2019
+ });
2020
+ }
2021
+ }
2022
+ /**
2023
+ * Cleans up tracking for removed DOM nodes
2024
+ */
2025
+ cleanupRemovedNodes(e) {
2026
+ e.forEach((t) => {
2027
+ if (t.nodeType !== 1) return;
2028
+ const r = t, s = this.trackedElements.get(r);
2029
+ s && (s.timeoutId !== null && window.clearTimeout(s.timeoutId), this.observer?.unobserve(r), this.trackedElements.delete(r)), Array.from(this.trackedElements.keys()).filter((o) => r.contains(o)).forEach((o) => {
2030
+ const l = this.trackedElements.get(o);
2031
+ l && l.timeoutId !== null && window.clearTimeout(l.timeoutId), this.observer?.unobserve(o), this.trackedElements.delete(o);
2032
+ });
2033
+ });
2034
+ }
2035
+ }
2036
+ class Lt extends S {
2037
+ isInitialized = !1;
2038
+ async initialize() {
2039
+ if (this.isInitialized)
2040
+ return;
2041
+ const e = this.get("config").integrations?.googleAnalytics?.measurementId, t = this.get("userId");
2042
+ if (!(!e?.trim() || !t?.trim()))
2043
+ try {
2044
+ if (this.isScriptAlreadyLoaded()) {
2045
+ this.isInitialized = !0;
2046
+ return;
2047
+ }
2048
+ await this.loadScript(e), this.configureGtag(e, t), this.isInitialized = !0;
2049
+ } catch (r) {
2050
+ a("error", "Google Analytics initialization failed", { error: r });
2051
+ }
2052
+ }
2053
+ trackEvent(e, t) {
2054
+ if (!(!e?.trim() || !this.isInitialized || typeof window.gtag != "function"))
2055
+ try {
2056
+ const r = Array.isArray(t) ? { items: t } : t;
2057
+ window.gtag("event", e, r);
2058
+ } catch (r) {
2059
+ a("error", "Google Analytics event tracking failed", { error: r });
2060
+ }
2061
+ }
2062
+ cleanup() {
2063
+ this.isInitialized = !1;
2064
+ const e = document.getElementById("tracelog-ga-script");
2065
+ e && e.remove();
2066
+ }
2067
+ isScriptAlreadyLoaded() {
2068
+ return document.getElementById("tracelog-ga-script") ? !0 : !!document.querySelector('script[src*="googletagmanager.com/gtag/js"]');
2069
+ }
2070
+ async loadScript(e) {
2071
+ return new Promise((t, r) => {
2072
+ const s = document.createElement("script");
2073
+ s.id = "tracelog-ga-script", s.async = !0, s.src = `https://www.googletagmanager.com/gtag/js?id=${e}`, s.onload = () => {
2074
+ t();
2075
+ }, s.onerror = () => {
2076
+ r(new Error("Failed to load Google Analytics script"));
2077
+ }, document.head.appendChild(s);
2078
+ });
2079
+ }
2080
+ configureGtag(e, t) {
2081
+ const r = document.createElement("script");
2082
+ r.innerHTML = `
2083
+ window.dataLayer = window.dataLayer || [];
2084
+ function gtag(){dataLayer.push(arguments);}
2085
+ gtag('js', new Date());
2086
+ gtag('config', '${e}', {
2087
+ 'user_id': '${t}'
2088
+ });
2089
+ `, document.head.appendChild(r);
2090
+ }
2091
+ }
2092
+ class Mt {
2093
+ storage;
2094
+ sessionStorageRef;
2095
+ fallbackStorage = /* @__PURE__ */ new Map();
2096
+ fallbackSessionStorage = /* @__PURE__ */ new Map();
2097
+ hasQuotaExceededError = !1;
2098
+ constructor() {
2099
+ this.storage = this.initializeStorage("localStorage"), this.sessionStorageRef = this.initializeStorage("sessionStorage"), this.storage || a("warn", "localStorage not available, using memory fallback"), this.sessionStorageRef || a("warn", "sessionStorage not available, using memory fallback");
2100
+ }
2101
+ /**
2102
+ * Retrieves an item from storage
2103
+ */
2104
+ getItem(e) {
2105
+ try {
2106
+ return this.storage ? this.storage.getItem(e) : this.fallbackStorage.get(e) ?? null;
2107
+ } catch {
2108
+ return this.fallbackStorage.get(e) ?? null;
2109
+ }
2110
+ }
2111
+ /**
2112
+ * Stores an item in storage
2113
+ */
2114
+ setItem(e, t) {
2115
+ this.fallbackStorage.set(e, t);
2116
+ try {
2117
+ if (this.storage) {
2118
+ this.storage.setItem(e, t);
2119
+ return;
2120
+ }
2121
+ } catch (r) {
2122
+ if (r instanceof DOMException && r.name === "QuotaExceededError")
2123
+ if (this.hasQuotaExceededError = !0, a("warn", "localStorage quota exceeded, attempting cleanup", {
2124
+ data: { key: e, valueSize: t.length }
2125
+ }), this.cleanupOldData())
2126
+ try {
2127
+ if (this.storage) {
2128
+ this.storage.setItem(e, t);
2129
+ return;
2130
+ }
2131
+ } catch (i) {
2132
+ a("error", "localStorage quota exceeded even after cleanup - data will not persist", {
2133
+ error: i,
2134
+ data: { key: e, valueSize: t.length }
2135
+ });
2136
+ }
2137
+ else
2138
+ a("error", "localStorage quota exceeded and no data to cleanup - data will not persist", {
2139
+ error: r,
2140
+ data: { key: e, valueSize: t.length }
2141
+ });
2142
+ }
2143
+ }
2144
+ /**
2145
+ * Removes an item from storage
2146
+ */
2147
+ removeItem(e) {
2148
+ try {
2149
+ this.storage && this.storage.removeItem(e);
2150
+ } catch {
2151
+ }
2152
+ this.fallbackStorage.delete(e);
2153
+ }
2154
+ /**
2155
+ * Clears all TracLog-related items from storage
2156
+ */
2157
+ clear() {
2158
+ if (!this.storage) {
2159
+ this.fallbackStorage.clear();
2160
+ return;
2161
+ }
2162
+ try {
2163
+ const e = [];
2164
+ for (let t = 0; t < this.storage.length; t++) {
2165
+ const r = this.storage.key(t);
2166
+ r?.startsWith("tracelog_") && e.push(r);
2167
+ }
2168
+ e.forEach((t) => {
2169
+ this.storage.removeItem(t);
2170
+ }), this.fallbackStorage.clear();
2171
+ } catch (e) {
2172
+ a("error", "Failed to clear storage", { error: e }), this.fallbackStorage.clear();
2173
+ }
2174
+ }
2175
+ /**
2176
+ * Checks if storage is available
2177
+ */
2178
+ isAvailable() {
2179
+ return this.storage !== null;
2180
+ }
2181
+ /**
2182
+ * Checks if a QuotaExceededError has occurred
2183
+ * This indicates localStorage is full and data may not persist
2184
+ */
2185
+ hasQuotaError() {
2186
+ return this.hasQuotaExceededError;
2187
+ }
2188
+ /**
2189
+ * Attempts to cleanup old TraceLog data from storage to free up space
2190
+ * Returns true if any data was removed, false otherwise
2191
+ */
2192
+ cleanupOldData() {
2193
+ if (!this.storage)
2194
+ return !1;
2195
+ try {
2196
+ const e = [], t = [];
2197
+ for (let i = 0; i < this.storage.length; i++) {
2198
+ const o = this.storage.key(i);
2199
+ o?.startsWith("tracelog_") && (e.push(o), o.startsWith("tracelog_persisted_events_") && t.push(o));
2200
+ }
2201
+ if (t.length > 0)
2202
+ return t.forEach((i) => {
2203
+ try {
2204
+ this.storage.removeItem(i);
2205
+ } catch {
2206
+ }
2207
+ }), !0;
2208
+ const r = ["tracelog_session_", "tracelog_user_id", "tracelog_device_id", "tracelog_config"], s = e.filter((i) => !r.some((o) => i.startsWith(o)));
2209
+ return s.length > 0 ? (s.slice(0, 5).forEach((o) => {
2210
+ try {
2211
+ this.storage.removeItem(o);
2212
+ } catch {
2213
+ }
2214
+ }), !0) : !1;
2215
+ } catch (e) {
2216
+ return a("error", "Failed to cleanup old data", { error: e }), !1;
2217
+ }
2218
+ }
2219
+ /**
2220
+ * Initialize storage (localStorage or sessionStorage) with feature detection
2221
+ */
2222
+ initializeStorage(e) {
2223
+ if (typeof window > "u")
2224
+ return null;
2225
+ try {
2226
+ const t = e === "localStorage" ? window.localStorage : window.sessionStorage, r = "__tracelog_test__";
2227
+ return t.setItem(r, "test"), t.removeItem(r), t;
2228
+ } catch {
2229
+ return null;
2230
+ }
2231
+ }
2232
+ /**
2233
+ * Retrieves an item from sessionStorage
2234
+ */
2235
+ getSessionItem(e) {
2236
+ try {
2237
+ return this.sessionStorageRef ? this.sessionStorageRef.getItem(e) : this.fallbackSessionStorage.get(e) ?? null;
2238
+ } catch {
2239
+ return this.fallbackSessionStorage.get(e) ?? null;
2240
+ }
2241
+ }
2242
+ /**
2243
+ * Stores an item in sessionStorage
2244
+ */
2245
+ setSessionItem(e, t) {
2246
+ this.fallbackSessionStorage.set(e, t);
2247
+ try {
2248
+ if (this.sessionStorageRef) {
2249
+ this.sessionStorageRef.setItem(e, t);
2250
+ return;
2251
+ }
2252
+ } catch (r) {
2253
+ r instanceof DOMException && r.name === "QuotaExceededError" && a("error", "sessionStorage quota exceeded - data will not persist", {
2254
+ error: r,
2255
+ data: { key: e, valueSize: t.length }
2256
+ });
2257
+ }
2258
+ }
2259
+ /**
2260
+ * Removes an item from sessionStorage
2261
+ */
2262
+ removeSessionItem(e) {
2263
+ try {
2264
+ this.sessionStorageRef && this.sessionStorageRef.removeItem(e);
2265
+ } catch {
2266
+ }
2267
+ this.fallbackSessionStorage.delete(e);
2268
+ }
2269
+ }
2270
+ class Nt extends S {
2271
+ eventManager;
2272
+ reportedByNav = /* @__PURE__ */ new Map();
2273
+ navigationHistory = [];
2274
+ // FIFO queue for tracking navigation order
2275
+ observers = [];
2276
+ vitalThresholds = ye;
2277
+ lastLongTaskSentAt = 0;
2278
+ constructor(e) {
2279
+ super(), this.eventManager = e;
2280
+ }
2281
+ async startTracking() {
2282
+ await this.initWebVitals(), this.observeLongTasks();
2283
+ }
2284
+ stopTracking() {
2285
+ this.observers.forEach((e, t) => {
2286
+ try {
2287
+ e.disconnect();
2288
+ } catch (r) {
2289
+ a("warn", "Failed to disconnect performance observer", { error: r, data: { observerIndex: t } });
2290
+ }
2291
+ }), this.observers.length = 0, this.reportedByNav.clear(), this.navigationHistory.length = 0;
2292
+ }
2293
+ observeWebVitalsFallback() {
2294
+ this.reportTTFB(), this.safeObserve(
2295
+ "largest-contentful-paint",
2296
+ (r) => {
2297
+ const s = r.getEntries(), i = s[s.length - 1];
2298
+ i && this.sendVital({ type: "LCP", value: Number(i.startTime.toFixed(2)) });
2299
+ },
2300
+ { type: "largest-contentful-paint", buffered: !0 },
2301
+ !0
2302
+ );
2303
+ let e = 0, t = this.getNavigationId();
2304
+ this.safeObserve(
2305
+ "layout-shift",
2306
+ (r) => {
2307
+ const s = this.getNavigationId();
2308
+ s !== t && (e = 0, t = s);
2309
+ const i = r.getEntries();
2310
+ for (const o of i) {
2311
+ if (o.hadRecentInput === !0)
2312
+ continue;
2313
+ const l = typeof o.value == "number" ? o.value : 0;
2314
+ e += l;
2315
+ }
2316
+ this.sendVital({ type: "CLS", value: Number(e.toFixed(2)) });
2317
+ },
2318
+ { type: "layout-shift", buffered: !0 }
2319
+ ), this.safeObserve(
2320
+ "paint",
2321
+ (r) => {
2322
+ for (const s of r.getEntries())
2323
+ s.name === "first-contentful-paint" && this.sendVital({ type: "FCP", value: Number(s.startTime.toFixed(2)) });
2324
+ },
2325
+ { type: "paint", buffered: !0 },
2326
+ !0
2327
+ ), this.safeObserve(
2328
+ "event",
2329
+ (r) => {
2330
+ let s = 0;
2331
+ const i = r.getEntries();
2332
+ for (const o of i) {
2333
+ const l = (o.processingEnd ?? 0) - (o.startTime ?? 0);
2334
+ s = Math.max(s, l);
2335
+ }
2336
+ s > 0 && this.sendVital({ type: "INP", value: Number(s.toFixed(2)) });
2337
+ },
2338
+ { type: "event", buffered: !0 }
2339
+ );
2340
+ }
2341
+ async initWebVitals() {
2342
+ try {
2343
+ const { onLCP: e, onCLS: t, onFCP: r, onTTFB: s, onINP: i } = await Promise.resolve().then(() => jt), o = (l) => (c) => {
2344
+ const d = Number(c.value.toFixed(2));
2345
+ this.sendVital({ type: l, value: d });
2346
+ };
2347
+ e(o("LCP"), { reportAllChanges: !1 }), t(o("CLS"), { reportAllChanges: !1 }), r(o("FCP"), { reportAllChanges: !1 }), s(o("TTFB"), { reportAllChanges: !1 }), i(o("INP"), { reportAllChanges: !1 });
2348
+ } catch (e) {
2349
+ a("warn", "Failed to load web-vitals library, using fallback", { error: e }), this.observeWebVitalsFallback();
2350
+ }
2351
+ }
2352
+ reportTTFB() {
2353
+ try {
2354
+ const e = performance.getEntriesByType("navigation")[0];
2355
+ if (!e)
2356
+ return;
2357
+ const t = e.responseStart;
2358
+ typeof t == "number" && Number.isFinite(t) && this.sendVital({ type: "TTFB", value: Number(t.toFixed(2)) });
2359
+ } catch (e) {
2360
+ a("warn", "Failed to report TTFB", { error: e });
2361
+ }
2362
+ }
2363
+ observeLongTasks() {
2364
+ this.safeObserve(
2365
+ "longtask",
2366
+ (e) => {
2367
+ const t = e.getEntries();
2368
+ for (const r of t) {
2369
+ const s = Number(r.duration.toFixed(2)), i = Date.now();
2370
+ i - this.lastLongTaskSentAt >= Ke && (this.shouldSendVital("LONG_TASK", s) && this.trackWebVital("LONG_TASK", s), this.lastLongTaskSentAt = i);
2371
+ }
2372
+ },
2373
+ { type: "longtask", buffered: !0 }
2374
+ );
2375
+ }
2376
+ sendVital(e) {
2377
+ if (!this.shouldSendVital(e.type, e.value))
2378
+ return;
2379
+ const t = this.getNavigationId();
2380
+ if (t) {
2381
+ const r = this.reportedByNav.get(t);
2382
+ if (r?.has(e.type))
2383
+ return;
2384
+ if (r)
2385
+ r.add(e.type);
2386
+ else if (this.reportedByNav.set(t, /* @__PURE__ */ new Set([e.type])), this.navigationHistory.push(t), this.navigationHistory.length > qe) {
2387
+ const i = this.navigationHistory.shift();
2388
+ i && this.reportedByNav.delete(i);
2389
+ }
2390
+ }
2391
+ this.trackWebVital(e.type, e.value);
2392
+ }
2393
+ trackWebVital(e, t) {
2394
+ if (!Number.isFinite(t)) {
2395
+ a("warn", "Invalid web vital value", { data: { type: e, value: t } });
2396
+ return;
2397
+ }
2398
+ this.eventManager.track({
2399
+ type: u.WEB_VITALS,
2400
+ web_vitals: {
2401
+ type: e,
2402
+ value: t
2403
+ }
2404
+ });
2405
+ }
2406
+ getNavigationId() {
2407
+ try {
2408
+ const e = performance.getEntriesByType("navigation")[0];
2409
+ if (!e)
2410
+ return null;
2411
+ const t = e.startTime || performance.now(), r = Math.random().toString(36).substr(2, 5);
2412
+ return `${t.toFixed(2)}_${window.location.pathname}_${r}`;
2413
+ } catch (e) {
2414
+ return a("warn", "Failed to get navigation ID", { error: e }), null;
2415
+ }
2416
+ }
2417
+ isObserverSupported(e) {
2418
+ if (typeof PerformanceObserver > "u") return !1;
2419
+ const t = PerformanceObserver.supportedEntryTypes;
2420
+ return !t || t.includes(e);
2421
+ }
2422
+ safeObserve(e, t, r, s = !1) {
2423
+ try {
2424
+ if (!this.isObserverSupported(e))
2425
+ return !1;
2426
+ const i = new PerformanceObserver((o, l) => {
2427
+ try {
2428
+ t(o, l);
2429
+ } catch (c) {
2430
+ a("warn", "Observer callback failed", {
2431
+ error: c,
2432
+ data: { type: e }
2433
+ });
2434
+ }
2435
+ if (s)
2436
+ try {
2437
+ l.disconnect();
2438
+ } catch {
2439
+ }
2440
+ });
2441
+ return i.observe(r ?? { type: e, buffered: !0 }), s || this.observers.push(i), !0;
2442
+ } catch (i) {
2443
+ return a("warn", "Failed to create performance observer", {
2444
+ error: i,
2445
+ data: { type: e }
2446
+ }), !1;
2447
+ }
2448
+ }
2449
+ shouldSendVital(e, t) {
2450
+ if (typeof t != "number" || !Number.isFinite(t))
2451
+ return a("warn", "Invalid web vital value", { data: { type: e, value: t } }), !1;
2452
+ const r = this.vitalThresholds[e];
2453
+ return !(typeof r == "number" && t <= r);
2454
+ }
2455
+ }
2456
+ class Rt extends S {
2457
+ eventManager;
2458
+ recentErrors = /* @__PURE__ */ new Map();
2459
+ errorBurstCounter = 0;
2460
+ burstWindowStart = 0;
2461
+ burstBackoffUntil = 0;
2462
+ constructor(e) {
2463
+ super(), this.eventManager = e;
2464
+ }
2465
+ startTracking() {
2466
+ window.addEventListener("error", this.handleError), window.addEventListener("unhandledrejection", this.handleRejection);
2467
+ }
2468
+ stopTracking() {
2469
+ window.removeEventListener("error", this.handleError), window.removeEventListener("unhandledrejection", this.handleRejection), this.recentErrors.clear(), this.errorBurstCounter = 0, this.burstWindowStart = 0, this.burstBackoffUntil = 0;
2470
+ }
2471
+ /**
2472
+ * Checks sampling rate and burst detection (Phase 3)
2473
+ * Returns false if in cooldown period after burst detection
2474
+ */
2475
+ shouldSample() {
2476
+ const e = Date.now();
2477
+ if (e < this.burstBackoffUntil)
2478
+ return !1;
2479
+ if (e - this.burstWindowStart > Je && (this.errorBurstCounter = 0, this.burstWindowStart = e), this.errorBurstCounter++, this.errorBurstCounter > et)
2480
+ return this.burstBackoffUntil = e + me, a("warn", "Error burst detected - entering cooldown", {
2481
+ data: {
2482
+ errorsInWindow: this.errorBurstCounter,
2483
+ cooldownMs: me
2484
+ }
2485
+ }), !1;
2486
+ const r = this.get("config")?.errorSampling ?? Le;
2487
+ return Math.random() < r;
2488
+ }
2489
+ handleError = (e) => {
2490
+ if (!this.shouldSample())
2491
+ return;
2492
+ const t = this.sanitize(e.message || "Unknown error");
2493
+ this.shouldSuppressError(P.JS_ERROR, t) || this.eventManager.track({
2494
+ type: u.ERROR,
2495
+ error_data: {
2496
+ type: P.JS_ERROR,
2497
+ message: t,
2498
+ ...e.filename && { filename: e.filename },
2499
+ ...e.lineno && { line: e.lineno },
2500
+ ...e.colno && { column: e.colno }
2501
+ }
2502
+ });
2503
+ };
2504
+ handleRejection = (e) => {
2505
+ if (!this.shouldSample())
2506
+ return;
2507
+ const t = this.extractRejectionMessage(e.reason), r = this.sanitize(t);
2508
+ this.shouldSuppressError(P.PROMISE_REJECTION, r) || this.eventManager.track({
2509
+ type: u.ERROR,
2510
+ error_data: {
2511
+ type: P.PROMISE_REJECTION,
2512
+ message: r
2513
+ }
2514
+ });
2515
+ };
2516
+ extractRejectionMessage(e) {
2517
+ if (!e) return "Unknown rejection";
2518
+ if (typeof e == "string") return e;
2519
+ if (e instanceof Error)
2520
+ return e.stack ?? e.message ?? e.toString();
2521
+ if (typeof e == "object" && "message" in e)
2522
+ return String(e.message);
2523
+ try {
2524
+ return JSON.stringify(e);
2525
+ } catch {
2526
+ return String(e);
2527
+ }
2528
+ }
2529
+ sanitize(e) {
2530
+ let t = e.length > Ee ? e.slice(0, Ee) + "..." : e;
2531
+ for (const r of le) {
2532
+ const s = new RegExp(r.source, r.flags);
2533
+ t = t.replace(s, "[REDACTED]");
2534
+ }
2535
+ return t;
2536
+ }
2537
+ shouldSuppressError(e, t) {
2538
+ const r = Date.now(), s = `${e}:${t}`, i = this.recentErrors.get(s);
2539
+ return i && r - i < ge ? (this.recentErrors.set(s, r), !0) : (this.recentErrors.set(s, r), this.recentErrors.size > Ze ? (this.recentErrors.clear(), this.recentErrors.set(s, r), !1) : (this.recentErrors.size > x && this.pruneOldErrors(), !1));
2540
+ }
2541
+ pruneOldErrors() {
2542
+ const e = Date.now();
2543
+ for (const [s, i] of this.recentErrors.entries())
2544
+ e - i > ge && this.recentErrors.delete(s);
2545
+ if (this.recentErrors.size <= x)
2546
+ return;
2547
+ const t = Array.from(this.recentErrors.entries()).sort((s, i) => s[1] - i[1]), r = this.recentErrors.size - x;
2548
+ for (let s = 0; s < r; s += 1) {
2549
+ const i = t[s];
2550
+ i && this.recentErrors.delete(i[0]);
2551
+ }
2552
+ }
2553
+ }
2554
+ class Ot extends S {
2555
+ isInitialized = !1;
2556
+ suppressNextScrollTimer = null;
2557
+ emitter = new mt();
2558
+ managers = {};
2559
+ handlers = {};
2560
+ integrations = {};
2561
+ get initialized() {
2562
+ return this.isInitialized;
2563
+ }
2564
+ async init(e = {}) {
2565
+ if (!this.isInitialized) {
2566
+ this.managers.storage = new Mt();
2567
+ try {
2568
+ this.setupState(e), await this.setupIntegrations(), this.managers.event = new Tt(this.managers.storage, this.integrations.googleAnalytics, this.emitter), this.initializeHandlers(), await this.managers.event.recoverPersistedEvents().catch((t) => {
2569
+ a("warn", "Failed to recover persisted events", { error: t });
2570
+ }), this.isInitialized = !0;
2571
+ } catch (t) {
2572
+ this.destroy(!0);
2573
+ const r = t instanceof Error ? t.message : String(t);
2574
+ throw new Error(`[TraceLog] TraceLog initialization failed: ${r}`);
2575
+ }
2576
+ }
2577
+ }
2578
+ sendCustomEvent(e, t) {
2579
+ if (!this.managers.event)
2580
+ return;
2581
+ const { valid: r, error: s, sanitizedMetadata: i } = gt(e, t);
2582
+ if (!r) {
2583
+ if (this.get("mode") === D.QA)
2584
+ throw new Error(`[TraceLog] Custom event "${e}" validation failed: ${s}`);
2585
+ return;
2586
+ }
2587
+ this.managers.event.track({
2588
+ type: u.CUSTOM,
2589
+ custom_event: {
2590
+ name: e,
2591
+ ...i && { metadata: i }
2592
+ }
2593
+ });
2594
+ }
2595
+ on(e, t) {
2596
+ this.emitter.on(e, t);
2597
+ }
2598
+ off(e, t) {
2599
+ this.emitter.off(e, t);
2600
+ }
2601
+ destroy(e = !1) {
2602
+ !this.isInitialized && !e || (this.integrations.googleAnalytics?.cleanup(), Object.values(this.handlers).filter(Boolean).forEach((t) => {
2603
+ try {
2604
+ t.stopTracking();
2605
+ } catch (r) {
2606
+ a("warn", "Failed to stop tracking", { error: r });
2607
+ }
2608
+ }), this.suppressNextScrollTimer && (clearTimeout(this.suppressNextScrollTimer), this.suppressNextScrollTimer = null), this.managers.event?.flushImmediatelySync(), this.managers.event?.stop(), this.emitter.removeAllListeners(), this.set("hasStartSession", !1), this.set("suppressNextScroll", !1), this.set("sessionId", null), this.isInitialized = !1, this.handlers = {});
2609
+ }
2610
+ setupState(e = {}) {
2611
+ this.set("config", e);
2612
+ const t = _t.getId(this.managers.storage);
2613
+ this.set("userId", t);
2614
+ const r = ot(e);
2615
+ this.set("collectApiUrl", r);
2616
+ const s = $e();
2617
+ this.set("device", s);
2618
+ const i = ee(window.location.href, e.sensitiveQueryParams);
2619
+ this.set("pageUrl", i);
2620
+ const o = nt() ? D.QA : void 0;
2621
+ o && this.set("mode", o);
2622
+ }
2623
+ async setupIntegrations() {
2624
+ if (this.get("config").integrations?.googleAnalytics?.measurementId?.trim())
2625
+ try {
2626
+ this.integrations.googleAnalytics = new Lt(), await this.integrations.googleAnalytics.initialize();
2627
+ } catch {
2628
+ this.integrations.googleAnalytics = void 0;
2629
+ }
2630
+ }
2631
+ initializeHandlers() {
2632
+ this.handlers.session = new It(
2633
+ this.managers.storage,
2634
+ this.managers.event
2635
+ ), this.handlers.session.startTracking();
2636
+ const e = () => {
2637
+ this.set("suppressNextScroll", !0), this.suppressNextScrollTimer && clearTimeout(this.suppressNextScrollTimer), this.suppressNextScrollTimer = window.setTimeout(() => {
2638
+ this.set("suppressNextScroll", !1);
2639
+ }, 500);
2640
+ };
2641
+ this.handlers.pageView = new vt(this.managers.event, e), this.handlers.pageView.startTracking(), this.handlers.click = new At(this.managers.event), this.handlers.click.startTracking(), this.handlers.scroll = new wt(this.managers.event), this.handlers.scroll.startTracking(), this.handlers.performance = new Nt(this.managers.event), this.handlers.performance.startTracking().catch((t) => {
2642
+ a("warn", "Failed to start performance tracking", { error: t });
2643
+ }), this.handlers.error = new Rt(this.managers.event), this.handlers.error.startTracking(), this.get("config").viewport && (this.handlers.viewport = new yt(this.managers.event), this.handlers.viewport.startTracking());
2644
+ }
2645
+ }
2646
+ const N = [];
2647
+ let E = null, C = !1, F = !1;
2648
+ const Ct = async (n) => {
2649
+ if (!(typeof window > "u" || typeof document > "u") && !window.__traceLogDisabled && !E && !C) {
2650
+ C = !0;
2651
+ try {
2652
+ const e = dt(n ?? {}), t = new Ot();
2653
+ try {
2654
+ N.forEach(({ event: i, callback: o }) => {
2655
+ t.on(i, o);
2656
+ }), N.length = 0;
2657
+ const r = t.init(e), s = new Promise((i, o) => {
2658
+ setTimeout(() => {
2659
+ o(new Error("[TraceLog] Initialization timeout after 10000ms"));
2660
+ }, 1e4);
2661
+ });
2662
+ await Promise.race([r, s]), E = t;
2663
+ } catch (r) {
2664
+ try {
2665
+ t.destroy(!0);
2666
+ } catch (s) {
2667
+ a("error", "Failed to cleanup partially initialized app", { error: s });
2668
+ }
2669
+ throw r;
2670
+ }
2671
+ } catch (e) {
2672
+ throw E = null, e;
2673
+ } finally {
2674
+ C = !1;
2675
+ }
2676
+ }
2677
+ }, bt = (n, e) => {
2678
+ if (!(typeof window > "u" || typeof document > "u")) {
2679
+ if (!E)
2680
+ throw new Error("[TraceLog] TraceLog not initialized. Please call init() first.");
2681
+ if (F)
2682
+ throw new Error("[TraceLog] Cannot send events while TraceLog is being destroyed");
2683
+ E.sendCustomEvent(n, e);
2684
+ }
2685
+ }, Pt = (n, e) => {
2686
+ if (!(typeof window > "u" || typeof document > "u")) {
2687
+ if (!E || C) {
2688
+ N.push({ event: n, callback: e });
2689
+ return;
2690
+ }
2691
+ E.on(n, e);
2692
+ }
2693
+ }, Dt = (n, e) => {
2694
+ if (!(typeof window > "u" || typeof document > "u")) {
2695
+ if (!E) {
2696
+ const t = N.findIndex((r) => r.event === n && r.callback === e);
2697
+ t !== -1 && N.splice(t, 1);
2698
+ return;
2699
+ }
2700
+ E.off(n, e);
2701
+ }
2702
+ }, Vt = () => typeof window > "u" || typeof document > "u" ? !1 : E !== null, kt = () => {
2703
+ if (!(typeof window > "u" || typeof document > "u")) {
2704
+ if (F)
2705
+ throw new Error("[TraceLog] Destroy operation already in progress");
2706
+ if (!E)
2707
+ throw new Error("[TraceLog] App not initialized");
2708
+ F = !0;
2709
+ try {
2710
+ E.destroy(), E = null, C = !1, N.length = 0;
2711
+ } catch (n) {
2712
+ E = null, C = !1, N.length = 0, a("warn", "Error during destroy, forced cleanup completed", { error: n });
2713
+ } finally {
2714
+ F = !1;
2715
+ }
2716
+ }
2717
+ }, ar = {
2718
+ WEB_VITALS_THRESHOLDS: ye
2719
+ // Business thresholds for performance analysis
2720
+ }, lr = {
2721
+ PII_PATTERNS: le
2722
+ // Patterns for sensitive data protection
2723
+ }, cr = {
2724
+ LOW_ACTIVITY_EVENT_COUNT: 50,
2725
+ HIGH_ACTIVITY_EVENT_COUNT: 1e3,
2726
+ MIN_EVENTS_FOR_DYNAMIC_CALCULATION: 100,
2727
+ MIN_EVENTS_FOR_TREND_ANALYSIS: 30,
2728
+ BOUNCE_RATE_SESSION_THRESHOLD: 1,
2729
+ // Sessions with 1 page view = bounce
2730
+ MIN_ENGAGED_SESSION_DURATION_MS: 30 * 1e3,
2731
+ MIN_SCROLL_DEPTH_ENGAGEMENT: 25
2732
+ // 25% scroll depth for engagement
2733
+ }, ur = {
2734
+ INACTIVITY_TIMEOUT_MS: 1800 * 1e3,
2735
+ // 30min for analytics (vs 15min client)
2736
+ SHORT_SESSION_THRESHOLD_MS: 30 * 1e3,
2737
+ MEDIUM_SESSION_THRESHOLD_MS: 300 * 1e3,
2738
+ LONG_SESSION_THRESHOLD_MS: 1800 * 1e3,
2739
+ MAX_REALISTIC_SESSION_DURATION_MS: 480 * 60 * 1e3
2740
+ // Filter outliers
2741
+ }, dr = {
2742
+ MOBILE_MAX_WIDTH: 768,
2743
+ TABLET_MAX_WIDTH: 1024,
2744
+ MOBILE_PERFORMANCE_FACTOR: 1.5,
2745
+ // Mobile typically 1.5x slower
2746
+ TABLET_PERFORMANCE_FACTOR: 1.2
2747
+ }, hr = {
2748
+ MIN_TEXT_LENGTH_FOR_ANALYSIS: 10,
2749
+ MIN_CLICKS_FOR_HOT_ELEMENT: 10,
2750
+ // Popular element threshold
2751
+ MIN_SCROLL_COMPLETION_PERCENT: 80,
2752
+ // Page consumption threshold
2753
+ MIN_TIME_ON_PAGE_FOR_READ_MS: 15 * 1e3
2754
+ }, fr = {
2755
+ SIGNIFICANT_CHANGE_PERCENT: 20,
2756
+ MAJOR_CHANGE_PERCENT: 50,
2757
+ MIN_EVENTS_FOR_INSIGHT: 100,
2758
+ MIN_SESSIONS_FOR_INSIGHT: 10,
2759
+ MIN_CORRELATION_STRENGTH: 0.7,
2760
+ // Strong correlation threshold
2761
+ LOW_ERROR_RATE_PERCENT: 1,
2762
+ HIGH_ERROR_RATE_PERCENT: 5,
2763
+ CRITICAL_ERROR_RATE_PERCENT: 10
2764
+ }, Er = {
2765
+ SHORT_TERM_TREND_HOURS: 24,
2766
+ MEDIUM_TERM_TREND_DAYS: 7,
2767
+ LONG_TERM_TREND_DAYS: 30,
2768
+ MIN_DATA_POINTS_FOR_TREND: 5,
2769
+ WEEKLY_PATTERN_MIN_WEEKS: 4,
2770
+ DAILY_PATTERN_MIN_DAYS: 14
2771
+ }, gr = {
2772
+ MIN_SEGMENT_SIZE: 10,
2773
+ MIN_COHORT_SIZE: 5,
2774
+ COHORT_ANALYSIS_DAYS: [1, 3, 7, 14, 30],
2775
+ MIN_FUNNEL_EVENTS: 20
2776
+ }, mr = {
2777
+ DEFAULT_EVENTS_LIMIT: 5,
2778
+ DEFAULT_SESSIONS_LIMIT: 5,
2779
+ DEFAULT_PAGES_LIMIT: 5,
2780
+ MAX_EVENTS_FOR_DEEP_ANALYSIS: 1e4,
2781
+ MAX_TIME_RANGE_DAYS: 365,
2782
+ ANALYTICS_BATCH_SIZE: 1e3
2783
+ // For historical analysis
2784
+ }, Sr = {
2785
+ ANOMALY_THRESHOLD_SIGMA: 2.5,
2786
+ STRONG_ANOMALY_THRESHOLD_SIGMA: 3,
2787
+ TRAFFIC_DROP_ALERT_PERCENT: -30,
2788
+ TRAFFIC_SPIKE_ALERT_PERCENT: 200,
2789
+ MIN_BASELINE_DAYS: 7,
2790
+ MIN_EVENTS_FOR_ANOMALY_DETECTION: 50
2791
+ }, Tr = {
2792
+ PAGE_URL_EXCLUDED: "excluded",
2793
+ PAGE_URL_UNKNOWN: "unknown"
2794
+ }, _r = {
2795
+ init: Ct,
2796
+ event: bt,
2797
+ on: Pt,
2798
+ off: Dt,
2799
+ isInitialized: Vt,
2800
+ destroy: kt
2801
+ };
2802
+ var re, Ne = -1, b = function(n) {
2803
+ addEventListener("pageshow", (function(e) {
2804
+ e.persisted && (Ne = e.timeStamp, n(e));
2805
+ }), !0);
2806
+ }, ce = function() {
2807
+ var n = self.performance && performance.getEntriesByType && performance.getEntriesByType("navigation")[0];
2808
+ if (n && n.responseStart > 0 && n.responseStart < performance.now()) return n;
2809
+ }, B = function() {
2810
+ var n = ce();
2811
+ return n && n.activationStart || 0;
2812
+ }, p = function(n, e) {
2813
+ var t = ce(), r = "navigate";
2814
+ return Ne >= 0 ? r = "back-forward-cache" : t && (document.prerendering || B() > 0 ? r = "prerender" : document.wasDiscarded ? r = "restore" : t.type && (r = t.type.replace(/_/g, "-"))), { name: n, value: e === void 0 ? -1 : e, rating: "good", delta: 0, entries: [], id: "v4-".concat(Date.now(), "-").concat(Math.floor(8999999999999 * Math.random()) + 1e12), navigationType: r };
2815
+ }, k = function(n, e, t) {
2816
+ try {
2817
+ if (PerformanceObserver.supportedEntryTypes.includes(n)) {
2818
+ var r = new PerformanceObserver((function(s) {
2819
+ Promise.resolve().then((function() {
2820
+ e(s.getEntries());
2821
+ }));
2822
+ }));
2823
+ return r.observe(Object.assign({ type: n, buffered: !0 }, t || {})), r;
2824
+ }
2825
+ } catch {
2826
+ }
2827
+ }, I = function(n, e, t, r) {
2828
+ var s, i;
2829
+ return function(o) {
2830
+ e.value >= 0 && (o || r) && ((i = e.value - (s || 0)) || s === void 0) && (s = e.value, e.delta = i, e.rating = (function(l, c) {
2831
+ return l > c[1] ? "poor" : l > c[0] ? "needs-improvement" : "good";
2832
+ })(e.value, t), n(e));
2833
+ };
2834
+ }, ue = function(n) {
2835
+ requestAnimationFrame((function() {
2836
+ return requestAnimationFrame((function() {
2837
+ return n();
2838
+ }));
2839
+ }));
2840
+ }, X = function(n) {
2841
+ document.addEventListener("visibilitychange", (function() {
2842
+ document.visibilityState === "hidden" && n();
2843
+ }));
2844
+ }, de = function(n) {
2845
+ var e = !1;
2846
+ return function() {
2847
+ e || (n(), e = !0);
2848
+ };
2849
+ }, O = -1, ve = function() {
2850
+ return document.visibilityState !== "hidden" || document.prerendering ? 1 / 0 : 0;
2851
+ }, W = function(n) {
2852
+ document.visibilityState === "hidden" && O > -1 && (O = n.type === "visibilitychange" ? n.timeStamp : 0, Ht());
2853
+ }, Ae = function() {
2854
+ addEventListener("visibilitychange", W, !0), addEventListener("prerenderingchange", W, !0);
2855
+ }, Ht = function() {
2856
+ removeEventListener("visibilitychange", W, !0), removeEventListener("prerenderingchange", W, !0);
2857
+ }, Re = function() {
2858
+ return O < 0 && (O = ve(), Ae(), b((function() {
2859
+ setTimeout((function() {
2860
+ O = ve(), Ae();
2861
+ }), 0);
2862
+ }))), { get firstHiddenTime() {
2863
+ return O;
2864
+ } };
2865
+ }, $ = function(n) {
2866
+ document.prerendering ? addEventListener("prerenderingchange", (function() {
2867
+ return n();
2868
+ }), !0) : n();
2869
+ }, ne = [1800, 3e3], Oe = function(n, e) {
2870
+ e = e || {}, $((function() {
2871
+ var t, r = Re(), s = p("FCP"), i = k("paint", (function(o) {
2872
+ o.forEach((function(l) {
2873
+ l.name === "first-contentful-paint" && (i.disconnect(), l.startTime < r.firstHiddenTime && (s.value = Math.max(l.startTime - B(), 0), s.entries.push(l), t(!0)));
2874
+ }));
2875
+ }));
2876
+ i && (t = I(n, s, ne, e.reportAllChanges), b((function(o) {
2877
+ s = p("FCP"), t = I(n, s, ne, e.reportAllChanges), ue((function() {
2878
+ s.value = performance.now() - o.timeStamp, t(!0);
2879
+ }));
2880
+ })));
2881
+ }));
2882
+ }, se = [0.1, 0.25], Ut = function(n, e) {
2883
+ e = e || {}, Oe(de((function() {
2884
+ var t, r = p("CLS", 0), s = 0, i = [], o = function(c) {
2885
+ c.forEach((function(d) {
2886
+ if (!d.hadRecentInput) {
2887
+ var T = i[0], g = i[i.length - 1];
2888
+ s && d.startTime - g.startTime < 1e3 && d.startTime - T.startTime < 5e3 ? (s += d.value, i.push(d)) : (s = d.value, i = [d]);
2889
+ }
2890
+ })), s > r.value && (r.value = s, r.entries = i, t());
2891
+ }, l = k("layout-shift", o);
2892
+ l && (t = I(n, r, se, e.reportAllChanges), X((function() {
2893
+ o(l.takeRecords()), t(!0);
2894
+ })), b((function() {
2895
+ s = 0, r = p("CLS", 0), t = I(n, r, se, e.reportAllChanges), ue((function() {
2896
+ return t();
2897
+ }));
2898
+ })), setTimeout(t, 0));
2899
+ })));
2900
+ }, Ce = 0, Y = 1 / 0, H = 0, xt = function(n) {
2901
+ n.forEach((function(e) {
2902
+ e.interactionId && (Y = Math.min(Y, e.interactionId), H = Math.max(H, e.interactionId), Ce = H ? (H - Y) / 7 + 1 : 0);
2903
+ }));
2904
+ }, be = function() {
2905
+ return re ? Ce : performance.interactionCount || 0;
2906
+ }, Ft = function() {
2907
+ "interactionCount" in performance || re || (re = k("event", xt, { type: "event", buffered: !0, durationThreshold: 0 }));
2908
+ }, _ = [], G = /* @__PURE__ */ new Map(), Pe = 0, Gt = function() {
2909
+ var n = Math.min(_.length - 1, Math.floor((be() - Pe) / 50));
2910
+ return _[n];
2911
+ }, Wt = [], Bt = function(n) {
2912
+ if (Wt.forEach((function(s) {
2913
+ return s(n);
2914
+ })), n.interactionId || n.entryType === "first-input") {
2915
+ var e = _[_.length - 1], t = G.get(n.interactionId);
2916
+ if (t || _.length < 10 || n.duration > e.latency) {
2917
+ if (t) n.duration > t.latency ? (t.entries = [n], t.latency = n.duration) : n.duration === t.latency && n.startTime === t.entries[0].startTime && t.entries.push(n);
2918
+ else {
2919
+ var r = { id: n.interactionId, latency: n.duration, entries: [n] };
2920
+ G.set(r.id, r), _.push(r);
2921
+ }
2922
+ _.sort((function(s, i) {
2923
+ return i.latency - s.latency;
2924
+ })), _.length > 10 && _.splice(10).forEach((function(s) {
2925
+ return G.delete(s.id);
2926
+ }));
2927
+ }
2928
+ }
2929
+ }, De = function(n) {
2930
+ var e = self.requestIdleCallback || self.setTimeout, t = -1;
2931
+ return n = de(n), document.visibilityState === "hidden" ? n() : (t = e(n), X(n)), t;
2932
+ }, ie = [200, 500], Xt = function(n, e) {
2933
+ "PerformanceEventTiming" in self && "interactionId" in PerformanceEventTiming.prototype && (e = e || {}, $((function() {
2934
+ var t;
2935
+ Ft();
2936
+ var r, s = p("INP"), i = function(l) {
2937
+ De((function() {
2938
+ l.forEach(Bt);
2939
+ var c = Gt();
2940
+ c && c.latency !== s.value && (s.value = c.latency, s.entries = c.entries, r());
2941
+ }));
2942
+ }, o = k("event", i, { durationThreshold: (t = e.durationThreshold) !== null && t !== void 0 ? t : 40 });
2943
+ r = I(n, s, ie, e.reportAllChanges), o && (o.observe({ type: "first-input", buffered: !0 }), X((function() {
2944
+ i(o.takeRecords()), r(!0);
2945
+ })), b((function() {
2946
+ Pe = be(), _.length = 0, G.clear(), s = p("INP"), r = I(n, s, ie, e.reportAllChanges);
2947
+ })));
2948
+ })));
2949
+ }, oe = [2500, 4e3], K = {}, $t = function(n, e) {
2950
+ e = e || {}, $((function() {
2951
+ var t, r = Re(), s = p("LCP"), i = function(c) {
2952
+ e.reportAllChanges || (c = c.slice(-1)), c.forEach((function(d) {
2953
+ d.startTime < r.firstHiddenTime && (s.value = Math.max(d.startTime - B(), 0), s.entries = [d], t());
2954
+ }));
2955
+ }, o = k("largest-contentful-paint", i);
2956
+ if (o) {
2957
+ t = I(n, s, oe, e.reportAllChanges);
2958
+ var l = de((function() {
2959
+ K[s.id] || (i(o.takeRecords()), o.disconnect(), K[s.id] = !0, t(!0));
2960
+ }));
2961
+ ["keydown", "click"].forEach((function(c) {
2962
+ addEventListener(c, (function() {
2963
+ return De(l);
2964
+ }), { once: !0, capture: !0 });
2965
+ })), X(l), b((function(c) {
2966
+ s = p("LCP"), t = I(n, s, oe, e.reportAllChanges), ue((function() {
2967
+ s.value = performance.now() - c.timeStamp, K[s.id] = !0, t(!0);
2968
+ }));
2969
+ }));
2970
+ }
2971
+ }));
2972
+ }, ae = [800, 1800], zt = function n(e) {
2973
+ document.prerendering ? $((function() {
2974
+ return n(e);
2975
+ })) : document.readyState !== "complete" ? addEventListener("load", (function() {
2976
+ return n(e);
2977
+ }), !0) : setTimeout(e, 0);
2978
+ }, Qt = function(n, e) {
2979
+ e = e || {};
2980
+ var t = p("TTFB"), r = I(n, t, ae, e.reportAllChanges);
2981
+ zt((function() {
2982
+ var s = ce();
2983
+ s && (t.value = Math.max(s.responseStart - B(), 0), t.entries = [s], r(!0), b((function() {
2984
+ t = p("TTFB", 0), (r = I(n, t, ae, e.reportAllChanges))(!0);
2985
+ })));
2986
+ }));
2987
+ };
2988
+ const jt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2989
+ __proto__: null,
2990
+ CLSThresholds: se,
2991
+ FCPThresholds: ne,
2992
+ INPThresholds: ie,
2993
+ LCPThresholds: oe,
2994
+ TTFBThresholds: ae,
2995
+ onCLS: Ut,
2996
+ onFCP: Oe,
2997
+ onINP: Xt,
2998
+ onLCP: $t,
2999
+ onTTFB: Qt
3000
+ }, Symbol.toStringTag, { value: "Module" }));
3001
+ export {
3002
+ mr as ANALYTICS_QUERY_LIMITS,
3003
+ Sr as ANOMALY_DETECTION,
3004
+ f as AppConfigValidationError,
3005
+ hr as CONTENT_ANALYTICS,
3006
+ lr as DATA_PROTECTION,
3007
+ dr as DEVICE_ANALYTICS,
3008
+ y as DeviceType,
3009
+ cr as ENGAGEMENT_THRESHOLDS,
3010
+ Z as EmitterEvent,
3011
+ P as ErrorType,
3012
+ u as EventType,
3013
+ fr as INSIGHT_THRESHOLDS,
3014
+ or as InitializationTimeoutError,
3015
+ M as IntegrationValidationError,
3016
+ nr as MAX_ARRAY_LENGTH,
3017
+ Zt as MAX_CUSTOM_EVENT_ARRAY_SIZE,
3018
+ qt as MAX_CUSTOM_EVENT_KEYS,
3019
+ Yt as MAX_CUSTOM_EVENT_NAME_LENGTH,
3020
+ Kt as MAX_CUSTOM_EVENT_STRING_SIZE,
3021
+ er as MAX_METADATA_NESTING_DEPTH,
3022
+ Jt as MAX_NESTED_OBJECT_KEYS,
3023
+ tr as MAX_STRING_LENGTH,
3024
+ rr as MAX_STRING_LENGTH_IN_ARRAY,
3025
+ D as Mode,
3026
+ ar as PERFORMANCE_CONFIG,
3027
+ R as PermanentError,
3028
+ gr as SEGMENTATION_ANALYTICS,
3029
+ ur as SESSION_ANALYTICS,
3030
+ Tr as SPECIAL_PAGE_URLS,
3031
+ he as SamplingRateValidationError,
3032
+ U as ScrollDirection,
3033
+ Ge as SessionTimeoutValidationError,
3034
+ q as SpecialApiUrl,
3035
+ Er as TEMPORAL_ANALYSIS,
3036
+ V as TraceLogValidationError,
3037
+ sr as isPrimaryScrollEvent,
3038
+ ir as isSecondaryScrollEvent,
3039
+ _r as tracelog
3040
+ };