@tracelog/lib 0.5.0 → 0.5.2

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