@tracelog/lib 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -0
  3. package/dist/browser/tracelog.js +4040 -0
  4. package/dist/browser/web-vitals-CCnqwnC8.mjs +198 -0
  5. package/dist/cjs/api.d.ts +46 -0
  6. package/dist/cjs/api.js +224 -0
  7. package/dist/cjs/app.constants.d.ts +1 -0
  8. package/dist/cjs/app.constants.js +5 -0
  9. package/dist/cjs/app.d.ts +59 -0
  10. package/dist/cjs/app.js +272 -0
  11. package/dist/cjs/app.types.d.ts +6 -0
  12. package/dist/cjs/app.types.js +22 -0
  13. package/dist/cjs/constants/api.constants.d.ts +4 -0
  14. package/dist/cjs/constants/api.constants.js +18 -0
  15. package/dist/cjs/constants/browser.constants.d.ts +3 -0
  16. package/dist/cjs/constants/browser.constants.js +41 -0
  17. package/dist/cjs/constants/index.d.ts +8 -0
  18. package/dist/cjs/constants/index.js +24 -0
  19. package/dist/cjs/constants/initialization.constants.d.ts +40 -0
  20. package/dist/cjs/constants/initialization.constants.js +48 -0
  21. package/dist/cjs/constants/limits.constants.d.ts +25 -0
  22. package/dist/cjs/constants/limits.constants.js +40 -0
  23. package/dist/cjs/constants/security.constants.d.ts +1 -0
  24. package/dist/cjs/constants/security.constants.js +12 -0
  25. package/dist/cjs/constants/storage.constants.d.ts +9 -0
  26. package/dist/cjs/constants/storage.constants.js +22 -0
  27. package/dist/cjs/constants/timing.constants.d.ts +22 -0
  28. package/dist/cjs/constants/timing.constants.js +34 -0
  29. package/dist/cjs/constants/validation.constants.d.ts +13 -0
  30. package/dist/cjs/constants/validation.constants.js +31 -0
  31. package/dist/cjs/handlers/click.handler.d.ts +17 -0
  32. package/dist/cjs/handlers/click.handler.js +199 -0
  33. package/dist/cjs/handlers/error.handler.d.ts +15 -0
  34. package/dist/cjs/handlers/error.handler.js +97 -0
  35. package/dist/cjs/handlers/network.handler.d.ts +16 -0
  36. package/dist/cjs/handlers/network.handler.js +136 -0
  37. package/dist/cjs/handlers/page-view.handler.d.ts +15 -0
  38. package/dist/cjs/handlers/page-view.handler.js +83 -0
  39. package/dist/cjs/handlers/performance.handler.d.ts +19 -0
  40. package/dist/cjs/handlers/performance.handler.js +255 -0
  41. package/dist/cjs/handlers/scroll.handler.d.ts +16 -0
  42. package/dist/cjs/handlers/scroll.handler.js +138 -0
  43. package/dist/cjs/handlers/session.handler.d.ts +29 -0
  44. package/dist/cjs/handlers/session.handler.js +357 -0
  45. package/dist/cjs/integrations/google-analytics.integration.d.ts +18 -0
  46. package/dist/cjs/integrations/google-analytics.integration.js +159 -0
  47. package/dist/cjs/listeners/activity-listener-manager.d.ts +8 -0
  48. package/dist/cjs/listeners/activity-listener-manager.js +32 -0
  49. package/dist/cjs/listeners/index.d.ts +6 -0
  50. package/dist/cjs/listeners/index.js +14 -0
  51. package/dist/cjs/listeners/input-listener-managers.d.ts +15 -0
  52. package/dist/cjs/listeners/input-listener-managers.js +58 -0
  53. package/dist/cjs/listeners/listeners.types.d.ts +4 -0
  54. package/dist/cjs/listeners/listeners.types.js +2 -0
  55. package/dist/cjs/listeners/touch-listener-manager.d.ts +10 -0
  56. package/dist/cjs/listeners/touch-listener-manager.js +56 -0
  57. package/dist/cjs/listeners/unload-listener-manager.d.ts +8 -0
  58. package/dist/cjs/listeners/unload-listener-manager.js +30 -0
  59. package/dist/cjs/listeners/visibility-listener-manager.d.ts +12 -0
  60. package/dist/cjs/listeners/visibility-listener-manager.js +83 -0
  61. package/dist/cjs/managers/api.manager.d.ts +3 -0
  62. package/dist/cjs/managers/api.manager.js +14 -0
  63. package/dist/cjs/managers/config.manager.d.ts +7 -0
  64. package/dist/cjs/managers/config.manager.js +94 -0
  65. package/dist/cjs/managers/cross-tab-session.manager.d.ts +170 -0
  66. package/dist/cjs/managers/cross-tab-session.manager.js +730 -0
  67. package/dist/cjs/managers/event.manager.d.ts +61 -0
  68. package/dist/cjs/managers/event.manager.js +508 -0
  69. package/dist/cjs/managers/sampling.manager.d.ts +8 -0
  70. package/dist/cjs/managers/sampling.manager.js +53 -0
  71. package/dist/cjs/managers/sender.manager.d.ts +46 -0
  72. package/dist/cjs/managers/sender.manager.js +304 -0
  73. package/dist/cjs/managers/session-recovery.manager.d.ts +65 -0
  74. package/dist/cjs/managers/session-recovery.manager.js +237 -0
  75. package/dist/cjs/managers/session.manager.d.ts +72 -0
  76. package/dist/cjs/managers/session.manager.js +587 -0
  77. package/dist/cjs/managers/state.manager.d.ts +5 -0
  78. package/dist/cjs/managers/state.manager.js +23 -0
  79. package/dist/cjs/managers/storage.manager.d.ts +10 -0
  80. package/dist/cjs/managers/storage.manager.js +81 -0
  81. package/dist/cjs/managers/tags.manager.d.ts +12 -0
  82. package/dist/cjs/managers/tags.manager.js +289 -0
  83. package/dist/cjs/managers/user.manager.d.ts +7 -0
  84. package/dist/cjs/managers/user.manager.js +22 -0
  85. package/dist/cjs/public-api.d.ts +1 -0
  86. package/dist/cjs/public-api.js +37 -0
  87. package/dist/cjs/types/api.types.d.ts +21 -0
  88. package/dist/cjs/types/api.types.js +25 -0
  89. package/dist/cjs/types/common.types.d.ts +1 -0
  90. package/dist/cjs/types/common.types.js +2 -0
  91. package/dist/cjs/types/config.types.d.ts +104 -0
  92. package/dist/cjs/types/config.types.js +2 -0
  93. package/dist/cjs/types/device.types.d.ts +6 -0
  94. package/dist/cjs/types/device.types.js +10 -0
  95. package/dist/cjs/types/event.types.d.ts +104 -0
  96. package/dist/cjs/types/event.types.js +25 -0
  97. package/dist/cjs/types/index.d.ts +13 -0
  98. package/dist/cjs/types/index.js +29 -0
  99. package/dist/cjs/types/log.types.d.ts +4 -0
  100. package/dist/cjs/types/log.types.js +2 -0
  101. package/dist/cjs/types/mode.types.d.ts +7 -0
  102. package/dist/cjs/types/mode.types.js +11 -0
  103. package/dist/cjs/types/queue.types.d.ts +23 -0
  104. package/dist/cjs/types/queue.types.js +2 -0
  105. package/dist/cjs/types/session.types.d.ts +65 -0
  106. package/dist/cjs/types/session.types.js +2 -0
  107. package/dist/cjs/types/state.types.d.ts +12 -0
  108. package/dist/cjs/types/state.types.js +2 -0
  109. package/dist/cjs/types/tag.types.d.ts +43 -0
  110. package/dist/cjs/types/tag.types.js +31 -0
  111. package/dist/cjs/types/validation-error.types.d.ts +42 -0
  112. package/dist/cjs/types/validation-error.types.js +68 -0
  113. package/dist/cjs/types/web-vitals.types.d.ts +6 -0
  114. package/dist/cjs/types/web-vitals.types.js +2 -0
  115. package/dist/cjs/types/window.types.d.ts +17 -0
  116. package/dist/cjs/types/window.types.js +2 -0
  117. package/dist/cjs/utils/browser/device-detector.utils.d.ts +6 -0
  118. package/dist/cjs/utils/browser/device-detector.utils.js +71 -0
  119. package/dist/cjs/utils/browser/index.d.ts +2 -0
  120. package/dist/cjs/utils/browser/index.js +18 -0
  121. package/dist/cjs/utils/browser/utm-params.utils.d.ts +6 -0
  122. package/dist/cjs/utils/browser/utm-params.utils.js +37 -0
  123. package/dist/cjs/utils/data/index.d.ts +1 -0
  124. package/dist/cjs/utils/data/index.js +17 -0
  125. package/dist/cjs/utils/data/uuid.utils.d.ts +5 -0
  126. package/dist/cjs/utils/data/uuid.utils.js +18 -0
  127. package/dist/cjs/utils/index.d.ts +6 -0
  128. package/dist/cjs/utils/index.js +22 -0
  129. package/dist/cjs/utils/logging/debug-logger.utils.d.ts +56 -0
  130. package/dist/cjs/utils/logging/debug-logger.utils.js +139 -0
  131. package/dist/cjs/utils/logging/index.d.ts +1 -0
  132. package/dist/cjs/utils/logging/index.js +5 -0
  133. package/dist/cjs/utils/network/index.d.ts +1 -0
  134. package/dist/cjs/utils/network/index.js +17 -0
  135. package/dist/cjs/utils/network/url.utils.d.ts +20 -0
  136. package/dist/cjs/utils/network/url.utils.js +172 -0
  137. package/dist/cjs/utils/security/index.d.ts +1 -0
  138. package/dist/cjs/utils/security/index.js +17 -0
  139. package/dist/cjs/utils/security/sanitize.utils.d.ts +32 -0
  140. package/dist/cjs/utils/security/sanitize.utils.js +319 -0
  141. package/dist/cjs/utils/validations/config-validations.utils.d.ts +42 -0
  142. package/dist/cjs/utils/validations/config-validations.utils.js +297 -0
  143. package/dist/cjs/utils/validations/event-validations.utils.d.ts +12 -0
  144. package/dist/cjs/utils/validations/event-validations.utils.js +30 -0
  145. package/dist/cjs/utils/validations/index.d.ts +5 -0
  146. package/dist/cjs/utils/validations/index.js +21 -0
  147. package/dist/cjs/utils/validations/metadata-validations.utils.d.ts +22 -0
  148. package/dist/cjs/utils/validations/metadata-validations.utils.js +115 -0
  149. package/dist/cjs/utils/validations/type-guards.utils.d.ts +6 -0
  150. package/dist/cjs/utils/validations/type-guards.utils.js +31 -0
  151. package/dist/cjs/utils/validations/url-validations.utils.d.ts +15 -0
  152. package/dist/cjs/utils/validations/url-validations.utils.js +47 -0
  153. package/dist/esm/api.d.ts +46 -0
  154. package/dist/esm/api.js +183 -0
  155. package/dist/esm/app.constants.d.ts +1 -0
  156. package/dist/esm/app.constants.js +1 -0
  157. package/dist/esm/app.d.ts +59 -0
  158. package/dist/esm/app.js +268 -0
  159. package/dist/esm/app.types.d.ts +6 -0
  160. package/dist/esm/app.types.js +6 -0
  161. package/dist/esm/constants/api.constants.d.ts +4 -0
  162. package/dist/esm/constants/api.constants.js +14 -0
  163. package/dist/esm/constants/browser.constants.d.ts +3 -0
  164. package/dist/esm/constants/browser.constants.js +38 -0
  165. package/dist/esm/constants/index.d.ts +8 -0
  166. package/dist/esm/constants/index.js +8 -0
  167. package/dist/esm/constants/initialization.constants.d.ts +40 -0
  168. package/dist/esm/constants/initialization.constants.js +45 -0
  169. package/dist/esm/constants/limits.constants.d.ts +25 -0
  170. package/dist/esm/constants/limits.constants.js +37 -0
  171. package/dist/esm/constants/security.constants.d.ts +1 -0
  172. package/dist/esm/constants/security.constants.js +9 -0
  173. package/dist/esm/constants/storage.constants.d.ts +9 -0
  174. package/dist/esm/constants/storage.constants.js +11 -0
  175. package/dist/esm/constants/timing.constants.d.ts +22 -0
  176. package/dist/esm/constants/timing.constants.js +31 -0
  177. package/dist/esm/constants/validation.constants.d.ts +13 -0
  178. package/dist/esm/constants/validation.constants.js +28 -0
  179. package/dist/esm/handlers/click.handler.d.ts +17 -0
  180. package/dist/esm/handlers/click.handler.js +195 -0
  181. package/dist/esm/handlers/error.handler.d.ts +15 -0
  182. package/dist/esm/handlers/error.handler.js +93 -0
  183. package/dist/esm/handlers/network.handler.d.ts +16 -0
  184. package/dist/esm/handlers/network.handler.js +132 -0
  185. package/dist/esm/handlers/page-view.handler.d.ts +15 -0
  186. package/dist/esm/handlers/page-view.handler.js +79 -0
  187. package/dist/esm/handlers/performance.handler.d.ts +19 -0
  188. package/dist/esm/handlers/performance.handler.js +218 -0
  189. package/dist/esm/handlers/scroll.handler.d.ts +16 -0
  190. package/dist/esm/handlers/scroll.handler.js +134 -0
  191. package/dist/esm/handlers/session.handler.d.ts +29 -0
  192. package/dist/esm/handlers/session.handler.js +353 -0
  193. package/dist/esm/integrations/google-analytics.integration.d.ts +18 -0
  194. package/dist/esm/integrations/google-analytics.integration.js +155 -0
  195. package/dist/esm/listeners/activity-listener-manager.d.ts +8 -0
  196. package/dist/esm/listeners/activity-listener-manager.js +28 -0
  197. package/dist/esm/listeners/index.d.ts +6 -0
  198. package/dist/esm/listeners/index.js +5 -0
  199. package/dist/esm/listeners/input-listener-managers.d.ts +15 -0
  200. package/dist/esm/listeners/input-listener-managers.js +53 -0
  201. package/dist/esm/listeners/listeners.types.d.ts +4 -0
  202. package/dist/esm/listeners/listeners.types.js +1 -0
  203. package/dist/esm/listeners/touch-listener-manager.d.ts +10 -0
  204. package/dist/esm/listeners/touch-listener-manager.js +52 -0
  205. package/dist/esm/listeners/unload-listener-manager.d.ts +8 -0
  206. package/dist/esm/listeners/unload-listener-manager.js +26 -0
  207. package/dist/esm/listeners/visibility-listener-manager.d.ts +12 -0
  208. package/dist/esm/listeners/visibility-listener-manager.js +79 -0
  209. package/dist/esm/managers/api.manager.d.ts +3 -0
  210. package/dist/esm/managers/api.manager.js +10 -0
  211. package/dist/esm/managers/config.manager.d.ts +7 -0
  212. package/dist/esm/managers/config.manager.js +90 -0
  213. package/dist/esm/managers/cross-tab-session.manager.d.ts +170 -0
  214. package/dist/esm/managers/cross-tab-session.manager.js +726 -0
  215. package/dist/esm/managers/event.manager.d.ts +61 -0
  216. package/dist/esm/managers/event.manager.js +504 -0
  217. package/dist/esm/managers/sampling.manager.d.ts +8 -0
  218. package/dist/esm/managers/sampling.manager.js +49 -0
  219. package/dist/esm/managers/sender.manager.d.ts +46 -0
  220. package/dist/esm/managers/sender.manager.js +300 -0
  221. package/dist/esm/managers/session-recovery.manager.d.ts +65 -0
  222. package/dist/esm/managers/session-recovery.manager.js +233 -0
  223. package/dist/esm/managers/session.manager.d.ts +72 -0
  224. package/dist/esm/managers/session.manager.js +583 -0
  225. package/dist/esm/managers/state.manager.d.ts +5 -0
  226. package/dist/esm/managers/state.manager.js +19 -0
  227. package/dist/esm/managers/storage.manager.d.ts +10 -0
  228. package/dist/esm/managers/storage.manager.js +77 -0
  229. package/dist/esm/managers/tags.manager.d.ts +12 -0
  230. package/dist/esm/managers/tags.manager.js +285 -0
  231. package/dist/esm/managers/user.manager.d.ts +7 -0
  232. package/dist/esm/managers/user.manager.js +18 -0
  233. package/dist/esm/public-api.d.ts +1 -0
  234. package/dist/esm/public-api.js +1 -0
  235. package/dist/esm/types/api.types.d.ts +21 -0
  236. package/dist/esm/types/api.types.js +22 -0
  237. package/dist/esm/types/common.types.d.ts +1 -0
  238. package/dist/esm/types/common.types.js +1 -0
  239. package/dist/esm/types/config.types.d.ts +104 -0
  240. package/dist/esm/types/config.types.js +1 -0
  241. package/dist/esm/types/device.types.d.ts +6 -0
  242. package/dist/esm/types/device.types.js +7 -0
  243. package/dist/esm/types/event.types.d.ts +104 -0
  244. package/dist/esm/types/event.types.js +22 -0
  245. package/dist/esm/types/index.d.ts +13 -0
  246. package/dist/esm/types/index.js +13 -0
  247. package/dist/esm/types/log.types.d.ts +4 -0
  248. package/dist/esm/types/log.types.js +1 -0
  249. package/dist/esm/types/mode.types.d.ts +7 -0
  250. package/dist/esm/types/mode.types.js +8 -0
  251. package/dist/esm/types/queue.types.d.ts +23 -0
  252. package/dist/esm/types/queue.types.js +1 -0
  253. package/dist/esm/types/session.types.d.ts +65 -0
  254. package/dist/esm/types/session.types.js +1 -0
  255. package/dist/esm/types/state.types.d.ts +12 -0
  256. package/dist/esm/types/state.types.js +1 -0
  257. package/dist/esm/types/tag.types.d.ts +43 -0
  258. package/dist/esm/types/tag.types.js +28 -0
  259. package/dist/esm/types/validation-error.types.d.ts +42 -0
  260. package/dist/esm/types/validation-error.types.js +59 -0
  261. package/dist/esm/types/web-vitals.types.d.ts +6 -0
  262. package/dist/esm/types/web-vitals.types.js +1 -0
  263. package/dist/esm/types/window.types.d.ts +17 -0
  264. package/dist/esm/types/window.types.js +1 -0
  265. package/dist/esm/utils/browser/device-detector.utils.d.ts +6 -0
  266. package/dist/esm/utils/browser/device-detector.utils.js +67 -0
  267. package/dist/esm/utils/browser/index.d.ts +2 -0
  268. package/dist/esm/utils/browser/index.js +2 -0
  269. package/dist/esm/utils/browser/utm-params.utils.d.ts +6 -0
  270. package/dist/esm/utils/browser/utm-params.utils.js +33 -0
  271. package/dist/esm/utils/data/index.d.ts +1 -0
  272. package/dist/esm/utils/data/index.js +1 -0
  273. package/dist/esm/utils/data/uuid.utils.d.ts +5 -0
  274. package/dist/esm/utils/data/uuid.utils.js +14 -0
  275. package/dist/esm/utils/index.d.ts +6 -0
  276. package/dist/esm/utils/index.js +6 -0
  277. package/dist/esm/utils/logging/debug-logger.utils.d.ts +56 -0
  278. package/dist/esm/utils/logging/debug-logger.utils.js +136 -0
  279. package/dist/esm/utils/logging/index.d.ts +1 -0
  280. package/dist/esm/utils/logging/index.js +1 -0
  281. package/dist/esm/utils/network/index.d.ts +1 -0
  282. package/dist/esm/utils/network/index.js +1 -0
  283. package/dist/esm/utils/network/url.utils.d.ts +20 -0
  284. package/dist/esm/utils/network/url.utils.js +166 -0
  285. package/dist/esm/utils/security/index.d.ts +1 -0
  286. package/dist/esm/utils/security/index.js +1 -0
  287. package/dist/esm/utils/security/sanitize.utils.d.ts +32 -0
  288. package/dist/esm/utils/security/sanitize.utils.js +311 -0
  289. package/dist/esm/utils/validations/config-validations.utils.d.ts +42 -0
  290. package/dist/esm/utils/validations/config-validations.utils.js +289 -0
  291. package/dist/esm/utils/validations/event-validations.utils.d.ts +12 -0
  292. package/dist/esm/utils/validations/event-validations.utils.js +26 -0
  293. package/dist/esm/utils/validations/index.d.ts +5 -0
  294. package/dist/esm/utils/validations/index.js +5 -0
  295. package/dist/esm/utils/validations/metadata-validations.utils.d.ts +22 -0
  296. package/dist/esm/utils/validations/metadata-validations.utils.js +110 -0
  297. package/dist/esm/utils/validations/type-guards.utils.d.ts +6 -0
  298. package/dist/esm/utils/validations/type-guards.utils.js +27 -0
  299. package/dist/esm/utils/validations/url-validations.utils.d.ts +15 -0
  300. package/dist/esm/utils/validations/url-validations.utils.js +42 -0
  301. package/package.json +80 -0
@@ -0,0 +1,4040 @@
1
+ var E = /* @__PURE__ */ ((n) => (n.Mobile = "mobile", n.Tablet = "tablet", n.Desktop = "desktop", n.Unknown = "unknown", n))(E || {});
2
+ const q = {};
3
+ class p {
4
+ get(e) {
5
+ return q[e];
6
+ }
7
+ set(e, t) {
8
+ const s = q[e];
9
+ q[e] = t, (e === "sessionId" || e === "config" || e === "hasStartSession") && i.debug("StateManager", "Critical state updated", {
10
+ key: e,
11
+ oldValue: e === "config" ? !!s : s,
12
+ newValue: e === "config" ? !!t : t
13
+ });
14
+ }
15
+ }
16
+ class Re extends p {
17
+ /**
18
+ * Client-facing error - Configuration/usage errors by the client
19
+ * Console: qa and debug modes | Events: NODE_ENV=dev
20
+ */
21
+ clientError(e, t, s) {
22
+ this.logMessage("CLIENT_ERROR", e, t, s);
23
+ }
24
+ /**
25
+ * Client-facing warning - Configuration/usage warnings by the client
26
+ * Console: qa and debug modes | Events: NODE_ENV=dev
27
+ */
28
+ clientWarn(e, t, s) {
29
+ this.logMessage("CLIENT_WARN", e, t, s);
30
+ }
31
+ /**
32
+ * General operational information
33
+ * Console: qa and debug modes | Events: NODE_ENV=dev
34
+ */
35
+ info(e, t, s) {
36
+ this.logMessage("INFO", e, t, s);
37
+ }
38
+ /**
39
+ * Internal library errors
40
+ * Console: debug mode only | Events: NODE_ENV=dev
41
+ */
42
+ error(e, t, s) {
43
+ this.logMessage("ERROR", e, t, s);
44
+ }
45
+ /**
46
+ * Internal library warnings
47
+ * Console: debug mode only | Events: NODE_ENV=dev
48
+ */
49
+ warn(e, t, s) {
50
+ this.logMessage("WARN", e, t, s);
51
+ }
52
+ /**
53
+ * Strategic debug information
54
+ * Console: debug mode only | Events: NODE_ENV=dev
55
+ */
56
+ debug(e, t, s) {
57
+ this.logMessage("DEBUG", e, t, s);
58
+ }
59
+ /**
60
+ * Detailed trace information
61
+ * Console: debug mode only | Events: NODE_ENV=dev
62
+ */
63
+ verbose(e, t, s) {
64
+ this.logMessage("VERBOSE", e, t, s);
65
+ }
66
+ getCurrentMode() {
67
+ try {
68
+ return this.get("config")?.mode;
69
+ } catch {
70
+ return;
71
+ }
72
+ }
73
+ shouldShowLog(e) {
74
+ switch (this.getCurrentMode()) {
75
+ case "qa":
76
+ return ["INFO", "CLIENT_ERROR", "CLIENT_WARN"].includes(e);
77
+ case "debug":
78
+ return !0;
79
+ default:
80
+ return !1;
81
+ }
82
+ }
83
+ formatMessage(e, t) {
84
+ return `[TraceLog:${e}] ${t}`;
85
+ }
86
+ getConsoleMethod(e) {
87
+ switch (e) {
88
+ case "CLIENT_ERROR":
89
+ case "ERROR":
90
+ return "error";
91
+ case "CLIENT_WARN":
92
+ case "WARN":
93
+ return "warn";
94
+ case "INFO":
95
+ case "DEBUG":
96
+ case "VERBOSE":
97
+ default:
98
+ return "log";
99
+ }
100
+ }
101
+ logMessage(e, t, s, r) {
102
+ if (!this.shouldShowLog(e))
103
+ return;
104
+ const a = this.formatMessage(t, s), o = this.getConsoleMethod(e);
105
+ r !== void 0 ? console[o](a, r) : console[o](a);
106
+ }
107
+ /**
108
+ * Dispatches tracelog:log events for E2E testing and development debugging
109
+ */
110
+ dispatchEvent(e, t, s, r) {
111
+ if (!(typeof window > "u" || typeof CustomEvent > "u"))
112
+ try {
113
+ const a = new CustomEvent("tracelog:log", {
114
+ detail: {
115
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
116
+ level: e,
117
+ namespace: t,
118
+ message: s,
119
+ data: r
120
+ }
121
+ });
122
+ window.dispatchEvent(a);
123
+ } catch {
124
+ console.log(`[TraceLog:${t}] ${s}`, r);
125
+ }
126
+ }
127
+ }
128
+ const i = new Re();
129
+ let J, Ee;
130
+ const ke = () => {
131
+ typeof window < "u" && !J && (J = window.matchMedia("(pointer: coarse)"), Ee = window.matchMedia("(hover: none)"));
132
+ }, we = () => {
133
+ try {
134
+ i.debug("DeviceDetector", "Starting device detection");
135
+ const n = navigator;
136
+ if (n.userAgentData && typeof n.userAgentData.mobile == "boolean") {
137
+ if (i.debug("DeviceDetector", "Using modern User-Agent Client Hints API", {
138
+ mobile: n.userAgentData.mobile,
139
+ platform: n.userAgentData.platform
140
+ }), n.userAgentData.platform && /ipad|tablet/i.test(n.userAgentData.platform))
141
+ return i.debug("DeviceDetector", "Device detected as tablet via platform hint"), E.Tablet;
142
+ const d = n.userAgentData.mobile ? E.Mobile : E.Desktop;
143
+ return i.debug("DeviceDetector", "Device detected via User-Agent hints", { result: d }), d;
144
+ }
145
+ i.debug("DeviceDetector", "Using fallback detection methods"), ke();
146
+ const e = window.innerWidth, t = J?.matches ?? !1, s = Ee?.matches ?? !1, r = "ontouchstart" in window || navigator.maxTouchPoints > 0, a = navigator.userAgent.toLowerCase(), o = /mobile|android|iphone|ipod|blackberry|iemobile|opera mini/.test(a), l = /tablet|ipad|android(?!.*mobile)/.test(a), c = {
147
+ width: e,
148
+ hasCoarsePointer: t,
149
+ hasNoHover: s,
150
+ hasTouchSupport: r,
151
+ isMobileUA: o,
152
+ isTabletUA: l,
153
+ maxTouchPoints: navigator.maxTouchPoints
154
+ };
155
+ return e <= 767 || o && r ? (i.debug("DeviceDetector", "Device detected as mobile", c), E.Mobile) : e >= 768 && e <= 1024 || l || t && s && r ? (i.debug("DeviceDetector", "Device detected as tablet", c), E.Tablet) : (i.debug("DeviceDetector", "Device detected as desktop", c), E.Desktop);
156
+ } catch (n) {
157
+ return i.warn("DeviceDetector", "Device detection failed, defaulting to desktop", {
158
+ error: n instanceof Error ? n.message : n
159
+ }), E.Desktop;
160
+ }
161
+ }, Ne = 2, Ue = 10, Ie = 1, ne = 500, Z = 3e4, ee = 864e5, re = 120, ae = 8 * 1024, oe = 10, ce = 10, _ = 255, M = 1e3, W = 100, le = 3, k = 2, Pe = 4, He = 0.75, De = 0.2, xe = 2e3, Oe = 1e3, Fe = 10, F = 10, L = 15 * 60 * 1e3, ze = 3e4, Te = 1e3, Me = 250, Ve = 2e3, de = 1e3, $e = 1e4, je = 2500, he = 1e3, ue = 3e4, ge = 1e3, Ge = 24, Qe = 24 * 60 * 60 * 1e3, Be = Te, qe = 5e3, We = 2e3, Xe = 2, Ke = 3, X = 24 * 60 * 60 * 1e3, K = 2 * 60 * 1e3, Ye = "https://api.tracelog.io", Ae = {
162
+ samplingRate: Ie,
163
+ tags: [],
164
+ excludedUrlPaths: []
165
+ }, Je = (n) => ({
166
+ ...Ae,
167
+ ...n,
168
+ sessionTimeout: L,
169
+ allowHttp: !1
170
+ }), z = "data-tl", fe = [
171
+ "button",
172
+ "a",
173
+ 'input[type="button"]',
174
+ 'input[type="submit"]',
175
+ 'input[type="reset"]',
176
+ 'input[type="checkbox"]',
177
+ 'input[type="radio"]',
178
+ "select",
179
+ "textarea",
180
+ '[role="button"]',
181
+ '[role="link"]',
182
+ '[role="tab"]',
183
+ '[role="menuitem"]',
184
+ '[role="option"]',
185
+ '[role="checkbox"]',
186
+ '[role="radio"]',
187
+ '[role="switch"]',
188
+ "[routerLink]",
189
+ "[ng-click]",
190
+ "[data-action]",
191
+ "[data-click]",
192
+ "[data-navigate]",
193
+ "[data-toggle]",
194
+ "[onclick]",
195
+ ".btn",
196
+ ".button",
197
+ ".clickable",
198
+ ".nav-link",
199
+ ".menu-item",
200
+ "[data-testid]",
201
+ '[tabindex="0"]'
202
+ ], Ze = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"], me = {
203
+ /** Maximum number of retries when waiting for concurrent initialization */
204
+ MAX_CONCURRENT_RETRIES: 20,
205
+ /** Delay between retries when waiting for concurrent initialization (ms) */
206
+ CONCURRENT_RETRY_DELAY_MS: 50,
207
+ /** Timeout for overall initialization process (ms) */
208
+ INITIALIZATION_TIMEOUT_MS: 1e4
209
+ }, b = {
210
+ /** Maximum number of consecutive failures before opening circuit */
211
+ MAX_FAILURES: 10,
212
+ /** Initial backoff delay when circuit opens (ms) */
213
+ INITIAL_BACKOFF_DELAY_MS: 1e3,
214
+ /** Maximum backoff delay (ms) */
215
+ MAX_BACKOFF_DELAY_MS: 3e4,
216
+ /** Backoff multiplier for exponential backoff */
217
+ BACKOFF_MULTIPLIER: 2,
218
+ /** Time-based recovery period for circuit breaker (ms) */
219
+ RECOVERY_TIME_MS: 3e4
220
+ // 30 seconds
221
+ }, pe = {
222
+ /** Timeout for session synchronization operations (ms) */
223
+ SYNC_TIMEOUT_MS: 2e3,
224
+ /** Maximum retry attempts for session operations */
225
+ MAX_RETRY_ATTEMPTS: 3
226
+ }, et = {
227
+ /** Multiplier for scroll debounce time when suppressing scroll events */
228
+ SUPPRESS_MULTIPLIER: 2
229
+ }, _e = [
230
+ /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
231
+ /javascript:/gi,
232
+ /on\w+\s*=/gi,
233
+ /<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,
234
+ /<embed\b[^>]*>/gi,
235
+ /<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi
236
+ ], S = "tl", ve = (n) => n ? `${S}:${n}:uid` : `${S}:uid`, tt = (n) => n ? `${S}:${n}:queue` : `${S}:queue`, st = (n) => n ? `${S}:${n}:session` : `${S}:session`, Y = (n) => n ? `${S}:${n}:cross_tab_session` : `${S}:cross_tab_session`, Se = (n, e) => `${S}:${n}:tab:${e}:info`, V = (n) => n ? `${S}:${n}:recovery` : `${S}:recovery`, it = (n) => n ? `${S}:${n}:broadcast` : `${S}:broadcast`, nt = /* @__PURE__ */ new Set([
237
+ "mode",
238
+ "tags",
239
+ "samplingRate",
240
+ "excludedUrlPaths",
241
+ "ipExcluded"
242
+ ]), w = {
243
+ // Project ID validation - consistent message across all layers
244
+ MISSING_PROJECT_ID: "Project ID is required",
245
+ PROJECT_ID_EMPTY_AFTER_TRIM: "Project ID is required",
246
+ // Session timeout validation
247
+ INVALID_SESSION_TIMEOUT: `Session timeout must be between ${Z}ms (30 seconds) and ${ee}ms (24 hours)`,
248
+ INVALID_ERROR_SAMPLING_RATE: "Error sampling must be between 0 and 1",
249
+ // Integration validation
250
+ INVALID_GOOGLE_ANALYTICS_ID: "Google Analytics measurement ID is required when integration is enabled",
251
+ // UI validation
252
+ INVALID_SCROLL_CONTAINER_SELECTORS: "Scroll container selectors must be valid CSS selectors",
253
+ // Global metadata validation
254
+ INVALID_GLOBAL_METADATA: "Global metadata must be an object",
255
+ // Array validation
256
+ INVALID_SENSITIVE_QUERY_PARAMS: "Sensitive query params must be an array of strings"
257
+ }, rt = () => {
258
+ i.debug("UTMParams", "Extracting UTM parameters from URL", {
259
+ url: window.location.href,
260
+ search: window.location.search
261
+ });
262
+ const n = new URLSearchParams(window.location.search), e = {};
263
+ Ze.forEach((s) => {
264
+ const r = n.get(s);
265
+ if (r) {
266
+ const a = s.split("utm_")[1];
267
+ e[a] = r, i.debug("UTMParams", "Found UTM parameter", { param: s, key: a, value: r });
268
+ }
269
+ });
270
+ const t = Object.keys(e).length ? e : void 0;
271
+ return t ? i.debug("UTMParams", "UTM parameters extracted successfully", {
272
+ parameterCount: Object.keys(t).length,
273
+ parameters: Object.keys(t)
274
+ }) : i.debug("UTMParams", "No UTM parameters found in URL"), t;
275
+ }, $ = () => {
276
+ const n = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (e) => {
277
+ const t = Math.random() * 16 | 0;
278
+ return (e === "x" ? t : t & 3 | 8).toString(16);
279
+ });
280
+ return i.verbose("UUIDUtils", "Generated new UUID", { uuid: n }), n;
281
+ };
282
+ var C = /* @__PURE__ */ ((n) => (n.HttpSkip = "http-skip", n.HttpLocal = "http-local", n))(C || {}), h = /* @__PURE__ */ ((n) => (n.PAGE_VIEW = "page_view", n.CLICK = "click", n.SCROLL = "scroll", n.SESSION_START = "session_start", n.SESSION_END = "session_end", n.CUSTOM = "custom", n.WEB_VITALS = "web_vitals", n.ERROR = "error", n))(h || {}), j = /* @__PURE__ */ ((n) => (n.UP = "up", n.DOWN = "down", n))(j || {}), D = /* @__PURE__ */ ((n) => (n.JS_ERROR = "js_error", n.PROMISE_REJECTION = "promise_rejection", n.NETWORK_ERROR = "network_error", n))(D || {}), N = /* @__PURE__ */ ((n) => (n.QA = "qa", n.DEBUG = "debug", n))(N || {}), G = /* @__PURE__ */ ((n) => (n.AND = "AND", n.OR = "OR", n))(G || {}), m = /* @__PURE__ */ ((n) => (n.URL_MATCHES = "url_matches", n.ELEMENT_MATCHES = "element_matches", n.DEVICE_TYPE = "device_type", n.ELEMENT_TEXT = "element_text", n.ELEMENT_ATTRIBUTE = "element_attribute", n.UTM_SOURCE = "utm_source", n.UTM_MEDIUM = "utm_medium", n.UTM_CAMPAIGN = "utm_campaign", n))(m || {}), f = /* @__PURE__ */ ((n) => (n.EQUALS = "equals", n.CONTAINS = "contains", n.STARTS_WITH = "starts_with", n.ENDS_WITH = "ends_with", n.REGEX = "regex", n.GREATER_THAN = "greater_than", n.LESS_THAN = "less_than", n.EXISTS = "exists", n.NOT_EXISTS = "not_exists", n))(f || {});
283
+ class x extends Error {
284
+ constructor(e, t, s) {
285
+ super(e), this.errorCode = t, this.layer = s, this.name = this.constructor.name, Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
286
+ }
287
+ }
288
+ class Q extends x {
289
+ constructor(e = "Project ID is required", t = "config") {
290
+ super(e, "PROJECT_ID_INVALID", t);
291
+ }
292
+ }
293
+ class H extends x {
294
+ constructor(e, t = "config") {
295
+ super(e, "APP_CONFIG_INVALID", t);
296
+ }
297
+ }
298
+ class at extends x {
299
+ constructor(e, t = "config") {
300
+ super(e, "SESSION_TIMEOUT_INVALID", t);
301
+ }
302
+ }
303
+ class ot extends x {
304
+ constructor(e, t = "config") {
305
+ super(e, "SAMPLING_RATE_INVALID", t);
306
+ }
307
+ }
308
+ class ye extends x {
309
+ constructor(e, t = "config") {
310
+ super(e, "INTEGRATION_INVALID", t);
311
+ }
312
+ }
313
+ const ct = (n) => {
314
+ if (!n || typeof n != "object")
315
+ throw i.clientError("ConfigValidation", "Configuration must be an object", { config: n }), new H("Configuration must be an object", "config");
316
+ if (!("id" in n))
317
+ throw i.clientError("ConfigValidation", "Project ID is missing from configuration"), new Q(w.MISSING_PROJECT_ID, "config");
318
+ if (n.id === null || n.id === void 0 || typeof n.id != "string")
319
+ throw i.clientError("ConfigValidation", "Project ID must be a non-empty string", {
320
+ providedId: n.id,
321
+ type: typeof n.id
322
+ }), new Q(w.MISSING_PROJECT_ID, "config");
323
+ if (n.sessionTimeout !== void 0 && (typeof n.sessionTimeout != "number" || n.sessionTimeout < Z || n.sessionTimeout > ee))
324
+ throw i.clientError("ConfigValidation", "Invalid session timeout", {
325
+ provided: n.sessionTimeout,
326
+ min: Z,
327
+ max: ee
328
+ }), new at(w.INVALID_SESSION_TIMEOUT, "config");
329
+ if (n.globalMetadata !== void 0 && (typeof n.globalMetadata != "object" || n.globalMetadata === null))
330
+ throw i.clientError("ConfigValidation", "Global metadata must be an object", {
331
+ provided: n.globalMetadata,
332
+ type: typeof n.globalMetadata
333
+ }), new H(w.INVALID_GLOBAL_METADATA, "config");
334
+ if (n.scrollContainerSelectors !== void 0 && lt(n.scrollContainerSelectors), n.integrations && dt(n.integrations), n.sensitiveQueryParams !== void 0) {
335
+ if (!Array.isArray(n.sensitiveQueryParams))
336
+ throw i.clientError("ConfigValidation", "Sensitive query params must be an array", {
337
+ provided: n.sensitiveQueryParams,
338
+ type: typeof n.sensitiveQueryParams
339
+ }), new H(w.INVALID_SENSITIVE_QUERY_PARAMS, "config");
340
+ for (const e of n.sensitiveQueryParams)
341
+ if (typeof e != "string")
342
+ throw i.clientError("ConfigValidation", "All sensitive query params must be strings", {
343
+ param: e,
344
+ type: typeof e
345
+ }), new H("All sensitive query params must be strings", "config");
346
+ }
347
+ if (n.errorSampling !== void 0 && (typeof n.errorSampling != "number" || n.errorSampling < 0 || n.errorSampling > 1))
348
+ throw i.clientError("ConfigValidation", "Invalid error sampling rate", {
349
+ provided: n.errorSampling,
350
+ expected: "0-1"
351
+ }), new ot(w.INVALID_ERROR_SAMPLING_RATE, "config");
352
+ }, lt = (n) => {
353
+ const e = Array.isArray(n) ? n : [n];
354
+ for (const t of e) {
355
+ if (typeof t != "string" || t.trim() === "")
356
+ throw i.clientError("ConfigValidation", "Invalid scroll container selector", {
357
+ selector: t,
358
+ type: typeof t,
359
+ isEmpty: t === "" || typeof t == "string" && t.trim() === ""
360
+ }), new H(w.INVALID_SCROLL_CONTAINER_SELECTORS, "config");
361
+ if (typeof document < "u")
362
+ try {
363
+ document.querySelector(t);
364
+ } catch {
365
+ i.clientWarn("ConfigValidation", `Invalid CSS selector will be ignored: "${t}"`);
366
+ }
367
+ }
368
+ }, dt = (n) => {
369
+ if (n && n.googleAnalytics) {
370
+ if (!n.googleAnalytics.measurementId || typeof n.googleAnalytics.measurementId != "string" || n.googleAnalytics.measurementId.trim() === "")
371
+ throw i.clientError("ConfigValidation", "Invalid Google Analytics measurement ID", {
372
+ provided: n.googleAnalytics.measurementId,
373
+ type: typeof n.googleAnalytics.measurementId
374
+ }), new ye(w.INVALID_GOOGLE_ANALYTICS_ID, "config");
375
+ const e = n.googleAnalytics.measurementId.trim();
376
+ if (!e.match(/^(G-|UA-)/))
377
+ throw i.clientError("ConfigValidation", 'Google Analytics measurement ID must start with "G-" or "UA-"', {
378
+ provided: e
379
+ }), new ye('Google Analytics measurement ID must start with "G-" or "UA-"', "config");
380
+ }
381
+ }, ht = (n) => {
382
+ ct(n);
383
+ const e = {
384
+ ...n,
385
+ id: n.id.trim(),
386
+ globalMetadata: n.globalMetadata ?? {},
387
+ sensitiveQueryParams: n.sensitiveQueryParams ?? []
388
+ };
389
+ if (!e.id)
390
+ throw i.clientError("ConfigValidation", "Project ID is empty after trimming whitespace", {
391
+ originalId: n.id,
392
+ normalizedId: e.id
393
+ }), new Q(w.PROJECT_ID_EMPTY_AFTER_TRIM, "config");
394
+ return e;
395
+ }, be = (n) => {
396
+ if (!n || typeof n != "string" || n.trim().length === 0)
397
+ return i.debug("Sanitize", "String sanitization skipped - empty or invalid input", { value: n, type: typeof n }), "";
398
+ const e = n.length;
399
+ let t = n;
400
+ n.length > M && (t = n.slice(0, Math.max(0, M)), i.warn("Sanitize", "String truncated due to length limit", {
401
+ originalLength: e,
402
+ maxLength: M,
403
+ truncatedLength: t.length
404
+ }));
405
+ let s = 0;
406
+ for (const a of _e) {
407
+ const o = t;
408
+ t = t.replace(a, ""), o !== t && s++;
409
+ }
410
+ s > 0 && i.warn("Sanitize", "XSS patterns detected and removed", {
411
+ patternMatches: s,
412
+ originalValue: n.slice(0, 100)
413
+ // Log first 100 chars for debugging
414
+ }), t = t.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#x27;").replaceAll("/", "&#x2F;");
415
+ const r = t.trim();
416
+ return (e > 50 || s > 0) && i.debug("Sanitize", "String sanitization completed", {
417
+ originalLength: e,
418
+ sanitizedLength: r.length,
419
+ xssPatternMatches: s,
420
+ wasTruncated: e > M
421
+ }), r;
422
+ }, ut = (n) => {
423
+ if (typeof n != "string")
424
+ return "";
425
+ n.length > M && (n = n.slice(0, Math.max(0, M)));
426
+ let e = n;
427
+ for (const t of _e)
428
+ e = e.replace(t, "");
429
+ return e = e.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#x27;"), e.trim();
430
+ }, B = (n, e = 0) => {
431
+ if (e > le)
432
+ return i.warn("Sanitize", "Maximum object depth exceeded during sanitization", {
433
+ depth: e,
434
+ maxDepth: le
435
+ }), null;
436
+ if (n == null)
437
+ return null;
438
+ if (typeof n == "string")
439
+ return be(n);
440
+ if (typeof n == "number")
441
+ return !Number.isFinite(n) || n < -Number.MAX_SAFE_INTEGER || n > Number.MAX_SAFE_INTEGER ? (i.warn("Sanitize", "Invalid number sanitized to 0", { value: n, isFinite: Number.isFinite(n) }), 0) : n;
442
+ if (typeof n == "boolean")
443
+ return n;
444
+ if (Array.isArray(n)) {
445
+ const t = n.length, s = n.slice(0, W);
446
+ t > W && i.warn("Sanitize", "Array truncated due to length limit", {
447
+ originalLength: t,
448
+ maxLength: W,
449
+ depth: e
450
+ });
451
+ const r = s.map((a) => B(a, e + 1)).filter((a) => a !== null);
452
+ return t > 0 && r.length === 0 && i.warn("Sanitize", "All array items were filtered out during sanitization", { originalLength: t, depth: e }), r;
453
+ }
454
+ if (typeof n == "object") {
455
+ const t = {}, s = Object.entries(n), r = s.length, a = s.slice(0, 20);
456
+ r > 20 && i.warn("Sanitize", "Object keys truncated due to limit", {
457
+ originalKeys: r,
458
+ maxKeys: 20,
459
+ depth: e
460
+ });
461
+ let o = 0;
462
+ for (const [l, c] of a) {
463
+ const d = be(l);
464
+ if (d) {
465
+ const u = B(c, e + 1);
466
+ u !== null ? t[d] = u : o++;
467
+ } else
468
+ o++;
469
+ }
470
+ return o > 0 && i.debug("Sanitize", "Object properties filtered during sanitization", {
471
+ filteredKeysCount: o,
472
+ remainingKeys: Object.keys(t).length,
473
+ depth: e
474
+ }), t;
475
+ }
476
+ return i.debug("Sanitize", "Unknown value type sanitized to null", { type: typeof n, depth: e }), null;
477
+ }, gt = (n) => {
478
+ i.debug("Sanitize", "Starting API config sanitization");
479
+ const e = {};
480
+ if (typeof n != "object" || n === null)
481
+ return i.warn("Sanitize", "API config data is not an object", { data: n, type: typeof n }), e;
482
+ try {
483
+ const t = Object.keys(n);
484
+ let s = 0, r = 0;
485
+ for (const a of t)
486
+ if (nt.has(a)) {
487
+ const o = n[a];
488
+ if (a === "excludedUrlPaths") {
489
+ const l = Array.isArray(o) ? o : typeof o == "string" ? [o] : [], c = l.length;
490
+ e.excludedUrlPaths = l.map((u) => ut(String(u))).filter(Boolean);
491
+ const d = c - e.excludedUrlPaths.length;
492
+ d > 0 && i.warn("Sanitize", "Some excluded URL paths were filtered during sanitization", {
493
+ originalCount: c,
494
+ filteredCount: d
495
+ });
496
+ } else if (a === "tags")
497
+ Array.isArray(o) ? (e.tags = o, i.debug("Sanitize", "Tags processed", { count: o.length })) : i.warn("Sanitize", "Tags value is not an array", { value: o, type: typeof o });
498
+ else {
499
+ const l = B(o);
500
+ l !== null ? e[a] = l : i.warn("Sanitize", "API config value sanitized to null", { key: a, originalValue: o });
501
+ }
502
+ s++;
503
+ } else
504
+ r++, i.debug("Sanitize", "API config key not allowed", { key: a });
505
+ i.info("Sanitize", "API config sanitization completed", {
506
+ originalKeys: t.length,
507
+ processedKeys: s,
508
+ filteredKeys: r,
509
+ finalKeys: Object.keys(e).length
510
+ });
511
+ } catch (t) {
512
+ throw i.error("Sanitize", "API config sanitization failed", {
513
+ error: t instanceof Error ? t.message : t
514
+ }), new Error(`API config sanitization failed: ${t instanceof Error ? t.message : "Unknown error"}`);
515
+ }
516
+ return e;
517
+ }, ft = (n) => {
518
+ if (i.debug("Sanitize", "Starting metadata sanitization", { hasMetadata: n != null }), typeof n != "object" || n === null)
519
+ return i.debug("Sanitize", "Metadata is not an object, returning empty object", {
520
+ metadata: n,
521
+ type: typeof n
522
+ }), {};
523
+ try {
524
+ const e = Object.keys(n).length, t = B(n), s = typeof t == "object" && t !== null ? t : {}, r = Object.keys(s).length;
525
+ return i.debug("Sanitize", "Metadata sanitization completed", {
526
+ originalKeys: e,
527
+ finalKeys: r,
528
+ keysFiltered: e - r
529
+ }), s;
530
+ } catch (e) {
531
+ throw i.error("Sanitize", "Metadata sanitization failed", {
532
+ error: e instanceof Error ? e.message : e
533
+ }), new Error(`Metadata sanitization failed: ${e instanceof Error ? e.message : "Unknown error"}`);
534
+ }
535
+ }, mt = (n) => {
536
+ if (typeof n != "object" || n === null)
537
+ return !1;
538
+ for (const e of Object.values(n)) {
539
+ if (e == null)
540
+ continue;
541
+ const t = typeof e;
542
+ if (!(t === "string" || t === "number" || t === "boolean")) {
543
+ if (Array.isArray(e)) {
544
+ if (!e.every((s) => typeof s == "string"))
545
+ return !1;
546
+ continue;
547
+ }
548
+ return !1;
549
+ }
550
+ }
551
+ return !0;
552
+ }, pt = (n) => typeof n != "string" ? {
553
+ valid: !1,
554
+ error: "Event name must be a string"
555
+ } : n.length === 0 ? {
556
+ valid: !1,
557
+ error: "Event name cannot be empty"
558
+ } : n.length > re ? {
559
+ valid: !1,
560
+ error: `Event name is too long (max ${re} characters)`
561
+ } : n.includes("<") || n.includes(">") || n.includes("&") ? {
562
+ valid: !1,
563
+ error: "Event name contains invalid characters"
564
+ } : ["constructor", "prototype", "__proto__", "eval", "function", "var", "let", "const"].includes(n.toLowerCase()) ? {
565
+ valid: !1,
566
+ error: "Event name cannot be a reserved word"
567
+ } : { valid: !0 }, vt = (n, e, t) => {
568
+ const s = ft(e), r = `${t} "${n}" metadata error`;
569
+ if (!mt(s))
570
+ return {
571
+ valid: !1,
572
+ error: `${r}: object has invalid types. Valid types are string, number, boolean or string arrays.`
573
+ };
574
+ let a;
575
+ try {
576
+ a = JSON.stringify(s);
577
+ } catch {
578
+ return {
579
+ valid: !1,
580
+ error: `${r}: object contains circular references or cannot be serialized.`
581
+ };
582
+ }
583
+ if (a.length > ae)
584
+ return {
585
+ valid: !1,
586
+ error: `${r}: object is too large (max ${ae / 1024} KB).`
587
+ };
588
+ if (Object.keys(s).length > oe)
589
+ return {
590
+ valid: !1,
591
+ error: `${r}: object has too many keys (max ${oe} keys).`
592
+ };
593
+ for (const [l, c] of Object.entries(s)) {
594
+ if (Array.isArray(c)) {
595
+ if (c.length > ce)
596
+ return {
597
+ valid: !1,
598
+ error: `${r}: array property "${l}" is too large (max ${ce} items).`
599
+ };
600
+ for (const d of c)
601
+ if (typeof d == "string" && d.length > 500)
602
+ return {
603
+ valid: !1,
604
+ error: `${r}: array property "${l}" contains strings that are too long (max 500 characters).`
605
+ };
606
+ }
607
+ if (typeof c == "string" && c.length > M)
608
+ return {
609
+ valid: !1,
610
+ error: `${r}: property "${l}" is too long (max ${M} characters).`
611
+ };
612
+ }
613
+ return {
614
+ valid: !0,
615
+ sanitizedMetadata: s
616
+ };
617
+ }, St = (n, e) => {
618
+ const t = pt(n);
619
+ if (!t.valid)
620
+ return i.clientError("EventValidation", "Event name validation failed", { eventName: n, error: t.error }), t;
621
+ if (!e)
622
+ return { valid: !0 };
623
+ const s = vt(n, e, "customEvent");
624
+ return s.valid || i.clientError("EventValidation", "Event metadata validation failed", {
625
+ eventName: n,
626
+ error: s.error
627
+ }), s;
628
+ }, te = (n, e = !1) => {
629
+ try {
630
+ const t = new URL(n), s = t.protocol === "https:", r = t.protocol === "http:";
631
+ return s || e && r;
632
+ } catch {
633
+ return !1;
634
+ }
635
+ }, yt = (n, e = !1) => {
636
+ i.debug("URLUtils", "Generating API URL", { projectId: n, allowHttp: e });
637
+ const t = new URL(window.location.href), s = t.hostname, r = s.split(".");
638
+ if (r.length === 0)
639
+ throw i.clientError("URLUtils", "Invalid hostname - no domain parts found", { hostname: s }), new Error("Invalid URL");
640
+ const a = r.slice(-2).join("."), o = e && t.protocol === "http:" ? "http" : "https", l = `${o}://${n}.${a}`;
641
+ if (i.debug("URLUtils", "Generated API URL", {
642
+ originalUrl: window.location.href,
643
+ hostname: s,
644
+ domainParts: r.length,
645
+ cleanDomain: a,
646
+ protocol: o,
647
+ generatedUrl: l
648
+ }), !te(l, e))
649
+ throw i.clientError("URLUtils", "Generated API URL failed validation", {
650
+ apiUrl: l,
651
+ allowHttp: e
652
+ }), new Error("Invalid URL");
653
+ return i.debug("URLUtils", "API URL generation completed successfully", { apiUrl: l }), l;
654
+ }, se = (n, e = []) => {
655
+ i.debug("URLUtils", "Normalizing URL", {
656
+ urlLength: n.length,
657
+ sensitiveParamsCount: e.length
658
+ });
659
+ try {
660
+ const t = new URL(n), s = t.searchParams, r = Array.from(s.keys()).length;
661
+ let a = !1;
662
+ const o = [];
663
+ if (e.forEach((c) => {
664
+ s.has(c) && (s.delete(c), a = !0, o.push(c));
665
+ }), a && i.debug("URLUtils", "Sensitive parameters removed from URL", {
666
+ removedParams: o,
667
+ originalParamCount: r,
668
+ finalParamCount: Array.from(s.keys()).length
669
+ }), !a && n.includes("?"))
670
+ return i.debug("URLUtils", "URL normalization - no changes needed"), n;
671
+ t.search = s.toString();
672
+ const l = t.toString();
673
+ return i.debug("URLUtils", "URL normalization completed", {
674
+ hasChanged: a,
675
+ originalLength: n.length,
676
+ normalizedLength: l.length
677
+ }), l;
678
+ } catch (t) {
679
+ return i.warn("URLUtils", "URL normalization failed, returning original", {
680
+ url: n.slice(0, 100),
681
+ error: t instanceof Error ? t.message : t
682
+ }), n;
683
+ }
684
+ }, bt = (n, e = []) => {
685
+ if (i.debug("URLUtils", "Checking if URL path is excluded", {
686
+ urlLength: n.length,
687
+ excludedPathsCount: e.length
688
+ }), e.length === 0)
689
+ return i.debug("URLUtils", "No excluded paths configured"), !1;
690
+ let t;
691
+ try {
692
+ t = new URL(n, window.location.origin).pathname, i.debug("URLUtils", "Extracted path from URL", { path: t });
693
+ } catch (c) {
694
+ return i.warn("URLUtils", "Failed to parse URL for path exclusion check", {
695
+ url: n.slice(0, 100),
696
+ error: c instanceof Error ? c.message : c
697
+ }), !1;
698
+ }
699
+ const s = (c) => typeof c == "object" && c !== void 0 && typeof c.test == "function", r = (c) => c.replaceAll(/[$()*+.?[\\\]^{|}]/g, "\\$&"), a = (c) => new RegExp(
700
+ "^" + c.split("*").map((d) => r(d)).join(".+") + "$"
701
+ ), o = e.find((c) => {
702
+ try {
703
+ if (s(c)) {
704
+ const u = c.test(t);
705
+ return u && i.debug("URLUtils", "Path matched regex pattern", { path: t, pattern: c.toString() }), u;
706
+ }
707
+ if (c.includes("*")) {
708
+ const u = a(c), g = u.test(t);
709
+ return g && i.debug("URLUtils", "Path matched wildcard pattern", { path: t, pattern: c, regex: u.toString() }), g;
710
+ }
711
+ const d = c === t;
712
+ return d && i.debug("URLUtils", "Path matched exact pattern", { path: t, pattern: c }), d;
713
+ } catch (d) {
714
+ return i.warn("URLUtils", "Error testing exclusion pattern", {
715
+ pattern: c,
716
+ path: t,
717
+ error: d instanceof Error ? d.message : d
718
+ }), !1;
719
+ }
720
+ }), l = !!o;
721
+ return i.debug("URLUtils", "URL path exclusion check completed", {
722
+ path: t,
723
+ isExcluded: l,
724
+ matchedPattern: o ?? null,
725
+ totalPatternsChecked: e.length
726
+ }), l;
727
+ };
728
+ class Et {
729
+ getUrl(e, t = !1) {
730
+ const s = yt(e, t);
731
+ if (!te(s, t))
732
+ throw new Error("Invalid URL");
733
+ return s;
734
+ }
735
+ }
736
+ class wt {
737
+ async get(e, t) {
738
+ if (t.id === C.HttpSkip)
739
+ return i.debug("ConfigManager", "Using special project id"), this.getDefaultConfig(t);
740
+ i.debug("ConfigManager", "Loading config from API", { apiUrl: e, projectId: t.id });
741
+ const s = await this.load(e, t, t.id === C.HttpLocal);
742
+ return i.info("ConfigManager", "Config loaded successfully", {
743
+ projectId: t.id,
744
+ mode: s.mode,
745
+ hasExcludedPaths: !!s.excludedUrlPaths?.length,
746
+ hasGlobalMetadata: !!s.globalMetadata
747
+ }), s;
748
+ }
749
+ async load(e, t, s) {
750
+ try {
751
+ const r = s ? `${window.location.origin}/config` : this.getUrl(e);
752
+ if (!r)
753
+ throw new Error("Config URL is not valid or not allowed");
754
+ const a = await fetch(r, {
755
+ method: "GET",
756
+ headers: { "Content-Type": "application/json" }
757
+ });
758
+ if (!a.ok) {
759
+ const A = `HTTP ${a.status}: ${a.statusText}`;
760
+ throw i.error("ConfigManager", "Config API request failed", {
761
+ status: a.status,
762
+ statusText: a.statusText,
763
+ configUrl: r
764
+ }), new Error(A);
765
+ }
766
+ const o = await a.json();
767
+ if (o == null || typeof o != "object" || Array.isArray(o))
768
+ throw i.error("ConfigManager", "Invalid config API response format", {
769
+ responseType: typeof o,
770
+ isArray: Array.isArray(o)
771
+ }), new Error("Invalid config API response: expected object");
772
+ const l = gt(o), d = { ...{ ...Ae, ...l }, ...t };
773
+ new URLSearchParams(window.location.search).get("qaMode") === "true" && !d.mode && (d.mode = N.QA, i.info("ConfigManager", "QA mode enabled via URL parameter"));
774
+ const y = Object.values(N).includes(d.mode) ? 1 : d.errorSampling ?? 0.1;
775
+ return { ...d, errorSampling: y };
776
+ } catch (r) {
777
+ const a = r instanceof Error ? r.message : "Unknown error";
778
+ throw i.error("ConfigManager", "Failed to load config", { error: a, apiUrl: e }), new Error(`Failed to load config: ${a}`);
779
+ }
780
+ }
781
+ getUrl(e) {
782
+ const s = new URLSearchParams(window.location.search).get("qaMode") === "true";
783
+ let r = `${e}/config`;
784
+ if (s && (r += "?qaMode=true"), !te(r))
785
+ throw i.clientError("ConfigManager", "Invalid config URL provided", { configUrl: r }), new Error("Config URL is not valid or not allowed");
786
+ return r;
787
+ }
788
+ getDefaultConfig(e) {
789
+ return Je({
790
+ ...e,
791
+ errorSampling: 1,
792
+ ...Object.values(C).includes(e.id) && { mode: N.DEBUG }
793
+ });
794
+ }
795
+ }
796
+ class It extends p {
797
+ storeManager;
798
+ queueStorageKey;
799
+ retryDelay = he;
800
+ retryTimeoutId = null;
801
+ lastAsyncSend = 0;
802
+ lastSyncSend = 0;
803
+ constructor(e) {
804
+ super(), this.storeManager = e, this.queueStorageKey = `${tt(this.get("config")?.id)}:${this.get("userId")}`, this.recoverPersistedEvents();
805
+ }
806
+ async sendEventsQueueAsync(e) {
807
+ return this.executeSend(e, () => this.sendQueueAsync(e));
808
+ }
809
+ sendEventsQueueSync(e) {
810
+ return this.executeSendSync(e, () => this.sendQueueSync(e));
811
+ }
812
+ sendEventsQueue(e) {
813
+ return this.executeSendSync(e, () => this.sendQueue(e));
814
+ }
815
+ recoverPersistedEvents() {
816
+ try {
817
+ const e = this.getPersistedData();
818
+ if (!e || !this.isDataRecent(e) || e.events.length === 0) {
819
+ this.clearPersistedEvents();
820
+ return;
821
+ }
822
+ const t = this.createRecoveryBody(e);
823
+ this.sendRecoveredEvents(t) ? (i.info("SenderManager", "Persisted events recovered successfully", {
824
+ eventsCount: e.events.length,
825
+ sessionId: e.sessionId
826
+ }), this.clearPersistedEvents()) : (i.warn("SenderManager", "Failed to recover persisted events, scheduling retry", {
827
+ eventsCount: e.events.length
828
+ }), this.scheduleRetryForRecoveredEvents(t));
829
+ } catch (e) {
830
+ i.error("SenderManager", "Failed to recover persisted events", { error: e });
831
+ }
832
+ }
833
+ stop() {
834
+ this.clearRetryTimeout(), this.resetRetryState();
835
+ }
836
+ /**
837
+ * Sends recovered events without re-deduplication since they were already processed
838
+ */
839
+ sendRecoveredEvents(e) {
840
+ return this.executeSendSync(e, () => this.sendQueue(e));
841
+ }
842
+ /**
843
+ * Schedules retry for recovered events using the specific recovery method
844
+ */
845
+ scheduleRetryForRecoveredEvents(e) {
846
+ this.retryTimeoutId === null && (this.retryTimeoutId = window.setTimeout(() => {
847
+ this.retryTimeoutId = null, this.sendRecoveredEvents(e);
848
+ }, this.retryDelay), this.retryDelay = Math.min(this.retryDelay * 2, ue));
849
+ }
850
+ canSendAsync() {
851
+ return Date.now() - this.lastAsyncSend >= ge;
852
+ }
853
+ canSendSync() {
854
+ return Date.now() - this.lastSyncSend >= ge;
855
+ }
856
+ async sendQueueAsync(e) {
857
+ const { url: t, payload: s } = this.prepareRequest(e);
858
+ try {
859
+ return (await fetch(t, {
860
+ method: "POST",
861
+ headers: {
862
+ "Content-Type": "application/json"
863
+ },
864
+ body: s
865
+ })).ok;
866
+ } catch (r) {
867
+ return i.error("SenderManager", "Failed to send events async", { error: r }), !1;
868
+ }
869
+ }
870
+ sendQueueSync(e) {
871
+ const { url: t, payload: s } = this.prepareRequest(e);
872
+ return this.isSendBeaconAvailable() && navigator.sendBeacon(t, s) ? !0 : this.sendSyncXHR(t, s);
873
+ }
874
+ sendQueue(e) {
875
+ if (!this.isSendBeaconAvailable())
876
+ return !1;
877
+ const { url: t, payload: s } = this.prepareRequest(e);
878
+ return navigator.sendBeacon(t, s);
879
+ }
880
+ sendSyncXHR(e, t) {
881
+ try {
882
+ const s = new XMLHttpRequest();
883
+ return s.open("POST", e, !1), s.setRequestHeader("Content-Type", "application/json"), s.timeout = xe, s.send(t), s.status >= 200 && s.status < 300;
884
+ } catch (s) {
885
+ return i.error("SenderManager", "Sync XHR failed", { error: s }), !1;
886
+ }
887
+ }
888
+ prepareRequest(e) {
889
+ return {
890
+ url: `${this.get("config").id === C.HttpLocal ? window.location.origin : this.get("apiUrl") ?? Ye}/events`,
891
+ payload: JSON.stringify(e)
892
+ };
893
+ }
894
+ getPersistedData() {
895
+ const e = this.storeManager.getItem(this.queueStorageKey);
896
+ return e ? JSON.parse(e) : null;
897
+ }
898
+ isDataRecent(e) {
899
+ return (Date.now() - e.timestamp) / 36e5 < Ge;
900
+ }
901
+ createRecoveryBody(e) {
902
+ return {
903
+ user_id: e.userId,
904
+ session_id: e.sessionId,
905
+ device: e.device,
906
+ events: e.events,
907
+ ...e.global_metadata && { global_metadata: e.global_metadata }
908
+ };
909
+ }
910
+ logQueue(e) {
911
+ i.info("SenderManager", " ⏩ Queue snapshot", e);
912
+ }
913
+ handleSendFailure(e) {
914
+ this.persistFailedEvents(e), this.scheduleRetry(e);
915
+ }
916
+ persistFailedEvents(e) {
917
+ try {
918
+ const t = {
919
+ userId: e.user_id,
920
+ sessionId: e.session_id,
921
+ device: e.device,
922
+ events: e.events,
923
+ timestamp: Date.now(),
924
+ ...e.global_metadata && { global_metadata: e.global_metadata }
925
+ };
926
+ this.storeManager.setItem(this.queueStorageKey, JSON.stringify(t));
927
+ } catch (t) {
928
+ i.error("SenderManager", "Failed to persist events", { error: t });
929
+ }
930
+ }
931
+ clearPersistedEvents() {
932
+ this.storeManager.removeItem(this.queueStorageKey);
933
+ }
934
+ resetRetryState() {
935
+ this.retryDelay = he, this.clearRetryTimeout();
936
+ }
937
+ scheduleRetry(e) {
938
+ this.retryTimeoutId === null && (this.retryTimeoutId = window.setTimeout(() => {
939
+ this.retryTimeoutId = null, this.sendEventsQueue(e);
940
+ }, this.retryDelay), this.retryDelay = Math.min(this.retryDelay * 2, ue));
941
+ }
942
+ async executeSend(e, t) {
943
+ if (this.shouldSkipSend())
944
+ return this.logQueue(e), !0;
945
+ if (!this.canSendAsync())
946
+ return i.info("SenderManager", "⏱️ Rate limited - skipping async send", {
947
+ eventsCount: e.events.length,
948
+ timeSinceLastSend: Date.now() - this.lastAsyncSend
949
+ }), !1;
950
+ i.info("SenderManager", "🌐 Sending events to server (async)", {
951
+ eventsCount: e.events.length,
952
+ sessionId: e.session_id,
953
+ userId: e.user_id
954
+ }), this.lastAsyncSend = Date.now();
955
+ try {
956
+ const s = await t();
957
+ return s ? (i.info("SenderManager", "✅ Successfully sent events to server", {
958
+ eventsCount: e.events.length,
959
+ method: "async"
960
+ }), this.resetRetryState(), this.clearPersistedEvents()) : (i.warn("SenderManager", "Failed to send events", {
961
+ eventsCount: e.events.length,
962
+ method: "async"
963
+ }), this.handleSendFailure(e)), s;
964
+ } catch {
965
+ return this.handleSendFailure(e), !1;
966
+ }
967
+ }
968
+ executeSendSync(e, t) {
969
+ if (this.shouldSkipSend())
970
+ return this.logQueue(e), !0;
971
+ if (!this.canSendSync())
972
+ return i.info("SenderManager", "⏱️ Rate limited - skipping sync send", {
973
+ eventsCount: e.events.length,
974
+ timeSinceLastSend: Date.now() - this.lastSyncSend
975
+ }), !1;
976
+ i.info("SenderManager", "🌐 Sending events to server (sync)", {
977
+ eventsCount: e.events.length,
978
+ sessionId: e.session_id,
979
+ userId: e.user_id,
980
+ method: "sendBeacon/XHR"
981
+ }), this.lastSyncSend = Date.now();
982
+ try {
983
+ const s = t();
984
+ return s ? (i.info("SenderManager", "✅ Successfully sent events to server", {
985
+ eventsCount: e.events.length,
986
+ method: "sync"
987
+ }), this.resetRetryState(), this.clearPersistedEvents()) : (i.warn("SenderManager", "Failed to send events", {
988
+ eventsCount: e.events.length,
989
+ method: "sync"
990
+ }), this.handleSendFailure(e)), s;
991
+ } catch {
992
+ return i.info("SenderManager", "💥 Exception during event sending", {
993
+ eventsCount: e.events.length,
994
+ method: "sync"
995
+ }), this.handleSendFailure(e), !1;
996
+ }
997
+ }
998
+ shouldSkipSend() {
999
+ const { id: e, mode: t } = this.get("config"), s = [N.QA, N.DEBUG];
1000
+ return e === C.HttpSkip ? !0 : !!t && s.includes(t) && e !== C.HttpLocal;
1001
+ }
1002
+ isSendBeaconAvailable() {
1003
+ return typeof navigator.sendBeacon == "function";
1004
+ }
1005
+ clearRetryTimeout() {
1006
+ this.retryTimeoutId !== null && (clearTimeout(this.retryTimeoutId), this.retryTimeoutId = null);
1007
+ }
1008
+ }
1009
+ class Tt extends p {
1010
+ shouldSampleEvent(e, t) {
1011
+ return this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug" ? !0 : e === h.WEB_VITALS ? this.isWebVitalEventSampledIn(t?.type) : this.isSampledIn();
1012
+ }
1013
+ isSampledIn() {
1014
+ const e = this.get("config").samplingRate ?? Ie;
1015
+ return e >= 1 ? !0 : e <= 0 ? !1 : this.getHash(this.get("userId")) % 100 / 100 < e;
1016
+ }
1017
+ isWebVitalEventSampledIn(e) {
1018
+ const t = e === "LONG_TASK", s = t ? De : He;
1019
+ if (s >= 1) return !0;
1020
+ if (s <= 0) return !1;
1021
+ const r = `${this.get("userId")}|${t ? "long_task" : "web_vitals"}`;
1022
+ return this.getHash(r) % 100 / 100 < s;
1023
+ }
1024
+ getHash(e) {
1025
+ let t = 0;
1026
+ for (let s = 0; s < e.length; s++) {
1027
+ const r = e.charCodeAt(s);
1028
+ t = (t << 5) - t + r, t |= 0;
1029
+ }
1030
+ return Math.abs(t);
1031
+ }
1032
+ }
1033
+ class Mt extends p {
1034
+ getEventTagsIds(e, t) {
1035
+ switch (e.type) {
1036
+ case h.PAGE_VIEW:
1037
+ return this.checkEventTypePageView(e, t);
1038
+ case h.CLICK:
1039
+ return this.checkEventTypeClick(e, t);
1040
+ default:
1041
+ return [];
1042
+ }
1043
+ }
1044
+ checkEventTypePageView(e, t) {
1045
+ const s = this.get("config")?.tags?.filter((a) => a.triggerType === h.PAGE_VIEW) ?? [];
1046
+ if (s.length === 0)
1047
+ return [];
1048
+ const r = [];
1049
+ for (const a of s) {
1050
+ const { id: o, logicalOperator: l, conditions: c } = a, d = [];
1051
+ for (const g of c)
1052
+ switch (g.type) {
1053
+ case m.URL_MATCHES: {
1054
+ d.push(this.matchUrlMatches(g, e.page_url));
1055
+ break;
1056
+ }
1057
+ case m.DEVICE_TYPE: {
1058
+ d.push(this.matchDeviceType(g, t));
1059
+ break;
1060
+ }
1061
+ case m.UTM_SOURCE: {
1062
+ d.push(this.matchUtmCondition(g, e.utm?.source));
1063
+ break;
1064
+ }
1065
+ case m.UTM_MEDIUM: {
1066
+ d.push(this.matchUtmCondition(g, e.utm?.medium));
1067
+ break;
1068
+ }
1069
+ case m.UTM_CAMPAIGN: {
1070
+ d.push(this.matchUtmCondition(g, e.utm?.campaign));
1071
+ break;
1072
+ }
1073
+ }
1074
+ let u = !1;
1075
+ u = l === G.AND ? d.every(Boolean) : d.some(Boolean), u && r.push(o);
1076
+ }
1077
+ return r;
1078
+ }
1079
+ checkEventTypeClick(e, t) {
1080
+ const s = this.get("config")?.tags?.filter((a) => a.triggerType === h.CLICK) ?? [];
1081
+ if (s.length === 0)
1082
+ return [];
1083
+ const r = [];
1084
+ for (const a of s) {
1085
+ const { id: o, logicalOperator: l, conditions: c } = a, d = [];
1086
+ for (const g of c) {
1087
+ if (!e.click_data) {
1088
+ d.push(!1);
1089
+ continue;
1090
+ }
1091
+ const y = e.click_data;
1092
+ switch (g.type) {
1093
+ case m.ELEMENT_MATCHES: {
1094
+ d.push(this.matchElementSelector(g, y));
1095
+ break;
1096
+ }
1097
+ case m.DEVICE_TYPE: {
1098
+ d.push(this.matchDeviceType(g, t));
1099
+ break;
1100
+ }
1101
+ case m.URL_MATCHES: {
1102
+ d.push(this.matchUrlMatches(g, e.page_url));
1103
+ break;
1104
+ }
1105
+ case m.UTM_SOURCE: {
1106
+ d.push(this.matchUtmCondition(g, e.utm?.source));
1107
+ break;
1108
+ }
1109
+ case m.UTM_MEDIUM: {
1110
+ d.push(this.matchUtmCondition(g, e.utm?.medium));
1111
+ break;
1112
+ }
1113
+ case m.UTM_CAMPAIGN: {
1114
+ d.push(this.matchUtmCondition(g, e.utm?.campaign));
1115
+ break;
1116
+ }
1117
+ }
1118
+ }
1119
+ let u = !1;
1120
+ u = l === G.AND ? d.every(Boolean) : d.some(Boolean), u && r.push(o);
1121
+ }
1122
+ return r;
1123
+ }
1124
+ matchUrlMatches(e, t) {
1125
+ if (e.type !== m.URL_MATCHES)
1126
+ return !1;
1127
+ const s = e.value.toLowerCase(), r = t.toLowerCase();
1128
+ switch (e.operator) {
1129
+ case f.EQUALS:
1130
+ return r === s;
1131
+ case f.CONTAINS:
1132
+ return r.includes(s);
1133
+ case f.STARTS_WITH:
1134
+ return r.startsWith(s);
1135
+ case f.ENDS_WITH:
1136
+ return r.endsWith(s);
1137
+ case f.REGEX:
1138
+ try {
1139
+ return new RegExp(s, "gi").test(r);
1140
+ } catch {
1141
+ return !1;
1142
+ }
1143
+ default:
1144
+ return !1;
1145
+ }
1146
+ }
1147
+ matchDeviceType(e, t) {
1148
+ if (e.type !== m.DEVICE_TYPE)
1149
+ return !1;
1150
+ const s = e.value.toLowerCase(), r = t.toLowerCase();
1151
+ switch (e.operator) {
1152
+ case f.EQUALS:
1153
+ return r === s;
1154
+ case f.CONTAINS:
1155
+ return r.includes(s);
1156
+ case f.STARTS_WITH:
1157
+ return r.startsWith(s);
1158
+ case f.ENDS_WITH:
1159
+ return r.endsWith(s);
1160
+ case f.REGEX:
1161
+ try {
1162
+ return new RegExp(s, "gi").test(r);
1163
+ } catch {
1164
+ return !1;
1165
+ }
1166
+ default:
1167
+ return !1;
1168
+ }
1169
+ }
1170
+ matchElementSelector(e, t) {
1171
+ if (e.type !== m.ELEMENT_MATCHES)
1172
+ return !1;
1173
+ const s = [
1174
+ t.id ?? "",
1175
+ t.class ?? "",
1176
+ t.tag ?? "",
1177
+ t.text ?? "",
1178
+ t.href ?? "",
1179
+ t.title ?? "",
1180
+ t.alt ?? "",
1181
+ t.role ?? "",
1182
+ t.ariaLabel ?? "",
1183
+ ...Object.values(t.dataAttributes ?? {})
1184
+ ].join(" "), r = e.value.toLowerCase(), a = s.toLowerCase();
1185
+ switch (e.operator) {
1186
+ case f.EQUALS:
1187
+ return this.checkElementFieldEquals(t, r);
1188
+ case f.CONTAINS:
1189
+ return a.includes(r);
1190
+ case f.STARTS_WITH:
1191
+ return a.startsWith(r);
1192
+ case f.ENDS_WITH:
1193
+ return a.endsWith(r);
1194
+ case f.REGEX:
1195
+ try {
1196
+ return new RegExp(r, "gi").test(a);
1197
+ } catch {
1198
+ return !1;
1199
+ }
1200
+ default:
1201
+ return !1;
1202
+ }
1203
+ }
1204
+ matchUtmCondition(e, t) {
1205
+ if (![m.UTM_SOURCE, m.UTM_MEDIUM, m.UTM_CAMPAIGN].includes(
1206
+ e.type
1207
+ ))
1208
+ return !1;
1209
+ const s = t ?? "", r = e.value.toLowerCase(), a = s.toLowerCase();
1210
+ switch (e.operator) {
1211
+ case f.EQUALS:
1212
+ return a === r;
1213
+ case f.CONTAINS:
1214
+ return a.includes(r);
1215
+ case f.STARTS_WITH:
1216
+ return a.startsWith(r);
1217
+ case f.ENDS_WITH:
1218
+ return a.endsWith(r);
1219
+ case f.REGEX:
1220
+ try {
1221
+ return new RegExp(r, "gi").test(a);
1222
+ } catch {
1223
+ return !1;
1224
+ }
1225
+ default:
1226
+ return !1;
1227
+ }
1228
+ }
1229
+ checkElementFieldEquals(e, t) {
1230
+ const s = [
1231
+ e.id,
1232
+ e.class,
1233
+ e.tag,
1234
+ e.text,
1235
+ e.href,
1236
+ e.title,
1237
+ e.alt,
1238
+ e.role,
1239
+ e.ariaLabel
1240
+ ];
1241
+ for (const r of s)
1242
+ if (r) {
1243
+ const a = r.toLowerCase(), o = t.toLowerCase();
1244
+ if (a === o)
1245
+ return !0;
1246
+ }
1247
+ if (e.dataAttributes)
1248
+ for (const r of Object.values(e.dataAttributes)) {
1249
+ const a = r.toLowerCase(), o = t.toLowerCase();
1250
+ if (a === o)
1251
+ return !0;
1252
+ }
1253
+ return !1;
1254
+ }
1255
+ }
1256
+ class At extends p {
1257
+ googleAnalytics;
1258
+ samplingManager;
1259
+ tagsManager;
1260
+ dataSender;
1261
+ storageManager;
1262
+ eventsQueue = [];
1263
+ lastEvent = null;
1264
+ eventsQueueIntervalId = null;
1265
+ intervalActive = !1;
1266
+ // Circuit breaker properties
1267
+ failureCount = 0;
1268
+ MAX_FAILURES = b.MAX_FAILURES;
1269
+ circuitOpen = !1;
1270
+ circuitOpenTime = 0;
1271
+ backoffDelay = b.INITIAL_BACKOFF_DELAY_MS;
1272
+ circuitResetTimeoutId = null;
1273
+ // Event deduplication properties
1274
+ eventFingerprints = /* @__PURE__ */ new Map();
1275
+ // Persistence storage key
1276
+ PERSISTENCE_KEY = "tl:circuit_breaker_events";
1277
+ constructor(e, t = null) {
1278
+ super(), this.storageManager = e, this.googleAnalytics = t, this.samplingManager = new Tt(), this.tagsManager = new Mt(), this.dataSender = new It(e), this.restoreEventsFromStorage(), i.debug("EventManager", "EventManager initialized", {
1279
+ hasGoogleAnalytics: !!t,
1280
+ restoredEventsCount: this.eventsQueue.length
1281
+ });
1282
+ }
1283
+ track({
1284
+ type: e,
1285
+ page_url: t,
1286
+ from_page_url: s,
1287
+ scroll_data: r,
1288
+ click_data: a,
1289
+ custom_event: o,
1290
+ web_vitals: l,
1291
+ session_end_reason: c,
1292
+ session_start_recovered: d
1293
+ }) {
1294
+ if (i.info("EventManager", `📥 Event captured: ${e}`, {
1295
+ type: e,
1296
+ page_url: t,
1297
+ hasCustomEvent: !!o,
1298
+ hasClickData: !!a,
1299
+ hasScrollData: !!r,
1300
+ hasWebVitals: !!l
1301
+ }), !this.samplingManager.shouldSampleEvent(e, l)) {
1302
+ i.debug("EventManager", "Event filtered by sampling", { type: e, samplingActive: !0 });
1303
+ return;
1304
+ }
1305
+ if (this.isDuplicatedEvent({
1306
+ type: e,
1307
+ page_url: t,
1308
+ scroll_data: r,
1309
+ click_data: a,
1310
+ custom_event: o,
1311
+ web_vitals: l,
1312
+ session_end_reason: c,
1313
+ session_start_recovered: d
1314
+ })) {
1315
+ const R = Date.now();
1316
+ if (this.eventsQueue && this.eventsQueue.length > 0) {
1317
+ const P = this.eventsQueue.at(-1);
1318
+ P && (P.timestamp = R);
1319
+ }
1320
+ this.lastEvent && (this.lastEvent.timestamp = R), i.debug("EventManager", "Duplicate event detected, timestamp updated", {
1321
+ type: e,
1322
+ queueLength: this.eventsQueue.length
1323
+ });
1324
+ return;
1325
+ }
1326
+ const g = t || this.get("pageUrl"), y = bt(g, this.get("config").excludedUrlPaths), I = this.get("hasStartSession"), A = e == h.SESSION_END;
1327
+ if (y && (!A || A && !I)) {
1328
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.debug("EventManager", `Event ${e} on excluded route: ${t}`);
1329
+ return;
1330
+ }
1331
+ const U = e === h.SESSION_START;
1332
+ U && this.set("hasStartSession", !0);
1333
+ const ie = U ? rt() : void 0, O = {
1334
+ type: e,
1335
+ page_url: y ? "excluded" : g,
1336
+ timestamp: Date.now(),
1337
+ ...U && { referrer: document.referrer || "Direct" },
1338
+ ...s && !y ? { from_page_url: s } : {},
1339
+ ...r && { scroll_data: r },
1340
+ ...a && { click_data: a },
1341
+ ...o && { custom_event: o },
1342
+ ...ie && { utm: ie },
1343
+ ...l && { web_vitals: l },
1344
+ ...c && { session_end_reason: c },
1345
+ ...d && { session_start_recovered: d }
1346
+ };
1347
+ if (this.get("config")?.tags?.length) {
1348
+ const R = this.tagsManager.getEventTagsIds(O, this.get("device"));
1349
+ R?.length && (O.tags = this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug" ? R.map((P) => ({
1350
+ id: P,
1351
+ key: this.get("config")?.tags?.find((Le) => Le.id === P)?.key ?? ""
1352
+ })) : R);
1353
+ }
1354
+ this.lastEvent = O, this.processAndSend(O);
1355
+ }
1356
+ stop() {
1357
+ this.eventsQueueIntervalId && (clearInterval(this.eventsQueueIntervalId), this.eventsQueueIntervalId = null, this.intervalActive = !1), this.circuitResetTimeoutId && (clearTimeout(this.circuitResetTimeoutId), this.circuitResetTimeoutId = null), this.eventsQueue.length > 0 && this.persistEventsToStorage(), this.eventFingerprints.clear(), this.circuitOpen = !1, this.circuitOpenTime = 0, this.failureCount = 0, this.backoffDelay = b.INITIAL_BACKOFF_DELAY_MS, this.lastEvent = null, this.dataSender.stop();
1358
+ }
1359
+ processAndSend(e) {
1360
+ if (i.info("EventManager", `🔄 Event processed and queued: ${e.type}`, {
1361
+ type: e.type,
1362
+ timestamp: e.timestamp,
1363
+ page_url: e.page_url,
1364
+ queueLengthBefore: this.eventsQueue.length
1365
+ }), this.get("config").ipExcluded) {
1366
+ i.info("EventManager", "❌ Event blocked: IP excluded");
1367
+ return;
1368
+ }
1369
+ if (this.eventsQueue.push(e), this.eventsQueue.length > ne) {
1370
+ const t = this.eventsQueue.shift();
1371
+ i.warn("EventManager", "Event queue overflow, oldest event removed", {
1372
+ maxLength: ne,
1373
+ currentLength: this.eventsQueue.length,
1374
+ removedEventType: t?.type
1375
+ });
1376
+ }
1377
+ if (this.eventsQueueIntervalId || (this.initEventsQueueInterval(), i.info("EventManager", "⏰ Event sender initialized - queue will be sent periodically", {
1378
+ queueLength: this.eventsQueue.length
1379
+ })), this.googleAnalytics && e.type === h.CUSTOM) {
1380
+ const t = e.custom_event;
1381
+ this.trackGoogleAnalyticsEvent(t);
1382
+ }
1383
+ }
1384
+ trackGoogleAnalyticsEvent(e) {
1385
+ this.get("config").mode === "qa" || this.get("config").mode === "debug" ? i.debug("EventManager", `Google Analytics event: ${JSON.stringify(e)}`) : this.googleAnalytics && this.googleAnalytics.trackEvent(e.name, e.metadata ?? {});
1386
+ }
1387
+ initEventsQueueInterval() {
1388
+ if (this.eventsQueueIntervalId || this.intervalActive)
1389
+ return;
1390
+ const e = this.get("config")?.id === "test" ? je : $e;
1391
+ this.eventsQueueIntervalId = window.setInterval(() => {
1392
+ this.eventsQueue.length > 0 && this.sendEventsQueue();
1393
+ }, e), this.intervalActive = !0;
1394
+ }
1395
+ async flushImmediately() {
1396
+ if (this.eventsQueue.length === 0)
1397
+ return !0;
1398
+ const e = this.buildEventsPayload(), t = await this.dataSender.sendEventsQueueAsync(e);
1399
+ return t && (this.eventsQueue = [], this.clearQueueInterval()), t;
1400
+ }
1401
+ flushImmediatelySync() {
1402
+ if (this.eventsQueue.length === 0)
1403
+ return !0;
1404
+ const e = this.buildEventsPayload(), t = this.dataSender.sendEventsQueueSync(e);
1405
+ return t && (this.eventsQueue = [], this.clearQueueInterval()), t;
1406
+ }
1407
+ getQueueLength() {
1408
+ return this.eventsQueue.length;
1409
+ }
1410
+ sendEventsQueue() {
1411
+ if (this.eventsQueue.length === 0)
1412
+ return;
1413
+ if (i.info("EventManager", "📤 Preparing to send event queue", {
1414
+ queueLength: this.eventsQueue.length,
1415
+ hasSessionId: !!this.get("sessionId"),
1416
+ circuitOpen: this.circuitOpen
1417
+ }), this.circuitOpen) {
1418
+ const s = Date.now() - this.circuitOpenTime;
1419
+ if (s >= b.RECOVERY_TIME_MS)
1420
+ this.resetCircuitBreaker(), i.info("EventManager", "Circuit breaker reset after timeout", {
1421
+ timeSinceOpen: s,
1422
+ recoveryTime: b.RECOVERY_TIME_MS
1423
+ });
1424
+ else {
1425
+ i.debug("EventManager", "Circuit breaker is open - skipping event sending", {
1426
+ queueLength: this.eventsQueue.length,
1427
+ failureCount: this.failureCount,
1428
+ timeSinceOpen: s,
1429
+ recoveryTime: b.RECOVERY_TIME_MS
1430
+ });
1431
+ return;
1432
+ }
1433
+ }
1434
+ if (!this.get("sessionId")) {
1435
+ i.info("EventManager", `⏳ Queue waiting: ${this.eventsQueue.length} events waiting for active session`);
1436
+ return;
1437
+ }
1438
+ const e = this.buildEventsPayload();
1439
+ this.dataSender.sendEventsQueue(e) ? (i.info("EventManager", "✅ Event queue sent successfully", {
1440
+ eventsCount: e.events.length,
1441
+ sessionId: e.session_id,
1442
+ uniqueEventsAfterDedup: e.events.length
1443
+ }), this.eventsQueue = [], this.failureCount = 0, this.backoffDelay = b.INITIAL_BACKOFF_DELAY_MS, this.clearPersistedEvents()) : (i.info("EventManager", "❌ Failed to send event queue", {
1444
+ eventsCount: e.events.length,
1445
+ failureCount: this.failureCount + 1,
1446
+ willOpenCircuit: this.failureCount + 1 >= this.MAX_FAILURES
1447
+ }), this.persistEventsToStorage(), this.eventsQueue = [], this.failureCount++, this.failureCount >= this.MAX_FAILURES && this.openCircuitBreaker());
1448
+ }
1449
+ buildEventsPayload() {
1450
+ const e = /* @__PURE__ */ new Map();
1451
+ for (const s of this.eventsQueue) {
1452
+ let r = `${s.type}_${s.page_url}`;
1453
+ s.click_data && (r += `_${s.click_data.x}_${s.click_data.y}`), s.scroll_data && (r += `_${s.scroll_data.depth}_${s.scroll_data.direction}`), s.custom_event && (r += `_${s.custom_event.name}`), s.web_vitals && (r += `_${s.web_vitals.type}`), e.has(r) || e.set(r, s);
1454
+ }
1455
+ const t = [...e.values()];
1456
+ return t.sort((s, r) => s.timestamp - r.timestamp), {
1457
+ user_id: this.get("userId"),
1458
+ session_id: this.get("sessionId"),
1459
+ device: this.get("device"),
1460
+ events: t,
1461
+ ...this.get("config")?.globalMetadata && { global_metadata: this.get("config")?.globalMetadata }
1462
+ };
1463
+ }
1464
+ clearQueueInterval() {
1465
+ this.eventsQueueIntervalId && (clearInterval(this.eventsQueueIntervalId), this.eventsQueueIntervalId = null, this.intervalActive = !1);
1466
+ }
1467
+ getEventFingerprint(e) {
1468
+ const t = `${e.type}_${e.page_url}`;
1469
+ if (e.click_data) {
1470
+ const s = Math.round((e.click_data.x || 0) / F) * F, r = Math.round((e.click_data.y || 0) / F) * F;
1471
+ return `${t}_${s}_${r}_${e.click_data.tag}_${e.click_data.id}`;
1472
+ }
1473
+ return e.scroll_data ? `${t}_${e.scroll_data.depth}_${e.scroll_data.direction}` : e.custom_event ? `${t}_${e.custom_event.name}` : e.web_vitals ? `${t}_${e.web_vitals.type}` : e.session_end_reason ? `${t}_${e.session_end_reason}` : e.session_start_recovered !== void 0 ? `${t}_${e.session_start_recovered}` : t;
1474
+ }
1475
+ isDuplicatedEvent(e) {
1476
+ const t = this.getEventFingerprint(e), s = this.eventFingerprints.get(t) ?? 0, r = Date.now();
1477
+ return r - s < de ? !0 : (this.eventFingerprints.set(t, r), this.cleanupOldFingerprints(), !1);
1478
+ }
1479
+ /**
1480
+ * Cleans up old fingerprints to prevent memory leaks
1481
+ */
1482
+ cleanupOldFingerprints() {
1483
+ if (this.eventFingerprints.size <= Oe)
1484
+ return;
1485
+ const e = Date.now(), t = de * Fe, s = [];
1486
+ for (const [r, a] of this.eventFingerprints)
1487
+ e - a > t && s.push(r);
1488
+ for (const r of s)
1489
+ this.eventFingerprints.delete(r);
1490
+ i.debug("EventManager", "Cleaned up old event fingerprints", {
1491
+ totalFingerprints: this.eventFingerprints.size + s.length,
1492
+ cleanedCount: s.length,
1493
+ remainingCount: this.eventFingerprints.size,
1494
+ cleanupThreshold: t
1495
+ });
1496
+ }
1497
+ /**
1498
+ * Opens the circuit breaker with time-based recovery and event persistence
1499
+ */
1500
+ openCircuitBreaker() {
1501
+ this.circuitOpen = !0, this.circuitOpenTime = Date.now(), this.persistEventsToStorage();
1502
+ const e = this.eventsQueue.length;
1503
+ this.eventsQueue = [], i.warn("EventManager", "Circuit breaker opened with time-based recovery", {
1504
+ maxFailures: this.MAX_FAILURES,
1505
+ persistedEvents: e,
1506
+ failureCount: this.failureCount,
1507
+ recoveryTime: b.RECOVERY_TIME_MS,
1508
+ openTime: this.circuitOpenTime
1509
+ }), this.backoffDelay = Math.min(
1510
+ this.backoffDelay * b.BACKOFF_MULTIPLIER,
1511
+ b.MAX_BACKOFF_DELAY_MS
1512
+ );
1513
+ }
1514
+ /**
1515
+ * Resets the circuit breaker and attempts to restore persisted events
1516
+ */
1517
+ resetCircuitBreaker() {
1518
+ this.circuitOpen = !1, this.circuitOpenTime = 0, this.failureCount = 0, this.circuitResetTimeoutId = null, i.info("EventManager", "Circuit breaker reset - attempting to restore events", {
1519
+ currentQueueLength: this.eventsQueue.length
1520
+ }), this.restoreEventsFromStorage(), i.info("EventManager", "Circuit breaker reset completed", {
1521
+ restoredQueueLength: this.eventsQueue.length,
1522
+ backoffDelay: this.backoffDelay
1523
+ });
1524
+ }
1525
+ /**
1526
+ * Persists current events queue to localStorage for recovery
1527
+ */
1528
+ persistEventsToStorage() {
1529
+ try {
1530
+ if (this.eventsQueue.length === 0)
1531
+ return;
1532
+ const e = {
1533
+ events: this.eventsQueue,
1534
+ timestamp: Date.now(),
1535
+ failureCount: this.failureCount
1536
+ };
1537
+ this.storageManager.setItem(this.PERSISTENCE_KEY, JSON.stringify(e)), i.debug("EventManager", "Events persisted to storage for recovery", {
1538
+ eventsCount: this.eventsQueue.length,
1539
+ failureCount: this.failureCount
1540
+ });
1541
+ } catch (e) {
1542
+ i.warn("EventManager", "Failed to persist events to storage", {
1543
+ error: e instanceof Error ? e.message : "Unknown error",
1544
+ eventsCount: this.eventsQueue.length
1545
+ });
1546
+ }
1547
+ }
1548
+ /**
1549
+ * Restores events from localStorage if available and not expired
1550
+ */
1551
+ restoreEventsFromStorage() {
1552
+ try {
1553
+ const e = this.storageManager.getItem(this.PERSISTENCE_KEY);
1554
+ if (!e)
1555
+ return;
1556
+ const t = JSON.parse(e), s = Date.now(), r = Qe;
1557
+ if (s - t.timestamp > r) {
1558
+ this.clearPersistedEvents(), i.debug("EventManager", "Cleared expired persisted events", {
1559
+ age: s - t.timestamp,
1560
+ maxAge: r
1561
+ });
1562
+ return;
1563
+ }
1564
+ Array.isArray(t.events) && t.events.length > 0 && this.eventsQueue.length === 0 && (this.eventsQueue = t.events, i.info("EventManager", "Restored events from storage", {
1565
+ restoredCount: t.events.length,
1566
+ originalFailureCount: t.failureCount ?? 0
1567
+ }));
1568
+ } catch (e) {
1569
+ i.warn("EventManager", "Failed to restore events from storage", {
1570
+ error: e instanceof Error ? e.message : "Unknown error"
1571
+ }), this.clearPersistedEvents();
1572
+ }
1573
+ }
1574
+ /**
1575
+ * Clears persisted events from localStorage
1576
+ */
1577
+ clearPersistedEvents() {
1578
+ try {
1579
+ this.storageManager.removeItem(this.PERSISTENCE_KEY), i.debug("EventManager", "Cleared persisted events from storage");
1580
+ } catch (e) {
1581
+ i.warn("EventManager", "Failed to clear persisted events", {
1582
+ error: e instanceof Error ? e.message : "Unknown error"
1583
+ });
1584
+ }
1585
+ }
1586
+ }
1587
+ class _t extends p {
1588
+ storageManager;
1589
+ constructor(e) {
1590
+ super(), this.storageManager = e;
1591
+ }
1592
+ getId() {
1593
+ const e = this.storageManager.getItem(ve(this.get("config")?.id));
1594
+ if (e)
1595
+ return e;
1596
+ const t = $();
1597
+ return this.storageManager.setItem(ve(this.get("config")?.id), t), t;
1598
+ }
1599
+ }
1600
+ class Ct {
1601
+ onActivity;
1602
+ options = { passive: !0 };
1603
+ constructor(e) {
1604
+ this.onActivity = e;
1605
+ }
1606
+ setup() {
1607
+ try {
1608
+ window.addEventListener("scroll", this.onActivity, this.options), window.addEventListener("resize", this.onActivity, this.options), window.addEventListener("focus", this.onActivity, this.options);
1609
+ } catch (e) {
1610
+ throw i.error("ActivityListenerManager", "Failed to setup activity listeners", { error: e }), e;
1611
+ }
1612
+ }
1613
+ cleanup() {
1614
+ try {
1615
+ window.removeEventListener("scroll", this.onActivity), window.removeEventListener("resize", this.onActivity), window.removeEventListener("focus", this.onActivity);
1616
+ } catch (e) {
1617
+ i.warn("ActivityListenerManager", "Error during activity listeners cleanup", { error: e });
1618
+ }
1619
+ }
1620
+ }
1621
+ class Lt {
1622
+ onActivity;
1623
+ options = { passive: !0 };
1624
+ motionThreshold;
1625
+ constructor(e, t) {
1626
+ this.onActivity = e, this.motionThreshold = t;
1627
+ }
1628
+ setup() {
1629
+ try {
1630
+ window.addEventListener("touchstart", this.onActivity, this.options), window.addEventListener("touchmove", this.onActivity, this.options), window.addEventListener("touchend", this.onActivity, this.options), window.addEventListener("orientationchange", this.onActivity, this.options), "DeviceMotionEvent" in window && window.addEventListener("devicemotion", this.handleDeviceMotion, this.options);
1631
+ } catch (e) {
1632
+ throw i.error("TouchListenerManager", "Failed to setup touch listeners", { error: e }), e;
1633
+ }
1634
+ }
1635
+ cleanup() {
1636
+ try {
1637
+ window.removeEventListener("touchstart", this.onActivity), window.removeEventListener("touchmove", this.onActivity), window.removeEventListener("touchend", this.onActivity), window.removeEventListener("orientationchange", this.onActivity), "DeviceMotionEvent" in window && window.removeEventListener("devicemotion", this.handleDeviceMotion);
1638
+ } catch (e) {
1639
+ i.warn("TouchListenerManager", "Error during touch listeners cleanup", { error: e });
1640
+ }
1641
+ }
1642
+ handleDeviceMotion = (e) => {
1643
+ try {
1644
+ const t = e.acceleration;
1645
+ t && Math.abs(t.x ?? 0) + Math.abs(t.y ?? 0) + Math.abs(t.z ?? 0) > this.motionThreshold && this.onActivity();
1646
+ } catch (t) {
1647
+ i.warn("TouchListenerManager", "Error handling device motion event", { error: t });
1648
+ }
1649
+ };
1650
+ }
1651
+ class Rt {
1652
+ onActivity;
1653
+ options = { passive: !0 };
1654
+ constructor(e) {
1655
+ this.onActivity = e;
1656
+ }
1657
+ setup() {
1658
+ try {
1659
+ window.addEventListener("mousemove", this.onActivity, this.options), window.addEventListener("mousedown", this.onActivity, this.options), window.addEventListener("wheel", this.onActivity, this.options);
1660
+ } catch (e) {
1661
+ throw i.error("MouseListenerManager", "Failed to setup mouse listeners", { error: e }), e;
1662
+ }
1663
+ }
1664
+ cleanup() {
1665
+ try {
1666
+ window.removeEventListener("mousemove", this.onActivity), window.removeEventListener("mousedown", this.onActivity), window.removeEventListener("wheel", this.onActivity);
1667
+ } catch (e) {
1668
+ i.warn("MouseListenerManager", "Error during mouse listeners cleanup", { error: e });
1669
+ }
1670
+ }
1671
+ }
1672
+ class kt {
1673
+ onActivity;
1674
+ options = { passive: !0 };
1675
+ constructor(e) {
1676
+ this.onActivity = e;
1677
+ }
1678
+ setup() {
1679
+ try {
1680
+ window.addEventListener("keydown", this.onActivity, this.options), window.addEventListener("keypress", this.onActivity, this.options);
1681
+ } catch (e) {
1682
+ throw i.error("KeyboardListenerManager", "Failed to setup keyboard listeners", { error: e }), e;
1683
+ }
1684
+ }
1685
+ cleanup() {
1686
+ try {
1687
+ window.removeEventListener("keydown", this.onActivity), window.removeEventListener("keypress", this.onActivity);
1688
+ } catch (e) {
1689
+ i.warn("KeyboardListenerManager", "Error during keyboard listeners cleanup", { error: e });
1690
+ }
1691
+ }
1692
+ }
1693
+ class Nt {
1694
+ onActivity;
1695
+ onVisibilityChange;
1696
+ isMobile;
1697
+ options = { passive: !0 };
1698
+ constructor(e, t, s) {
1699
+ this.onActivity = e, this.onVisibilityChange = t, this.isMobile = s;
1700
+ }
1701
+ setup() {
1702
+ try {
1703
+ "visibilityState" in document && document.addEventListener("visibilitychange", this.onVisibilityChange, this.options), window.addEventListener("blur", this.onVisibilityChange, this.options), window.addEventListener("focus", this.onActivity, this.options), "onLine" in navigator && (window.addEventListener("online", this.onActivity, this.options), window.addEventListener("offline", this.onVisibilityChange, this.options)), this.isMobile && this.setupMobileEvents();
1704
+ } catch (e) {
1705
+ throw i.error("VisibilityListenerManager", "Failed to setup visibility listeners", { error: e }), e;
1706
+ }
1707
+ }
1708
+ cleanup() {
1709
+ try {
1710
+ "visibilityState" in document && document.removeEventListener("visibilitychange", this.onVisibilityChange), window.removeEventListener("blur", this.onVisibilityChange), window.removeEventListener("focus", this.onActivity), "onLine" in navigator && (window.removeEventListener("online", this.onActivity), window.removeEventListener("offline", this.onVisibilityChange)), this.isMobile && this.cleanupMobileEvents();
1711
+ } catch (e) {
1712
+ i.warn("VisibilityListenerManager", "Error during visibility listeners cleanup", { error: e });
1713
+ }
1714
+ }
1715
+ setupMobileEvents() {
1716
+ try {
1717
+ document.addEventListener("pause", this.onVisibilityChange, this.options), document.addEventListener("resume", this.onActivity, this.options), "orientation" in screen && screen.orientation.addEventListener("change", this.onActivity, this.options), window.addEventListener("pageshow", this.onActivity, this.options), window.addEventListener("pagehide", this.onActivity, this.options);
1718
+ } catch (e) {
1719
+ i.warn("VisibilityListenerManager", "Failed to setup mobile listeners", { error: e });
1720
+ }
1721
+ }
1722
+ cleanupMobileEvents() {
1723
+ try {
1724
+ document.removeEventListener("pause", this.onVisibilityChange), document.removeEventListener("resume", this.onActivity), "orientation" in screen && screen.orientation.removeEventListener("change", this.onActivity), window.removeEventListener("pageshow", this.onActivity), window.removeEventListener("pagehide", this.onActivity);
1725
+ } catch (e) {
1726
+ i.warn("VisibilityListenerManager", "Error during mobile listeners cleanup", { error: e });
1727
+ }
1728
+ }
1729
+ }
1730
+ class Ut {
1731
+ onInactivity;
1732
+ options = { passive: !0 };
1733
+ constructor(e) {
1734
+ this.onInactivity = e;
1735
+ }
1736
+ setup() {
1737
+ try {
1738
+ window.addEventListener("beforeunload", this.onInactivity, this.options), window.addEventListener("pagehide", this.onInactivity, this.options);
1739
+ } catch (e) {
1740
+ throw i.error("UnloadListenerManager", "Failed to setup unload listeners", { error: e }), e;
1741
+ }
1742
+ }
1743
+ cleanup() {
1744
+ try {
1745
+ window.removeEventListener("beforeunload", this.onInactivity), window.removeEventListener("pagehide", this.onInactivity);
1746
+ } catch (e) {
1747
+ i.warn("UnloadListenerManager", "Error during unload listeners cleanup", { error: e });
1748
+ }
1749
+ }
1750
+ }
1751
+ class Ce extends p {
1752
+ config;
1753
+ storageManager;
1754
+ eventManager;
1755
+ projectId;
1756
+ debugMode;
1757
+ constructor(e, t, s, r) {
1758
+ super(), this.storageManager = e, this.eventManager = s ?? null, this.projectId = t, this.debugMode = (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") ?? !1, this.config = {
1759
+ recoveryWindowMs: this.calculateRecoveryWindow(),
1760
+ maxRecoveryAttempts: Ke,
1761
+ contextPreservation: !0,
1762
+ ...r
1763
+ };
1764
+ }
1765
+ /**
1766
+ * Attempt to recover a session
1767
+ */
1768
+ attemptSessionRecovery(e) {
1769
+ this.debugMode && i.debug("SessionRecovery", "Attempting session recovery");
1770
+ const t = this.getStoredRecoveryAttempts(), s = this.getLastRecoveryAttempt();
1771
+ if (!this.canAttemptRecovery(s))
1772
+ return this.debugMode && i.debug(
1773
+ "SessionRecovery",
1774
+ "Session recovery not possible - outside recovery window or max attempts reached"
1775
+ ), {
1776
+ recovered: !1
1777
+ };
1778
+ const r = s?.context;
1779
+ if (!r)
1780
+ return this.debugMode && i.debug("SessionRecovery", "No session context available for recovery"), {
1781
+ recovered: !1
1782
+ };
1783
+ const a = Date.now();
1784
+ if (a - r.lastActivity > this.config.recoveryWindowMs)
1785
+ return this.debugMode && i.debug("SessionRecovery", "Session recovery failed - outside recovery window"), {
1786
+ recovered: !1
1787
+ };
1788
+ const l = r.sessionId, c = (s?.attempt ?? 0) + 1, d = {
1789
+ sessionId: e ?? l,
1790
+ timestamp: a,
1791
+ attempt: c,
1792
+ context: {
1793
+ ...r,
1794
+ recoveryAttempts: c,
1795
+ lastActivity: a
1796
+ }
1797
+ };
1798
+ return t.push(d), this.storeRecoveryAttempts(t), this.debugMode && i.debug("SessionRecovery", `Session recovery successful: recovery of session ${l}`), {
1799
+ recovered: !0,
1800
+ recoveredSessionId: l,
1801
+ context: d.context
1802
+ };
1803
+ }
1804
+ /**
1805
+ * Calculate the recovery window with bounds checking
1806
+ */
1807
+ calculateRecoveryWindow() {
1808
+ const t = (this.get("config")?.sessionTimeout ?? L) * Xe, s = Math.max(
1809
+ Math.min(t, X),
1810
+ K
1811
+ );
1812
+ return this.debugMode && (t > X ? i.warn(
1813
+ "SessionRecovery",
1814
+ `Recovery window capped at ${X}ms (24h). Calculated: ${t}ms`
1815
+ ) : t < K && i.warn(
1816
+ "SessionRecovery",
1817
+ `Recovery window increased to minimum ${K}ms (2min). Calculated: ${t}ms`
1818
+ )), s;
1819
+ }
1820
+ /**
1821
+ * Check if session recovery can be attempted
1822
+ */
1823
+ canAttemptRecovery(e) {
1824
+ return e ? !(Date.now() - e.context.lastActivity > this.config.recoveryWindowMs || e.attempt >= this.config.maxRecoveryAttempts) : !0;
1825
+ }
1826
+ /**
1827
+ * Store session context for potential recovery
1828
+ */
1829
+ storeSessionContextForRecovery(e) {
1830
+ try {
1831
+ const t = this.getStoredRecoveryAttempts(), s = {
1832
+ sessionId: e.sessionId,
1833
+ timestamp: Date.now(),
1834
+ attempt: 0,
1835
+ context: e
1836
+ };
1837
+ t.push(s);
1838
+ const r = 5;
1839
+ t.length > r && t.splice(0, t.length - r), this.storeRecoveryAttempts(t), this.debugMode && i.debug("SessionRecovery", `Stored session context for recovery: ${e.sessionId}`);
1840
+ } catch (t) {
1841
+ this.debugMode && i.warn("SessionRecovery", "Failed to store session context for recovery", { error: t });
1842
+ }
1843
+ }
1844
+ /**
1845
+ * Get stored recovery attempts
1846
+ */
1847
+ getStoredRecoveryAttempts() {
1848
+ try {
1849
+ const e = this.storageManager.getItem(V(this.projectId));
1850
+ return e ? JSON.parse(e) : [];
1851
+ } catch (e) {
1852
+ if (this.debugMode) {
1853
+ const t = this.storageManager.getItem(V(this.projectId));
1854
+ i.warn(
1855
+ "SessionRecovery",
1856
+ `Failed to parse stored recovery attempts for projectId ${this.projectId}. Data: ${t}`,
1857
+ { error: e }
1858
+ );
1859
+ }
1860
+ return [];
1861
+ }
1862
+ }
1863
+ /**
1864
+ * Store recovery attempts
1865
+ */
1866
+ storeRecoveryAttempts(e) {
1867
+ try {
1868
+ this.storageManager.setItem(V(this.projectId), JSON.stringify(e));
1869
+ } catch (t) {
1870
+ this.debugMode && i.warn("SessionRecovery", "Failed to store recovery attempts", { error: t });
1871
+ }
1872
+ }
1873
+ /**
1874
+ * Get the last recovery attempt
1875
+ */
1876
+ getLastRecoveryAttempt() {
1877
+ const e = this.getStoredRecoveryAttempts();
1878
+ return e.length > 0 ? e[e.length - 1] : null;
1879
+ }
1880
+ /**
1881
+ * Clean up old recovery attempts
1882
+ */
1883
+ cleanupOldRecoveryAttempts() {
1884
+ const e = this.getStoredRecoveryAttempts(), t = Date.now(), s = e.filter((r) => t - r.timestamp <= this.config.recoveryWindowMs);
1885
+ s.length !== e.length && (this.storeRecoveryAttempts(s), this.debugMode && i.debug("SessionRecovery", `Cleaned up ${e.length - s.length} old recovery attempts`));
1886
+ }
1887
+ /**
1888
+ * Check if there's a recoverable session.
1889
+ * Returns false when no recovery attempts are stored.
1890
+ */
1891
+ hasRecoverableSession() {
1892
+ const e = this.getLastRecoveryAttempt();
1893
+ return e ? this.canAttemptRecovery(e) : !1;
1894
+ }
1895
+ /**
1896
+ * Get recovery window in milliseconds
1897
+ */
1898
+ getRecoveryWindowMs() {
1899
+ return this.config.recoveryWindowMs;
1900
+ }
1901
+ /**
1902
+ * Get max recovery attempts
1903
+ */
1904
+ getMaxRecoveryAttempts() {
1905
+ return this.config.maxRecoveryAttempts;
1906
+ }
1907
+ /**
1908
+ * Clear all stored recovery data
1909
+ */
1910
+ clearRecoveryData() {
1911
+ this.storageManager.removeItem(V(this.projectId)), this.debugMode && i.debug("SessionRecovery", "Cleared all recovery data");
1912
+ }
1913
+ }
1914
+ class Pt extends p {
1915
+ config;
1916
+ eventManager = null;
1917
+ storageManager = null;
1918
+ listenerManagers = [];
1919
+ deviceCapabilities;
1920
+ onActivity;
1921
+ onInactivity;
1922
+ // Recovery manager
1923
+ recoveryManager = null;
1924
+ isSessionActive = !1;
1925
+ lastActivityTime = 0;
1926
+ inactivityTimer = null;
1927
+ sessionStartTime = 0;
1928
+ throttleTimeout = null;
1929
+ // Track visibility change timeout for proper cleanup
1930
+ visibilityChangeTimeout = null;
1931
+ // Session End Management
1932
+ pendingSessionEnd = !1;
1933
+ sessionEndPromise = null;
1934
+ sessionEndLock = Promise.resolve({
1935
+ success: !0,
1936
+ reason: "manual_stop",
1937
+ timestamp: Date.now(),
1938
+ eventsFlushed: 0,
1939
+ method: "async"
1940
+ });
1941
+ cleanupHandlers = [];
1942
+ sessionEndConfig;
1943
+ sessionEndReason = null;
1944
+ sessionEndPriority = {
1945
+ page_unload: 4,
1946
+ manual_stop: 3,
1947
+ orphaned_cleanup: 2,
1948
+ inactivity: 1,
1949
+ tab_closed: 0
1950
+ };
1951
+ sessionEndStats = {
1952
+ totalSessionEnds: 0,
1953
+ successfulEnds: 0,
1954
+ failedEnds: 0,
1955
+ duplicatePrevented: 0,
1956
+ reasonCounts: {
1957
+ inactivity: 0,
1958
+ page_unload: 0,
1959
+ manual_stop: 0,
1960
+ orphaned_cleanup: 0,
1961
+ tab_closed: 0
1962
+ }
1963
+ };
1964
+ // Session health monitoring
1965
+ sessionHealth = {
1966
+ recoveryAttempts: 0,
1967
+ sessionTimeouts: 0,
1968
+ crossTabConflicts: 0,
1969
+ lastHealthCheck: Date.now()
1970
+ };
1971
+ constructor(e, t, s, r, a) {
1972
+ super(), this.config = {
1973
+ throttleDelay: Te,
1974
+ visibilityTimeout: Ve,
1975
+ motionThreshold: Ne,
1976
+ timeout: this.get("config")?.sessionTimeout ?? L
1977
+ }, this.sessionEndConfig = {
1978
+ enablePageUnloadHandlers: !0,
1979
+ syncTimeoutMs: 1e3,
1980
+ maxRetries: 2,
1981
+ debugMode: !1,
1982
+ ...a
1983
+ }, this.onActivity = e, this.onInactivity = t, this.eventManager = s ?? null, this.storageManager = r ?? null, this.deviceCapabilities = this.detectDeviceCapabilities(), this.initializeRecoveryManager(), this.initializeListenerManagers(), this.setupAllListeners(), this.sessionEndConfig.enablePageUnloadHandlers && this.setupPageUnloadHandlers(), i.debug("SessionManager", "SessionManager initialized", {
1984
+ sessionTimeout: this.config.timeout,
1985
+ deviceCapabilities: this.deviceCapabilities,
1986
+ unloadHandlersEnabled: this.sessionEndConfig.enablePageUnloadHandlers
1987
+ });
1988
+ }
1989
+ /**
1990
+ * Initialize recovery manager
1991
+ */
1992
+ initializeRecoveryManager() {
1993
+ if (!this.storageManager) return;
1994
+ const e = this.get("config")?.id;
1995
+ if (e)
1996
+ try {
1997
+ this.recoveryManager = new Ce(this.storageManager, e, this.eventManager ?? void 0), i.debug("SessionManager", "Recovery manager initialized", { projectId: e });
1998
+ } catch (t) {
1999
+ i.error("SessionManager", "Failed to initialize recovery manager", { error: t, projectId: e });
2000
+ }
2001
+ }
2002
+ /**
2003
+ * Store session context for recovery
2004
+ */
2005
+ storeSessionContextForRecovery() {
2006
+ if (!this.recoveryManager) return;
2007
+ const e = this.get("sessionId");
2008
+ if (!e) return;
2009
+ const t = {
2010
+ sessionId: e,
2011
+ startTime: this.sessionStartTime,
2012
+ lastActivity: this.lastActivityTime,
2013
+ tabCount: 1,
2014
+ // This will be updated by cross-tab manager
2015
+ recoveryAttempts: 0,
2016
+ metadata: {
2017
+ userAgent: navigator.userAgent,
2018
+ pageUrl: this.get("pageUrl")
2019
+ }
2020
+ };
2021
+ this.recoveryManager.storeSessionContextForRecovery(t);
2022
+ }
2023
+ startSession() {
2024
+ const e = Date.now();
2025
+ let t = "", s = !1;
2026
+ if (this.recoveryManager?.hasRecoverableSession()) {
2027
+ const r = this.recoveryManager.attemptSessionRecovery();
2028
+ r.recovered && r.recoveredSessionId && (t = r.recoveredSessionId, s = !0, this.trackSessionHealth("recovery"), r.context ? (this.sessionStartTime = r.context.startTime, this.lastActivityTime = e) : (this.sessionStartTime = e, this.lastActivityTime = e), i.info("SessionManager", "Session successfully recovered", {
2029
+ sessionId: t,
2030
+ recoveryAttempts: this.sessionHealth.recoveryAttempts
2031
+ }));
2032
+ }
2033
+ return s || (t = $(), this.sessionStartTime = e, this.lastActivityTime = e, i.info("SessionManager", "New session started", { sessionId: t })), this.isSessionActive = !0, this.resetInactivityTimer(), this.storeSessionContextForRecovery(), { sessionId: t, recovered: s };
2034
+ }
2035
+ endSession() {
2036
+ if (this.sessionStartTime === 0)
2037
+ return 0;
2038
+ const e = Date.now() - this.sessionStartTime;
2039
+ return this.sessionStartTime = 0, this.isSessionActive = !1, this.inactivityTimer && (clearTimeout(this.inactivityTimer), this.inactivityTimer = null), e;
2040
+ }
2041
+ destroy() {
2042
+ this.clearTimers(), this.cleanupAllListeners(), this.resetState(), this.cleanupHandlers.forEach((e) => e()), this.cleanupHandlers = [], this.pendingSessionEnd = !1, this.sessionEndPromise = null, this.sessionEndLock = Promise.resolve({
2043
+ success: !0,
2044
+ reason: "manual_stop",
2045
+ timestamp: Date.now(),
2046
+ eventsFlushed: 0,
2047
+ method: "async"
2048
+ }), this.recoveryManager && (this.recoveryManager.cleanupOldRecoveryAttempts(), this.recoveryManager = null);
2049
+ }
2050
+ detectDeviceCapabilities() {
2051
+ const e = "ontouchstart" in window || navigator.maxTouchPoints > 0, t = window.matchMedia("(pointer: fine)").matches, s = !window.matchMedia("(pointer: coarse)").matches, r = we() === E.Mobile;
2052
+ return { hasTouch: e, hasMouse: t, hasKeyboard: s, isMobile: r };
2053
+ }
2054
+ initializeListenerManagers() {
2055
+ this.listenerManagers.push(new Ct(this.handleActivity)), this.deviceCapabilities.hasTouch && this.listenerManagers.push(new Lt(this.handleActivity, this.config.motionThreshold)), this.deviceCapabilities.hasMouse && this.listenerManagers.push(new Rt(this.handleActivity)), this.deviceCapabilities.hasKeyboard && this.listenerManagers.push(new kt(this.handleActivity)), this.listenerManagers.push(
2056
+ new Nt(this.handleActivity, this.handleVisibilityChange, this.deviceCapabilities.isMobile)
2057
+ ), this.listenerManagers.push(new Ut(this.handleInactivity));
2058
+ }
2059
+ setupAllListeners() {
2060
+ this.listenerManagers.forEach((e) => e.setup());
2061
+ }
2062
+ cleanupAllListeners() {
2063
+ this.listenerManagers.forEach((e) => e.cleanup());
2064
+ }
2065
+ clearTimers() {
2066
+ this.inactivityTimer && (clearTimeout(this.inactivityTimer), this.inactivityTimer = null), this.throttleTimeout && (clearTimeout(this.throttleTimeout), this.throttleTimeout = null);
2067
+ }
2068
+ resetState() {
2069
+ this.isSessionActive = !1, this.lastActivityTime = 0, this.sessionStartTime = 0;
2070
+ }
2071
+ handleActivity = () => {
2072
+ const e = Date.now();
2073
+ e - this.lastActivityTime < this.config.throttleDelay || (this.lastActivityTime = e, this.isSessionActive ? (this.onActivity(), this.resetInactivityTimer()) : (this.throttleTimeout && (clearTimeout(this.throttleTimeout), this.throttleTimeout = null), this.throttleTimeout = window.setTimeout(() => {
2074
+ this.onActivity(), this.throttleTimeout = null;
2075
+ }, 100)));
2076
+ };
2077
+ handleInactivity = () => {
2078
+ this.trackSessionHealth("timeout"), this.onInactivity();
2079
+ };
2080
+ handleVisibilityChange = () => {
2081
+ document.hidden ? this.isSessionActive && (this.inactivityTimer && (clearTimeout(this.inactivityTimer), this.inactivityTimer = null), this.inactivityTimer = window.setTimeout(this.handleInactivity, this.config.visibilityTimeout)) : this.handleActivity();
2082
+ };
2083
+ resetInactivityTimer = () => {
2084
+ this.inactivityTimer && (clearTimeout(this.inactivityTimer), this.inactivityTimer = null), this.isSessionActive && (this.inactivityTimer = window.setTimeout(() => {
2085
+ this.handleInactivity();
2086
+ }, this.config.timeout));
2087
+ };
2088
+ clearInactivityTimer() {
2089
+ this.inactivityTimer && (clearTimeout(this.inactivityTimer), this.inactivityTimer = null);
2090
+ }
2091
+ shouldProceedWithSessionEnd(e) {
2092
+ return !this.sessionEndReason || this.sessionEndPriority[e] > this.sessionEndPriority[this.sessionEndReason];
2093
+ }
2094
+ async waitForCompletion() {
2095
+ return this.sessionEndPromise ? await this.sessionEndPromise : {
2096
+ success: !1,
2097
+ reason: "inactivity",
2098
+ timestamp: Date.now(),
2099
+ eventsFlushed: 0,
2100
+ method: "async"
2101
+ };
2102
+ }
2103
+ async endSessionManaged(e) {
2104
+ return this.sessionEndLock = this.sessionEndLock.then(async () => {
2105
+ if (this.sessionEndStats.totalSessionEnds++, this.sessionEndStats.reasonCounts[e]++, this.pendingSessionEnd)
2106
+ return this.sessionEndStats.duplicatePrevented++, i.debug("SessionManager", "Session end already pending, waiting for completion", { reason: e }), this.waitForCompletion();
2107
+ if (!this.shouldProceedWithSessionEnd(e))
2108
+ return this.sessionEndConfig.debugMode && i.debug(
2109
+ "SessionManager",
2110
+ `Session end skipped due to lower priority. Current: ${this.sessionEndReason}, Requested: ${e}`
2111
+ ), {
2112
+ success: !1,
2113
+ reason: e,
2114
+ timestamp: Date.now(),
2115
+ eventsFlushed: 0,
2116
+ method: "async"
2117
+ };
2118
+ this.sessionEndReason = e, this.pendingSessionEnd = !0, this.sessionEndPromise = this.performSessionEnd(e, "async");
2119
+ try {
2120
+ return await this.sessionEndPromise;
2121
+ } finally {
2122
+ this.pendingSessionEnd = !1, this.sessionEndPromise = null, this.sessionEndReason = null;
2123
+ }
2124
+ });
2125
+ }
2126
+ endSessionSafely(e, t) {
2127
+ return t?.forceSync ?? (t?.allowSync && ["page_unload", "tab_closed"].includes(e)) ? this.endSessionManagedSync(e) : this.endSessionManaged(e);
2128
+ }
2129
+ isPendingSessionEnd() {
2130
+ return this.pendingSessionEnd;
2131
+ }
2132
+ /**
2133
+ * Track session health events for monitoring and diagnostics
2134
+ */
2135
+ trackSessionHealth(e) {
2136
+ const t = Date.now();
2137
+ switch (e) {
2138
+ case "recovery":
2139
+ this.sessionHealth.recoveryAttempts++;
2140
+ break;
2141
+ case "timeout":
2142
+ this.sessionHealth.sessionTimeouts++;
2143
+ break;
2144
+ case "conflict":
2145
+ this.sessionHealth.crossTabConflicts++;
2146
+ break;
2147
+ }
2148
+ this.sessionHealth.lastHealthCheck = t, this.sessionHealth.recoveryAttempts > 3 && this.eventManager && (this.eventManager.track({
2149
+ type: h.CUSTOM,
2150
+ custom_event: {
2151
+ name: "session_health_degraded",
2152
+ metadata: {
2153
+ ...this.sessionHealth,
2154
+ event_trigger: e
2155
+ }
2156
+ }
2157
+ }), this.sessionEndConfig.debugMode && i.warn(
2158
+ "SessionManager",
2159
+ `Session health degraded: ${this.sessionHealth.recoveryAttempts} recovery attempts`
2160
+ )), this.sessionEndConfig.debugMode && i.debug("SessionManager", `Session health event tracked: ${e}`);
2161
+ }
2162
+ async performSessionEnd(e, t) {
2163
+ const s = Date.now();
2164
+ let r = 0;
2165
+ try {
2166
+ if (i.info("SessionManager", "Starting session end", { method: t, reason: e, timestamp: s }), this.eventManager) {
2167
+ this.eventManager.track({
2168
+ type: h.SESSION_END,
2169
+ session_end_reason: e
2170
+ }), r = this.eventManager.getQueueLength();
2171
+ const o = await this.eventManager.flushImmediately();
2172
+ this.cleanupSession();
2173
+ const l = {
2174
+ success: o,
2175
+ reason: e,
2176
+ timestamp: s,
2177
+ eventsFlushed: r,
2178
+ method: t
2179
+ };
2180
+ return o ? this.sessionEndStats.successfulEnds++ : this.sessionEndStats.failedEnds++, l;
2181
+ }
2182
+ this.cleanupSession();
2183
+ const a = {
2184
+ success: !0,
2185
+ reason: e,
2186
+ timestamp: s,
2187
+ eventsFlushed: 0,
2188
+ method: t
2189
+ };
2190
+ return this.sessionEndStats.successfulEnds++, a;
2191
+ } catch (a) {
2192
+ return this.sessionEndStats.failedEnds++, i.error("SessionManager", "Session end failed", { error: a, reason: e, method: t }), this.cleanupSession(), {
2193
+ success: !1,
2194
+ reason: e,
2195
+ timestamp: s,
2196
+ eventsFlushed: r,
2197
+ method: t
2198
+ };
2199
+ }
2200
+ }
2201
+ cleanupSession() {
2202
+ this.endSession(), this.clearTimers(), this.set("sessionId", null), this.set("hasStartSession", !1);
2203
+ }
2204
+ endSessionManagedSync(e) {
2205
+ if (this.sessionEndStats.totalSessionEnds++, this.sessionEndStats.reasonCounts[e]++, this.pendingSessionEnd && (this.sessionEndStats.duplicatePrevented++, i.warn("SessionManager", "Sync session end called while async end pending", { reason: e })), !this.shouldProceedWithSessionEnd(e))
2206
+ return this.sessionEndConfig.debugMode && i.debug(
2207
+ "SessionManager",
2208
+ `Sync session end skipped due to lower priority. Current: ${this.sessionEndReason}, Requested: ${e}`
2209
+ ), {
2210
+ success: !1,
2211
+ reason: e,
2212
+ timestamp: Date.now(),
2213
+ eventsFlushed: 0,
2214
+ method: "sync"
2215
+ };
2216
+ this.sessionEndReason = e, this.pendingSessionEnd = !0;
2217
+ try {
2218
+ return this.performSessionEndSync(e);
2219
+ } finally {
2220
+ this.pendingSessionEnd = !1, this.sessionEndPromise = null, this.sessionEndReason = null;
2221
+ }
2222
+ }
2223
+ performSessionEndSync(e) {
2224
+ const t = Date.now();
2225
+ let s = 0;
2226
+ try {
2227
+ if (this.eventManager) {
2228
+ this.eventManager.track({
2229
+ type: h.SESSION_END,
2230
+ session_end_reason: e
2231
+ }), s = this.eventManager.getQueueLength();
2232
+ const a = this.eventManager.flushImmediatelySync();
2233
+ this.cleanupSession();
2234
+ const o = {
2235
+ success: a,
2236
+ reason: e,
2237
+ timestamp: t,
2238
+ eventsFlushed: s,
2239
+ method: "sync"
2240
+ };
2241
+ return a ? this.sessionEndStats.successfulEnds++ : this.sessionEndStats.failedEnds++, o;
2242
+ }
2243
+ this.cleanupSession();
2244
+ const r = {
2245
+ success: !0,
2246
+ reason: e,
2247
+ timestamp: t,
2248
+ eventsFlushed: 0,
2249
+ method: "sync"
2250
+ };
2251
+ return this.sessionEndStats.successfulEnds++, r;
2252
+ } catch (r) {
2253
+ return this.sessionEndStats.failedEnds++, this.cleanupSession(), i.error("SessionManager", "Sync session end failed", { error: r, reason: e }), {
2254
+ success: !1,
2255
+ reason: e,
2256
+ timestamp: t,
2257
+ eventsFlushed: s,
2258
+ method: "sync"
2259
+ };
2260
+ }
2261
+ }
2262
+ setupPageUnloadHandlers() {
2263
+ let e = !1;
2264
+ const t = () => {
2265
+ e || !this.get("sessionId") || (e = !0, this.clearInactivityTimer(), this.endSessionSafely("page_unload", { forceSync: !0 }));
2266
+ }, s = () => {
2267
+ t();
2268
+ }, r = (o) => {
2269
+ o.persisted || t();
2270
+ }, a = () => {
2271
+ document.visibilityState === "hidden" && this.get("sessionId") && !e && (this.visibilityChangeTimeout = window.setTimeout(() => {
2272
+ document.visibilityState === "hidden" && this.get("sessionId") && !e && t(), this.visibilityChangeTimeout = null;
2273
+ }, 1e3));
2274
+ };
2275
+ window.addEventListener("beforeunload", s), window.addEventListener("pagehide", r), document.addEventListener("visibilitychange", a), this.cleanupHandlers.push(
2276
+ () => window.removeEventListener("beforeunload", s),
2277
+ () => window.removeEventListener("pagehide", r),
2278
+ () => document.removeEventListener("visibilitychange", a),
2279
+ () => {
2280
+ this.visibilityChangeTimeout && (clearTimeout(this.visibilityChangeTimeout), this.visibilityChangeTimeout = null);
2281
+ }
2282
+ );
2283
+ }
2284
+ }
2285
+ class Ht extends p {
2286
+ constructor(e, t, s, r) {
2287
+ super(), this.callbacks = r, this.storageManager = e, this.projectId = t, this.tabId = $(), this.config = {
2288
+ tabHeartbeatIntervalMs: qe,
2289
+ tabElectionTimeoutMs: We,
2290
+ debugMode: (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") ?? !1,
2291
+ ...s
2292
+ }, this.tabInfo = {
2293
+ id: this.tabId,
2294
+ lastHeartbeat: Date.now(),
2295
+ isLeader: !1,
2296
+ sessionId: "",
2297
+ startTime: Date.now()
2298
+ }, this.broadcastChannel = this.initializeBroadcastChannel(), this.initialize();
2299
+ }
2300
+ config;
2301
+ storageManager;
2302
+ broadcastChannel;
2303
+ tabId;
2304
+ tabInfo;
2305
+ projectId;
2306
+ leaderTabId = null;
2307
+ isTabLeader = !1;
2308
+ heartbeatInterval = null;
2309
+ electionTimeout = null;
2310
+ cleanupTimeout = null;
2311
+ sessionEnded = !1;
2312
+ // Additional timeout tracking for proper cleanup
2313
+ fallbackLeadershipTimeout = null;
2314
+ electionDelayTimeout = null;
2315
+ tabInfoCleanupTimeout = null;
2316
+ closingAnnouncementTimeout = null;
2317
+ leaderHealthCheckInterval = null;
2318
+ lastHeartbeatSent = 0;
2319
+ /**
2320
+ * Initialize BroadcastChannel if supported
2321
+ */
2322
+ initializeBroadcastChannel() {
2323
+ if (!this.isBroadcastChannelSupported())
2324
+ return null;
2325
+ try {
2326
+ const e = new BroadcastChannel(it(this.projectId));
2327
+ return this.setupBroadcastListeners(e), e;
2328
+ } catch (e) {
2329
+ return this.config.debugMode && i.warn("CrossTabSession", "Failed to initialize BroadcastChannel", { error: e }), null;
2330
+ }
2331
+ }
2332
+ /**
2333
+ * Initialize the cross-tab session manager
2334
+ */
2335
+ initialize() {
2336
+ if (!this.broadcastChannel) {
2337
+ this.becomeLeader();
2338
+ return;
2339
+ }
2340
+ const e = this.getStoredSessionContext();
2341
+ e ? this.tryJoinExistingSession(e) : this.startLeaderElection(), this.startHeartbeat(), this.broadcastChannel && this.setupLeadershipFallback();
2342
+ }
2343
+ /**
2344
+ * Check if this tab should be the session leader
2345
+ */
2346
+ tryJoinExistingSession(e) {
2347
+ this.config.debugMode && i.debug("CrossTabSession", `Attempting to join existing session: ${e.sessionId}`), this.tabInfo.sessionId = e.sessionId, this.requestLeadershipStatus(), e.tabCount += 1, e.lastActivity = Date.now(), this.storeSessionContext(e), this.callbacks?.onTabActivity && this.callbacks.onTabActivity();
2348
+ }
2349
+ /**
2350
+ * Request leadership status from other tabs
2351
+ */
2352
+ requestLeadershipStatus() {
2353
+ if (!this.broadcastChannel) return;
2354
+ this.electionTimeout && (clearTimeout(this.electionTimeout), this.electionTimeout = null);
2355
+ const e = {
2356
+ type: "election_request",
2357
+ tabId: this.tabId,
2358
+ sessionId: this.tabInfo.sessionId,
2359
+ timestamp: Date.now()
2360
+ };
2361
+ this.broadcastChannel.postMessage(e);
2362
+ const t = Math.floor(Math.random() * 500);
2363
+ this.electionTimeout = window.setTimeout(() => {
2364
+ this.isTabLeader || this.becomeLeader();
2365
+ }, this.config.tabElectionTimeoutMs + t);
2366
+ }
2367
+ /**
2368
+ * Start leader election process with debouncing to prevent excessive elections
2369
+ */
2370
+ startLeaderElection() {
2371
+ if (this.electionTimeout) {
2372
+ this.config.debugMode && i.debug("CrossTabSession", "Leader election already in progress, skipping");
2373
+ return;
2374
+ }
2375
+ this.config.debugMode && i.debug("CrossTabSession", "Starting leader election");
2376
+ const e = Math.floor(Math.random() * 50) + 10;
2377
+ this.electionTimeout = window.setTimeout(() => {
2378
+ this.electionTimeout = null, this.requestLeadershipStatus();
2379
+ }, e);
2380
+ }
2381
+ /**
2382
+ * Become the session leader
2383
+ */
2384
+ becomeLeader() {
2385
+ if (!this.isTabLeader) {
2386
+ if (this.isTabLeader = !0, this.tabInfo.isLeader = !0, this.leaderTabId = this.tabId, this.config.debugMode && i.debug("CrossTabSession", `Tab ${this.tabId} became session leader`), this.electionTimeout && (clearTimeout(this.electionTimeout), this.electionTimeout = null), this.tabInfo.sessionId) {
2387
+ const e = this.getStoredSessionContext();
2388
+ e && (e.lastActivity = Date.now(), this.storeSessionContext(e));
2389
+ } else {
2390
+ const e = $();
2391
+ this.tabInfo.sessionId = e;
2392
+ const t = {
2393
+ sessionId: e,
2394
+ startTime: Date.now(),
2395
+ lastActivity: Date.now(),
2396
+ tabCount: 1,
2397
+ recoveryAttempts: 0
2398
+ };
2399
+ this.storeSessionContext(t), this.callbacks?.onSessionStart && this.callbacks.onSessionStart(e), this.announceSessionStart(e);
2400
+ }
2401
+ this.storeTabInfo(), this.announceLeadership();
2402
+ }
2403
+ }
2404
+ /**
2405
+ * Announce session start to other tabs
2406
+ */
2407
+ announceSessionStart(e) {
2408
+ if (!this.broadcastChannel) return;
2409
+ const t = {
2410
+ type: "session_start",
2411
+ tabId: this.tabId,
2412
+ sessionId: e,
2413
+ timestamp: Date.now()
2414
+ };
2415
+ this.broadcastChannel.postMessage(t);
2416
+ }
2417
+ /**
2418
+ * Announce leadership to other tabs
2419
+ */
2420
+ announceLeadership() {
2421
+ if (!this.broadcastChannel || !this.tabInfo.sessionId) return;
2422
+ const e = {
2423
+ type: "election_response",
2424
+ tabId: this.tabId,
2425
+ sessionId: this.tabInfo.sessionId,
2426
+ timestamp: Date.now(),
2427
+ data: { isLeader: !0 }
2428
+ };
2429
+ this.broadcastChannel.postMessage(e);
2430
+ }
2431
+ /**
2432
+ * Setup fallback mechanism to ensure a leader is always elected
2433
+ */
2434
+ setupLeadershipFallback() {
2435
+ const e = this.config.tabElectionTimeoutMs + 1500;
2436
+ this.fallbackLeadershipTimeout = window.setTimeout(() => {
2437
+ !this.isTabLeader && !this.leaderTabId && (this.tabInfo.sessionId ? (this.config.debugMode && i.warn(
2438
+ "CrossTabSession",
2439
+ `No leader detected after ${e}ms, forcing leadership for tab ${this.tabId}`
2440
+ ), this.becomeLeader()) : (this.config.debugMode && i.warn(
2441
+ "CrossTabSession",
2442
+ `No session or leader detected after ${e}ms, starting new session for tab ${this.tabId}`
2443
+ ), this.becomeLeader())), this.fallbackLeadershipTimeout = null;
2444
+ }, e), this.leaderHealthCheckInterval = window.setInterval(() => {
2445
+ if (!this.sessionEnded && this.leaderTabId && !this.isTabLeader) {
2446
+ const s = this.getStoredSessionContext();
2447
+ if (s) {
2448
+ const r = Date.now() - s.lastActivity, a = this.config.tabHeartbeatIntervalMs * 3;
2449
+ r > a && (this.config.debugMode && i.warn(
2450
+ "CrossTabSession",
2451
+ `Leader tab appears inactive (${r}ms), attempting to become leader`
2452
+ ), this.leaderTabId = null, this.startLeaderElection());
2453
+ }
2454
+ }
2455
+ }, this.config.tabHeartbeatIntervalMs * 2);
2456
+ const t = this.endSession.bind(this);
2457
+ this.endSession = (s) => {
2458
+ this.leaderHealthCheckInterval && (clearInterval(this.leaderHealthCheckInterval), this.leaderHealthCheckInterval = null), t(s);
2459
+ };
2460
+ }
2461
+ /**
2462
+ * Setup BroadcastChannel event listeners
2463
+ */
2464
+ setupBroadcastListeners(e) {
2465
+ e.addEventListener("message", (t) => {
2466
+ const s = t.data;
2467
+ s.tabId !== this.tabId && this.handleCrossTabMessage(s);
2468
+ });
2469
+ }
2470
+ /**
2471
+ * Handle cross-tab messages
2472
+ */
2473
+ handleCrossTabMessage(e) {
2474
+ switch (this.config.debugMode && i.debug("CrossTabSession", `Received cross-tab message: ${e.type} from ${e.tabId}`), e.type) {
2475
+ case "heartbeat":
2476
+ this.handleHeartbeatMessage(e);
2477
+ break;
2478
+ case "session_start":
2479
+ this.handleSessionStartMessage(e);
2480
+ break;
2481
+ case "session_end":
2482
+ this.handleSessionEndMessage(e);
2483
+ break;
2484
+ case "tab_closing":
2485
+ this.handleTabClosingMessage(e);
2486
+ break;
2487
+ case "election_request":
2488
+ this.handleElectionRequest(e);
2489
+ break;
2490
+ case "election_response":
2491
+ this.handleElectionResponse(e);
2492
+ break;
2493
+ }
2494
+ }
2495
+ /**
2496
+ * Handle heartbeat message from another tab
2497
+ */
2498
+ handleHeartbeatMessage(e) {
2499
+ if (e.sessionId === this.tabInfo.sessionId) {
2500
+ const t = this.getStoredSessionContext();
2501
+ t && (t.lastActivity = Date.now(), this.storeSessionContext(t), this.callbacks?.onTabActivity && this.callbacks.onTabActivity());
2502
+ }
2503
+ }
2504
+ /**
2505
+ * Handle session start message from another tab
2506
+ */
2507
+ handleSessionStartMessage(e) {
2508
+ if (e.sessionId && !this.tabInfo.sessionId) {
2509
+ this.tabInfo.sessionId = e.sessionId, this.storeTabInfo();
2510
+ const t = this.getStoredSessionContext();
2511
+ t && (t.tabCount += 1, this.storeSessionContext(t));
2512
+ }
2513
+ }
2514
+ /**
2515
+ * Handle session end message from another tab
2516
+ */
2517
+ handleSessionEndMessage(e) {
2518
+ if (this.isTabLeader) {
2519
+ this.config.debugMode && i.debug("CrossTabSession", `Ignoring session end message from ${e.tabId} (this tab is leader)`);
2520
+ return;
2521
+ }
2522
+ if (!this.leaderTabId || e.tabId !== this.leaderTabId) {
2523
+ if (this.config.debugMode) {
2524
+ const s = this.leaderTabId ? `; leader is ${this.leaderTabId}` : "";
2525
+ i.debug("CrossTabSession", `Ignoring session end message from ${e.tabId}${s}`);
2526
+ }
2527
+ return;
2528
+ }
2529
+ this.tabInfo.sessionId = "", this.storeTabInfo(), this.leaderTabId = null, this.getStoredSessionContext() || (this.broadcastChannel ? this.startLeaderElection() : this.becomeLeader());
2530
+ }
2531
+ /**
2532
+ * Handle tab closing message from another tab
2533
+ */
2534
+ handleTabClosingMessage(e) {
2535
+ const t = this.getStoredSessionContext();
2536
+ if (t && e.sessionId === t.sessionId) {
2537
+ const s = t.tabCount;
2538
+ t.tabCount = Math.max(1, t.tabCount - 1), t.lastActivity = Date.now(), this.storeSessionContext(t), this.config.debugMode && i.debug(
2539
+ "CrossTabSession",
2540
+ `Tab count updated from ${s} to ${t.tabCount} after tab ${e.tabId} closed`
2541
+ ), (e.data?.isLeader ?? e.tabId === this.leaderTabId) && !this.isTabLeader && (this.config.debugMode && i.debug("CrossTabSession", `Leader tab ${e.tabId} closed, starting leader election`), this.leaderTabId = null, this.electionDelayTimeout = window.setTimeout(() => {
2542
+ this.startLeaderElection(), this.electionDelayTimeout = null;
2543
+ }, 200));
2544
+ }
2545
+ }
2546
+ /**
2547
+ * Handle election request from another tab
2548
+ */
2549
+ handleElectionRequest(e) {
2550
+ if (this.isTabLeader) {
2551
+ const t = {
2552
+ type: "election_response",
2553
+ tabId: this.tabId,
2554
+ sessionId: this.tabInfo.sessionId,
2555
+ timestamp: Date.now(),
2556
+ data: { isLeader: !0 }
2557
+ };
2558
+ this.broadcastChannel && this.broadcastChannel.postMessage(t);
2559
+ }
2560
+ }
2561
+ /**
2562
+ * Handle election response from another tab
2563
+ */
2564
+ handleElectionResponse(e) {
2565
+ e.data?.isLeader && (this.isTabLeader ? this.config.debugMode && (i.warn(
2566
+ "CrossTabSession",
2567
+ `Received leadership claim from ${e.tabId} but this tab is already leader`
2568
+ ), this.callbacks?.onCrossTabConflict && this.callbacks.onCrossTabConflict()) : (this.isTabLeader = !1, this.tabInfo.isLeader = !1, this.leaderTabId = e.tabId, this.config.debugMode && i.debug("CrossTabSession", `Acknowledging tab ${e.tabId} as leader`), this.electionTimeout && (clearTimeout(this.electionTimeout), this.electionTimeout = null), e.sessionId && (this.tabInfo.sessionId = e.sessionId, this.storeTabInfo())));
2569
+ }
2570
+ /**
2571
+ * Start heartbeat to keep session active
2572
+ */
2573
+ startHeartbeat() {
2574
+ this.heartbeatInterval && clearInterval(this.heartbeatInterval), this.heartbeatInterval = window.setInterval(() => {
2575
+ this.sendHeartbeat(), this.updateTabInfo();
2576
+ }, this.config.tabHeartbeatIntervalMs);
2577
+ }
2578
+ /**
2579
+ * Send heartbeat to other tabs with rate limiting to prevent flooding
2580
+ */
2581
+ sendHeartbeat() {
2582
+ if (!this.broadcastChannel || !this.tabInfo.sessionId) return;
2583
+ const e = Date.now(), t = this.lastHeartbeatSent ?? 0, s = this.config.tabHeartbeatIntervalMs * 0.8;
2584
+ if (!this.isTabLeader && e - t < s)
2585
+ return;
2586
+ const r = {
2587
+ type: "heartbeat",
2588
+ tabId: this.tabId,
2589
+ sessionId: this.tabInfo.sessionId,
2590
+ timestamp: e
2591
+ };
2592
+ this.broadcastChannel.postMessage(r), this.lastHeartbeatSent = e;
2593
+ }
2594
+ /**
2595
+ * Update tab info with current timestamp
2596
+ */
2597
+ updateTabInfo() {
2598
+ this.tabInfo.lastHeartbeat = Date.now(), this.storeTabInfo();
2599
+ }
2600
+ /**
2601
+ * End session and notify other tabs
2602
+ */
2603
+ endSession(e) {
2604
+ this.sessionEnded || (this.sessionEnded = !0, this.config.debugMode && i.debug(
2605
+ "CrossTabSession",
2606
+ `Ending cross-tab session: ${e} (tab: ${this.tabId}, isLeader: ${this.isTabLeader})`
2607
+ ), this.announceTabClosing(), this.isTabLeader && e !== "manual_stop" && this.announceSessionEnd(e), this.tabInfoCleanupTimeout = window.setTimeout(() => {
2608
+ this.clearTabInfo(), this.tabInfoCleanupTimeout = null;
2609
+ }, 150));
2610
+ }
2611
+ /**
2612
+ * Announce tab is closing to other tabs
2613
+ */
2614
+ announceTabClosing() {
2615
+ if (!this.broadcastChannel || !this.tabInfo.sessionId) return;
2616
+ const e = {
2617
+ type: "tab_closing",
2618
+ tabId: this.tabId,
2619
+ sessionId: this.tabInfo.sessionId,
2620
+ timestamp: Date.now(),
2621
+ data: { isLeader: this.isTabLeader }
2622
+ };
2623
+ this.broadcastChannel.postMessage(e), this.closingAnnouncementTimeout = window.setTimeout(() => {
2624
+ this.config.debugMode && i.debug("CrossTabSession", `Tab ${this.tabId} closing announcement sent`), this.closingAnnouncementTimeout = null;
2625
+ }, 100);
2626
+ }
2627
+ /**
2628
+ * Announce session end to other tabs
2629
+ */
2630
+ announceSessionEnd(e) {
2631
+ if (!this.broadcastChannel) return;
2632
+ const t = {
2633
+ type: "session_end",
2634
+ tabId: this.tabId,
2635
+ sessionId: this.tabInfo.sessionId,
2636
+ timestamp: Date.now(),
2637
+ data: { reason: e }
2638
+ };
2639
+ this.broadcastChannel.postMessage(t);
2640
+ }
2641
+ /**
2642
+ * Get current session ID
2643
+ */
2644
+ getSessionId() {
2645
+ return this.tabInfo.sessionId;
2646
+ }
2647
+ /**
2648
+ * Get current tab ID
2649
+ */
2650
+ getTabId() {
2651
+ return this.tabId;
2652
+ }
2653
+ /**
2654
+ * Check if this tab is the session leader
2655
+ */
2656
+ isLeader() {
2657
+ return this.isTabLeader;
2658
+ }
2659
+ /**
2660
+ * Get current session context from storage
2661
+ */
2662
+ getStoredSessionContext() {
2663
+ try {
2664
+ const e = this.storageManager.getItem(Y(this.projectId));
2665
+ return e ? JSON.parse(e) : null;
2666
+ } catch (e) {
2667
+ return this.config.debugMode && i.warn("CrossTabSession", "Failed to parse stored session context", { error: e }), null;
2668
+ }
2669
+ }
2670
+ /**
2671
+ * Store session context to localStorage
2672
+ */
2673
+ storeSessionContext(e) {
2674
+ try {
2675
+ this.storageManager.setItem(Y(this.projectId), JSON.stringify(e));
2676
+ } catch (t) {
2677
+ this.config.debugMode && i.warn("CrossTabSession", "Failed to store session context", { error: t });
2678
+ }
2679
+ }
2680
+ /**
2681
+ * Clear stored session context
2682
+ */
2683
+ clearStoredSessionContext() {
2684
+ this.storageManager.removeItem(Y(this.projectId));
2685
+ }
2686
+ /**
2687
+ * Store tab info to localStorage
2688
+ */
2689
+ storeTabInfo() {
2690
+ try {
2691
+ this.storageManager.setItem(Se(this.projectId, this.tabId), JSON.stringify(this.tabInfo));
2692
+ } catch (e) {
2693
+ this.config.debugMode && i.warn("CrossTabSession", "Failed to store tab info", { error: e });
2694
+ }
2695
+ }
2696
+ /**
2697
+ * Clear tab info from localStorage
2698
+ */
2699
+ clearTabInfo() {
2700
+ this.storageManager.removeItem(Se(this.projectId, this.tabId));
2701
+ }
2702
+ /**
2703
+ * Check if BroadcastChannel is supported
2704
+ */
2705
+ isBroadcastChannelSupported() {
2706
+ return typeof window < "u" && "BroadcastChannel" in window;
2707
+ }
2708
+ /**
2709
+ * Get session timeout considering cross-tab activity
2710
+ */
2711
+ getEffectiveSessionTimeout() {
2712
+ const e = this.getStoredSessionContext();
2713
+ if (!e)
2714
+ return this.get("config")?.sessionTimeout ?? L;
2715
+ const s = Date.now() - e.lastActivity, r = this.get("config")?.sessionTimeout ?? L;
2716
+ return Math.max(0, r - s);
2717
+ }
2718
+ /**
2719
+ * Update session activity from any tab
2720
+ */
2721
+ updateSessionActivity() {
2722
+ const e = this.getStoredSessionContext();
2723
+ e && (e.lastActivity = Date.now(), this.storeSessionContext(e)), this.sendHeartbeat();
2724
+ }
2725
+ /**
2726
+ * Cleanup resources
2727
+ */
2728
+ destroy() {
2729
+ this.heartbeatInterval && (clearInterval(this.heartbeatInterval), this.heartbeatInterval = null), this.electionTimeout && (clearTimeout(this.electionTimeout), this.electionTimeout = null), this.cleanupTimeout && (clearTimeout(this.cleanupTimeout), this.cleanupTimeout = null), this.fallbackLeadershipTimeout && (clearTimeout(this.fallbackLeadershipTimeout), this.fallbackLeadershipTimeout = null), this.electionDelayTimeout && (clearTimeout(this.electionDelayTimeout), this.electionDelayTimeout = null), this.tabInfoCleanupTimeout && (clearTimeout(this.tabInfoCleanupTimeout), this.tabInfoCleanupTimeout = null), this.closingAnnouncementTimeout && (clearTimeout(this.closingAnnouncementTimeout), this.closingAnnouncementTimeout = null), this.leaderHealthCheckInterval && (clearInterval(this.leaderHealthCheckInterval), this.leaderHealthCheckInterval = null), this.endSession("manual_stop"), this.broadcastChannel && this.broadcastChannel.close();
2730
+ }
2731
+ }
2732
+ class Dt extends p {
2733
+ eventManager;
2734
+ storageManager;
2735
+ sessionStorageKey;
2736
+ sessionManager = null;
2737
+ recoveryManager = null;
2738
+ _crossTabSessionManager = null;
2739
+ heartbeatInterval = null;
2740
+ _isInitializingCrossTab = !1;
2741
+ get crossTabSessionManager() {
2742
+ if (!this._crossTabSessionManager && !this._isInitializingCrossTab && this.shouldUseCrossTabs()) {
2743
+ this._isInitializingCrossTab = !0;
2744
+ try {
2745
+ const e = this.get("config")?.id;
2746
+ e && this.initializeCrossTabSessionManager(e);
2747
+ } catch (e) {
2748
+ i.error("SessionHandler", "Failed to initialize cross-tab session manager", {
2749
+ error: e instanceof Error ? e.message : "Unknown error"
2750
+ });
2751
+ } finally {
2752
+ this._isInitializingCrossTab = !1;
2753
+ }
2754
+ }
2755
+ return this._crossTabSessionManager;
2756
+ }
2757
+ shouldUseCrossTabs() {
2758
+ return typeof BroadcastChannel < "u" && typeof navigator < "u" && "serviceWorker" in navigator;
2759
+ }
2760
+ constructor(e, t) {
2761
+ super(), this.eventManager = t, this.storageManager = e, this.sessionStorageKey = st(this.get("config")?.id);
2762
+ const s = this.get("config")?.id;
2763
+ s && this.initializeSessionRecoveryManager(s);
2764
+ }
2765
+ startTracking() {
2766
+ if (this.sessionManager) {
2767
+ i.debug("SessionHandler", "Session tracking already active");
2768
+ return;
2769
+ }
2770
+ i.debug("SessionHandler", "Starting session tracking"), this.checkOrphanedSessions();
2771
+ const e = async () => {
2772
+ if (this.crossTabSessionManager && this.crossTabSessionManager.updateSessionActivity(), !this.get("sessionId"))
2773
+ try {
2774
+ const r = await this.createOrJoinSession();
2775
+ this.set("sessionId", r.sessionId), i.info("SessionHandler", "🏁 Session started", {
2776
+ sessionId: r.sessionId,
2777
+ recovered: r.recovered,
2778
+ crossTabActive: !!this.crossTabSessionManager
2779
+ }), this.trackSession(h.SESSION_START, r.recovered), this.persistSession(r.sessionId), this.startHeartbeat();
2780
+ } catch (r) {
2781
+ i.error(
2782
+ "SessionHandler",
2783
+ `Session creation failed: ${r instanceof Error ? r.message : "Unknown error"}`
2784
+ ), this.forceCleanupSession();
2785
+ }
2786
+ }, t = () => {
2787
+ if (this.get("sessionId")) {
2788
+ if (this.crossTabSessionManager && this.crossTabSessionManager.getEffectiveSessionTimeout() > 0) {
2789
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.debug("SessionHandler", "Session kept alive by cross-tab activity");
2790
+ return;
2791
+ }
2792
+ this.sessionManager.endSessionManaged("inactivity").then((r) => {
2793
+ i.info("SessionHandler", "🛑 Session ended by inactivity", {
2794
+ sessionId: this.get("sessionId"),
2795
+ reason: r.reason,
2796
+ success: r.success,
2797
+ eventsFlushed: r.eventsFlushed
2798
+ }), this.crossTabSessionManager && this.crossTabSessionManager.endSession("inactivity"), this.clearPersistedSession(), this.stopHeartbeat();
2799
+ }).catch((r) => {
2800
+ i.error(
2801
+ "SessionHandler",
2802
+ `Session end failed: ${r instanceof Error ? r.message : "Unknown error"}`
2803
+ ), this.forceCleanupSession();
2804
+ });
2805
+ }
2806
+ }, s = {
2807
+ enablePageUnloadHandlers: !0,
2808
+ debugMode: (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") ?? !1,
2809
+ syncTimeoutMs: pe.SYNC_TIMEOUT_MS,
2810
+ maxRetries: pe.MAX_RETRY_ATTEMPTS
2811
+ };
2812
+ this.sessionManager = new Pt(
2813
+ e,
2814
+ t,
2815
+ this.eventManager,
2816
+ this.storageManager,
2817
+ s
2818
+ ), i.debug("SessionHandler", "Session manager initialized"), this.startInitialSession();
2819
+ }
2820
+ stopTracking() {
2821
+ if (i.info("SessionHandler", "Stopping session tracking"), this.sessionManager) {
2822
+ if (this.get("sessionId"))
2823
+ try {
2824
+ this.sessionManager.endSessionSafely("manual_stop", { forceSync: !0 }), this.clearPersistedSession(), this.stopHeartbeat();
2825
+ } catch (e) {
2826
+ i.error(
2827
+ "SessionHandler",
2828
+ `Manual session stop failed: ${e instanceof Error ? e.message : "Unknown error"}`
2829
+ ), this.forceCleanupSession();
2830
+ }
2831
+ this.sessionManager.destroy(), this.sessionManager = null;
2832
+ }
2833
+ this._crossTabSessionManager && (this._crossTabSessionManager.destroy(), this._crossTabSessionManager = null), this._isInitializingCrossTab = !1, this.recoveryManager && (this.recoveryManager.cleanupOldRecoveryAttempts(), this.recoveryManager = null);
2834
+ }
2835
+ initializeSessionRecoveryManager(e) {
2836
+ this.recoveryManager = new Ce(this.storageManager, e, this.eventManager), i.debug("SessionHandler", "Session recovery manager initialized", { projectId: e });
2837
+ }
2838
+ initializeCrossTabSessionManager(e) {
2839
+ const t = {
2840
+ debugMode: (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") ?? !1
2841
+ }, l = {
2842
+ onSessionStart: (c) => {
2843
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.debug("SessionHandler", `Cross-tab session started: ${c}`), this.set("sessionId", c), this.trackSession(h.SESSION_START, !1), this.persistSession(c), this.startHeartbeat();
2844
+ },
2845
+ onSessionEnd: (c) => {
2846
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.debug("SessionHandler", `Cross-tab session ended: ${c}`), this.clearPersistedSession(), this.trackSession(h.SESSION_END, !1, c);
2847
+ },
2848
+ onTabActivity: () => {
2849
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.debug("SessionHandler", "Cross-tab activity detected");
2850
+ },
2851
+ onCrossTabConflict: () => {
2852
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.warn("SessionHandler", "Cross-tab conflict detected"), this.sessionManager && this.sessionManager.trackSessionHealth("conflict");
2853
+ }
2854
+ };
2855
+ this._crossTabSessionManager = new Ht(this.storageManager, e, t, l), i.debug("SessionHandler", "Cross-tab session manager initialized", { projectId: e });
2856
+ }
2857
+ async createOrJoinSession() {
2858
+ if (this.crossTabSessionManager) {
2859
+ const t = this.crossTabSessionManager.getSessionId();
2860
+ if (t)
2861
+ return { sessionId: t, recovered: !1 };
2862
+ const s = this.sessionManager.startSession();
2863
+ return { sessionId: s.sessionId, recovered: s.recovered ?? !1 };
2864
+ }
2865
+ const e = this.sessionManager.startSession();
2866
+ return { sessionId: e.sessionId, recovered: e.recovered ?? !1 };
2867
+ }
2868
+ forceCleanupSession() {
2869
+ if (this.set("sessionId", null), this.clearPersistedSession(), this.stopHeartbeat(), this.crossTabSessionManager)
2870
+ try {
2871
+ this.crossTabSessionManager.endSession("orphaned_cleanup");
2872
+ } catch (e) {
2873
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.warn(
2874
+ "SessionHandler",
2875
+ `Cross-tab cleanup failed during force cleanup: ${e instanceof Error ? e.message : "Unknown error"}`
2876
+ );
2877
+ }
2878
+ try {
2879
+ this.trackSession(h.SESSION_END, !1, "orphaned_cleanup");
2880
+ } catch (e) {
2881
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.warn(
2882
+ "SessionHandler",
2883
+ `Session tracking failed during force cleanup: ${e instanceof Error ? e.message : "Unknown error"}`
2884
+ );
2885
+ }
2886
+ (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.debug("SessionHandler", "Forced session cleanup completed");
2887
+ }
2888
+ trackSession(e, t = !1, s) {
2889
+ this.eventManager.track({
2890
+ type: e,
2891
+ ...e === h.SESSION_START && t && { session_start_recovered: t },
2892
+ ...e === h.SESSION_END && { session_end_reason: s ?? "orphaned_cleanup" }
2893
+ });
2894
+ }
2895
+ startInitialSession() {
2896
+ if (this.get("sessionId")) {
2897
+ i.debug("SessionHandler", "Session already exists, skipping initial session creation");
2898
+ return;
2899
+ }
2900
+ if (i.debug("SessionHandler", "Starting initial session"), this.crossTabSessionManager) {
2901
+ const t = this.crossTabSessionManager.getSessionId();
2902
+ if (t) {
2903
+ this.set("sessionId", t), this.persistSession(t), this.startHeartbeat();
2904
+ return;
2905
+ }
2906
+ return;
2907
+ }
2908
+ i.debug("SessionHandler", "Starting regular session (no cross-tab)");
2909
+ const e = this.sessionManager.startSession();
2910
+ this.set("sessionId", e.sessionId), this.trackSession(h.SESSION_START, e.recovered), this.persistSession(e.sessionId), this.startHeartbeat();
2911
+ }
2912
+ checkOrphanedSessions() {
2913
+ const e = this.storageManager.getItem(this.sessionStorageKey);
2914
+ if (e)
2915
+ try {
2916
+ const t = JSON.parse(e), r = Date.now() - t.lastHeartbeat, a = this.get("config")?.sessionTimeout ?? L;
2917
+ if (r > a) {
2918
+ const o = this.recoveryManager?.hasRecoverableSession();
2919
+ if (o && this.recoveryManager) {
2920
+ const l = {
2921
+ sessionId: t.sessionId,
2922
+ startTime: t.startTime,
2923
+ lastActivity: t.lastHeartbeat,
2924
+ tabCount: 1,
2925
+ recoveryAttempts: 0,
2926
+ metadata: {
2927
+ userAgent: navigator.userAgent,
2928
+ pageUrl: this.get("pageUrl")
2929
+ }
2930
+ };
2931
+ this.recoveryManager.storeSessionContextForRecovery(l), (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.debug("SessionHandler", `Orphaned session stored for recovery: ${t.sessionId}`);
2932
+ }
2933
+ this.trackSession(h.SESSION_END), this.clearPersistedSession(), (this.get("config")?.mode === "qa" || this.get("config")?.mode === "debug") && i.debug(
2934
+ "SessionHandler",
2935
+ `Orphaned session ended: ${t.sessionId}, recovery available: ${o}`
2936
+ );
2937
+ }
2938
+ } catch {
2939
+ this.clearPersistedSession();
2940
+ }
2941
+ }
2942
+ persistSession(e) {
2943
+ const t = {
2944
+ sessionId: e,
2945
+ startTime: Date.now(),
2946
+ lastHeartbeat: Date.now()
2947
+ };
2948
+ this.storageManager.setItem(this.sessionStorageKey, JSON.stringify(t));
2949
+ }
2950
+ clearPersistedSession() {
2951
+ this.storageManager.removeItem(this.sessionStorageKey);
2952
+ }
2953
+ startHeartbeat() {
2954
+ this.stopHeartbeat(), this.heartbeatInterval = setInterval(() => {
2955
+ const e = this.storageManager.getItem(this.sessionStorageKey);
2956
+ if (e)
2957
+ try {
2958
+ const t = JSON.parse(e);
2959
+ t.lastHeartbeat = Date.now(), this.storageManager.setItem(this.sessionStorageKey, JSON.stringify(t));
2960
+ } catch {
2961
+ this.clearPersistedSession();
2962
+ }
2963
+ }, ze);
2964
+ }
2965
+ stopHeartbeat() {
2966
+ this.heartbeatInterval && (clearInterval(this.heartbeatInterval), this.heartbeatInterval = null);
2967
+ }
2968
+ }
2969
+ class xt extends p {
2970
+ eventManager;
2971
+ onTrack;
2972
+ originalPushState;
2973
+ originalReplaceState;
2974
+ constructor(e, t) {
2975
+ super(), this.eventManager = e, this.onTrack = t;
2976
+ }
2977
+ startTracking() {
2978
+ i.debug("PageViewHandler", "Starting page view tracking"), this.trackInitialPageView(), this.trackCurrentPage(), window.addEventListener("popstate", this.trackCurrentPage), window.addEventListener("hashchange", this.trackCurrentPage), this.patchHistory("pushState"), this.patchHistory("replaceState");
2979
+ }
2980
+ stopTracking() {
2981
+ i.debug("PageViewHandler", "Stopping page view tracking"), window.removeEventListener("popstate", this.trackCurrentPage), window.removeEventListener("hashchange", this.trackCurrentPage), this.originalPushState && (window.history.pushState = this.originalPushState), this.originalReplaceState && (window.history.replaceState = this.originalReplaceState);
2982
+ }
2983
+ patchHistory(e) {
2984
+ e === "pushState" && !this.originalPushState ? this.originalPushState = window.history.pushState : e === "replaceState" && !this.originalReplaceState && (this.originalReplaceState = window.history.replaceState);
2985
+ const t = window.history[e];
2986
+ window.history[e] = (...s) => {
2987
+ t.apply(window.history, s), this.trackCurrentPage();
2988
+ };
2989
+ }
2990
+ trackCurrentPage = () => {
2991
+ const e = window.location.href, t = se(e, this.get("config").sensitiveQueryParams);
2992
+ if (this.get("pageUrl") !== t) {
2993
+ const s = this.get("pageUrl");
2994
+ i.debug("PageViewHandler", "Page navigation detected", { from: s, to: t }), this.set("pageUrl", t), this.eventManager.track({
2995
+ type: h.PAGE_VIEW,
2996
+ page_url: this.get("pageUrl"),
2997
+ from_page_url: s,
2998
+ ...this.extractPageViewData() && { page_view: this.extractPageViewData() }
2999
+ }), this.onTrack();
3000
+ }
3001
+ };
3002
+ trackInitialPageView() {
3003
+ this.eventManager.track({
3004
+ type: h.PAGE_VIEW,
3005
+ page_url: this.get("pageUrl"),
3006
+ ...this.extractPageViewData() && { page_view: this.extractPageViewData() }
3007
+ }), this.onTrack();
3008
+ }
3009
+ extractPageViewData() {
3010
+ const e = window.location, t = {
3011
+ ...document.referrer && { referrer: document.referrer },
3012
+ ...document.title && { title: document.title },
3013
+ ...e.pathname && { pathname: e.pathname },
3014
+ ...e.search && { search: e.search },
3015
+ ...e.hash && { hash: e.hash }
3016
+ };
3017
+ return Object.values(t).some((s) => !!s) ? t : void 0;
3018
+ }
3019
+ }
3020
+ class Ot extends p {
3021
+ eventManager;
3022
+ clickHandler;
3023
+ constructor(e) {
3024
+ super(), this.eventManager = e;
3025
+ }
3026
+ startTracking() {
3027
+ if (this.clickHandler) {
3028
+ i.debug("ClickHandler", "Click tracking already active");
3029
+ return;
3030
+ }
3031
+ i.debug("ClickHandler", "Starting click tracking"), this.clickHandler = (e) => {
3032
+ const t = e, s = t.target, r = s instanceof HTMLElement ? s : s instanceof Node && s.parentElement instanceof HTMLElement ? s.parentElement : null;
3033
+ if (!r) {
3034
+ i.warn("ClickHandler", "Click target not found or not an element");
3035
+ return;
3036
+ }
3037
+ i.info("ClickHandler", "🖱️ Click detected on element", {
3038
+ tagName: r.tagName,
3039
+ className: r.className || "none",
3040
+ textContent: r.textContent?.slice(0, 50) ?? "empty"
3041
+ });
3042
+ const a = this.findTrackingElement(r), o = this.getRelevantClickElement(r), l = this.calculateClickCoordinates(t, r);
3043
+ if (a) {
3044
+ const d = this.extractTrackingData(a);
3045
+ if (d) {
3046
+ const u = this.createCustomEventData(d);
3047
+ this.eventManager.track({
3048
+ type: h.CUSTOM,
3049
+ custom_event: {
3050
+ name: u.name,
3051
+ ...u.value && { metadata: { value: u.value } }
3052
+ }
3053
+ });
3054
+ }
3055
+ }
3056
+ const c = this.generateClickData(r, o, l);
3057
+ this.eventManager.track({
3058
+ type: h.CLICK,
3059
+ click_data: c
3060
+ });
3061
+ }, window.addEventListener("click", this.clickHandler, !0);
3062
+ }
3063
+ stopTracking() {
3064
+ this.clickHandler && (window.removeEventListener("click", this.clickHandler, !0), this.clickHandler = void 0);
3065
+ }
3066
+ findTrackingElement(e) {
3067
+ return e.hasAttribute(`${z}-name`) ? e : e.closest(`[${z}-name]`) || void 0;
3068
+ }
3069
+ getRelevantClickElement(e) {
3070
+ for (const t of fe)
3071
+ try {
3072
+ if (e.matches(t))
3073
+ return e;
3074
+ } catch (s) {
3075
+ i.warn("ClickHandler", "Invalid selector in interactive elements check", {
3076
+ selector: t,
3077
+ error: s instanceof Error ? s.message : "Unknown error"
3078
+ });
3079
+ continue;
3080
+ }
3081
+ for (const t of fe)
3082
+ try {
3083
+ const s = e.closest(t);
3084
+ if (s)
3085
+ return s;
3086
+ } catch (s) {
3087
+ i.warn("ClickHandler", "Invalid selector in parent element search", {
3088
+ selector: t,
3089
+ error: s instanceof Error ? s.message : "Unknown error"
3090
+ });
3091
+ continue;
3092
+ }
3093
+ return e;
3094
+ }
3095
+ calculateClickCoordinates(e, t) {
3096
+ const s = t.getBoundingClientRect(), r = e.clientX, a = e.clientY, o = s.width > 0 ? Math.max(0, Math.min(1, Number(((r - s.left) / s.width).toFixed(3)))) : 0, l = s.height > 0 ? Math.max(0, Math.min(1, Number(((a - s.top) / s.height).toFixed(3)))) : 0;
3097
+ return { x: r, y: a, relativeX: o, relativeY: l };
3098
+ }
3099
+ extractTrackingData(e) {
3100
+ const t = e.getAttribute(`${z}-name`), s = e.getAttribute(`${z}-value`);
3101
+ if (t)
3102
+ return {
3103
+ element: e,
3104
+ name: t,
3105
+ ...s && { value: s }
3106
+ };
3107
+ }
3108
+ generateClickData(e, t, s) {
3109
+ const { x: r, y: a, relativeX: o, relativeY: l } = s, c = this.getRelevantText(e, t), d = this.extractElementAttributes(t), u = t.getAttribute("href"), g = t.getAttribute("title"), y = t.getAttribute("alt"), I = t.getAttribute("role"), A = t.getAttribute("aria-label"), U = typeof t.className == "string" ? t.className : String(t.className);
3110
+ return {
3111
+ x: r,
3112
+ y: a,
3113
+ relativeX: o,
3114
+ relativeY: l,
3115
+ tag: t.tagName.toLowerCase(),
3116
+ ...t.id && { id: t.id },
3117
+ ...t.className && { class: U },
3118
+ ...c && { text: c },
3119
+ ...u && { href: u },
3120
+ ...g && { title: g },
3121
+ ...y && { alt: y },
3122
+ ...I && { role: I },
3123
+ ...A && { ariaLabel: A },
3124
+ ...Object.keys(d).length > 0 && { dataAttributes: d }
3125
+ };
3126
+ }
3127
+ getRelevantText(e, t) {
3128
+ const s = ["main", "section", "article", "body", "html", "header", "footer", "aside", "nav"], r = e.textContent?.trim() ?? "", a = t.textContent?.trim() ?? "";
3129
+ if (!r && !a)
3130
+ return "";
3131
+ if (r && r.length <= _)
3132
+ return r;
3133
+ const o = s.includes(t.tagName.toLowerCase()), l = a.length > _ * 2;
3134
+ return o && l ? r && r.length <= _ ? r : "" : a.length <= _ ? a : r && r.length < a.length * 0.1 ? r.length <= _ ? r : r.slice(0, _ - 3) + "..." : a.slice(0, _ - 3) + "...";
3135
+ }
3136
+ extractElementAttributes(e) {
3137
+ const t = ["id", "class", "data-testid", "aria-label", "title", "href", "type", "name"], s = {};
3138
+ for (const r of t) {
3139
+ const a = e.getAttribute(r);
3140
+ a && (s[r] = a);
3141
+ }
3142
+ return s;
3143
+ }
3144
+ createCustomEventData(e) {
3145
+ return {
3146
+ name: e.name,
3147
+ ...e.value && { value: e.value }
3148
+ };
3149
+ }
3150
+ }
3151
+ class Ft extends p {
3152
+ eventManager;
3153
+ containers = [];
3154
+ constructor(e) {
3155
+ super(), this.eventManager = e;
3156
+ }
3157
+ startTracking() {
3158
+ const e = this.get("config").scrollContainerSelectors, t = Array.isArray(e) ? e : typeof e == "string" ? [e] : [];
3159
+ i.debug("ScrollHandler", "Starting scroll tracking", { selectorsCount: t.length });
3160
+ const s = t.map((r) => this.safeQuerySelector(r)).filter((r) => r instanceof HTMLElement);
3161
+ s.length === 0 && s.push(window);
3162
+ for (const r of s)
3163
+ this.setupScrollContainer(r);
3164
+ }
3165
+ stopTracking() {
3166
+ i.debug("ScrollHandler", "Stopping scroll tracking", { containersCount: this.containers.length });
3167
+ for (const e of this.containers)
3168
+ e.debounceTimer && clearTimeout(e.debounceTimer), e.element instanceof Window ? window.removeEventListener("scroll", e.listener) : e.element.removeEventListener("scroll", e.listener);
3169
+ this.containers.length = 0;
3170
+ }
3171
+ setupScrollContainer(e) {
3172
+ if (e !== window && !this.isElementScrollable(e))
3173
+ return;
3174
+ const t = {
3175
+ element: e,
3176
+ lastScrollPos: this.getScrollTop(e),
3177
+ debounceTimer: null,
3178
+ listener: () => {
3179
+ }
3180
+ }, s = () => {
3181
+ if (this.get("suppressNextScroll")) {
3182
+ this.set("suppressNextScroll", !1);
3183
+ return;
3184
+ }
3185
+ t.debounceTimer && clearTimeout(t.debounceTimer), t.debounceTimer = window.setTimeout(() => {
3186
+ const r = this.calculateScrollData(t);
3187
+ r && this.eventManager.track({
3188
+ type: h.SCROLL,
3189
+ scroll_data: r
3190
+ }), t.debounceTimer = null;
3191
+ }, Me);
3192
+ };
3193
+ t.listener = s, this.containers.push(t), e instanceof Window ? window.addEventListener("scroll", s, { passive: !0 }) : e.addEventListener("scroll", s, { passive: !0 });
3194
+ }
3195
+ calculateScrollData(e) {
3196
+ const { element: t, lastScrollPos: s } = e, r = this.getScrollTop(t), a = this.getViewportHeight(t), o = this.getScrollHeight(t);
3197
+ if (t === window && o <= a)
3198
+ return null;
3199
+ const l = r > s ? j.DOWN : j.UP, c = o > a ? Math.min(100, Math.max(0, Math.floor(r / (o - a) * 100))) : 0;
3200
+ return Math.abs(r - s) < Ue ? null : (e.lastScrollPos = r, { depth: c, direction: l });
3201
+ }
3202
+ getScrollTop(e) {
3203
+ return e instanceof Window ? window.scrollY : e.scrollTop;
3204
+ }
3205
+ getViewportHeight(e) {
3206
+ return e instanceof Window ? window.innerHeight : e.clientHeight;
3207
+ }
3208
+ getScrollHeight(e) {
3209
+ return e instanceof Window ? document.documentElement.scrollHeight : e.scrollHeight;
3210
+ }
3211
+ isElementScrollable(e) {
3212
+ const t = getComputedStyle(e), s = t.overflowY === "auto" || t.overflowY === "scroll" || t.overflowX === "auto" || t.overflowX === "scroll" || t.overflow === "auto" || t.overflow === "scroll", r = e.scrollHeight > e.clientHeight || e.scrollWidth > e.clientWidth;
3213
+ return s && r;
3214
+ }
3215
+ safeQuerySelector(e) {
3216
+ try {
3217
+ return document.querySelector(e);
3218
+ } catch (t) {
3219
+ return i.clientWarn("ScrollHandler", "Invalid CSS selector", {
3220
+ selector: e,
3221
+ error: t instanceof Error ? t.message : "Unknown error"
3222
+ }), null;
3223
+ }
3224
+ }
3225
+ }
3226
+ class zt extends p {
3227
+ isInitialized = !1;
3228
+ constructor() {
3229
+ super();
3230
+ }
3231
+ async initialize() {
3232
+ if (this.isInitialized)
3233
+ return;
3234
+ const e = this.get("config").integrations?.googleAnalytics?.measurementId;
3235
+ if (!e?.trim()) {
3236
+ i.clientWarn("GoogleAnalytics", "Google Analytics integration disabled - measurementId not configured", {
3237
+ hasIntegrations: !!this.get("config").integrations,
3238
+ hasGoogleAnalytics: !!this.get("config").integrations?.googleAnalytics
3239
+ });
3240
+ return;
3241
+ }
3242
+ const t = this.get("userId");
3243
+ if (!t?.trim()) {
3244
+ i.warn("GoogleAnalytics", "Google Analytics initialization delayed - userId not available", {
3245
+ measurementId: e.substring(0, 8) + "..."
3246
+ });
3247
+ return;
3248
+ }
3249
+ try {
3250
+ if (this.isScriptAlreadyLoaded()) {
3251
+ i.info("GoogleAnalytics", "Google Analytics script already loaded", { measurementId: e }), this.isInitialized = !0;
3252
+ return;
3253
+ }
3254
+ await this.loadScript(e), this.configureGtag(e, t), this.isInitialized = !0, i.info("GoogleAnalytics", "Google Analytics integration initialized successfully", {
3255
+ measurementId: e,
3256
+ userId: t
3257
+ });
3258
+ } catch (s) {
3259
+ i.error("GoogleAnalytics", "Google Analytics initialization failed", {
3260
+ error: s instanceof Error ? s.message : "Unknown error",
3261
+ measurementId: e,
3262
+ userId: t
3263
+ });
3264
+ }
3265
+ }
3266
+ trackEvent(e, t) {
3267
+ if (!e?.trim()) {
3268
+ i.clientWarn("GoogleAnalytics", "Event tracking skipped - invalid event name provided", {
3269
+ eventName: e,
3270
+ hasMetadata: !!t && Object.keys(t).length > 0
3271
+ });
3272
+ return;
3273
+ }
3274
+ if (this.isInitialized) {
3275
+ if (typeof window.gtag != "function") {
3276
+ i.warn("GoogleAnalytics", "Event tracking failed - gtag function not available", {
3277
+ eventName: e,
3278
+ hasGtag: typeof window.gtag,
3279
+ hasDataLayer: Array.isArray(window.dataLayer)
3280
+ });
3281
+ return;
3282
+ }
3283
+ try {
3284
+ window.gtag("event", e, t);
3285
+ } catch (s) {
3286
+ i.error("GoogleAnalytics", "Event tracking failed", {
3287
+ eventName: e,
3288
+ error: s instanceof Error ? s.message : "Unknown error",
3289
+ metadataKeys: Object.keys(t || {})
3290
+ });
3291
+ }
3292
+ }
3293
+ }
3294
+ cleanup() {
3295
+ this.isInitialized = !1;
3296
+ const e = document.getElementById("tracelog-ga-script");
3297
+ e && e.remove(), i.info("GoogleAnalytics", "Google Analytics integration cleanup completed");
3298
+ }
3299
+ isScriptAlreadyLoaded() {
3300
+ if (document.getElementById("tracelog-ga-script"))
3301
+ return !0;
3302
+ const t = document.querySelector('script[src*="googletagmanager.com/gtag/js"]');
3303
+ return t ? (i.clientWarn("GoogleAnalytics", "Google Analytics script already loaded from external source", {
3304
+ scriptSrc: t.getAttribute("src"),
3305
+ hasGtag: typeof window.gtag == "function"
3306
+ }), !0) : !1;
3307
+ }
3308
+ async loadScript(e) {
3309
+ return new Promise((t, s) => {
3310
+ try {
3311
+ const r = document.createElement("script");
3312
+ r.id = "tracelog-ga-script", r.async = !0, r.src = `https://www.googletagmanager.com/gtag/js?id=${e}`, r.onload = () => {
3313
+ t();
3314
+ }, r.onerror = () => {
3315
+ const a = new Error("Failed to load Google Analytics script");
3316
+ i.error("GoogleAnalytics", "Google Analytics script load failed", {
3317
+ measurementId: e,
3318
+ error: a.message,
3319
+ scriptSrc: r.src
3320
+ }), s(a);
3321
+ }, document.head.appendChild(r);
3322
+ } catch (r) {
3323
+ const a = r instanceof Error ? r : new Error(String(r));
3324
+ i.error("GoogleAnalytics", "Error creating Google Analytics script", {
3325
+ measurementId: e,
3326
+ error: a.message
3327
+ }), s(a);
3328
+ }
3329
+ });
3330
+ }
3331
+ configureGtag(e, t) {
3332
+ try {
3333
+ const s = document.createElement("script");
3334
+ s.innerHTML = `
3335
+ window.dataLayer = window.dataLayer || [];
3336
+ function gtag(){dataLayer.push(arguments);}
3337
+ gtag('js', new Date());
3338
+ gtag('config', '${e}', {
3339
+ 'user_id': '${t}'
3340
+ });
3341
+ `, document.head.appendChild(s);
3342
+ } catch (s) {
3343
+ throw i.error("GoogleAnalytics", "Failed to configure Google Analytics", {
3344
+ measurementId: e,
3345
+ userId: t,
3346
+ error: s instanceof Error ? s.message : "Unknown error"
3347
+ }), s;
3348
+ }
3349
+ }
3350
+ }
3351
+ class Vt {
3352
+ storage = null;
3353
+ fallbackStorage = /* @__PURE__ */ new Map();
3354
+ storageAvailable = !1;
3355
+ constructor() {
3356
+ this.storage = this.init(), this.storageAvailable = this.storage !== null, this.storageAvailable || i.warn("StorageManager", "localStorage not available, using memory fallback");
3357
+ }
3358
+ getItem(e) {
3359
+ if (!this.storageAvailable)
3360
+ return this.fallbackStorage.get(e) ?? null;
3361
+ try {
3362
+ return this.storage ? this.storage.getItem(e) : this.fallbackStorage.get(e) ?? null;
3363
+ } catch (t) {
3364
+ return i.warn("StorageManager", "Storage getItem failed, using memory fallback", { key: e, error: t }), this.storageAvailable = !1, this.fallbackStorage.get(e) ?? null;
3365
+ }
3366
+ }
3367
+ setItem(e, t) {
3368
+ if (!this.storageAvailable) {
3369
+ this.fallbackStorage.set(e, t);
3370
+ return;
3371
+ }
3372
+ try {
3373
+ if (this.storage) {
3374
+ this.storage.setItem(e, t);
3375
+ return;
3376
+ }
3377
+ this.fallbackStorage.set(e, t);
3378
+ } catch (s) {
3379
+ i.warn("StorageManager", "Storage setItem failed, using memory fallback", { key: e, error: s }), this.storageAvailable = !1, this.fallbackStorage.set(e, t);
3380
+ }
3381
+ }
3382
+ removeItem(e) {
3383
+ if (!this.storageAvailable) {
3384
+ this.fallbackStorage.delete(e);
3385
+ return;
3386
+ }
3387
+ try {
3388
+ if (this.storage) {
3389
+ this.storage.removeItem(e);
3390
+ return;
3391
+ }
3392
+ this.fallbackStorage.delete(e);
3393
+ } catch (t) {
3394
+ i.warn("StorageManager", "Storage removeItem failed, using memory fallback", { key: e, error: t }), this.storageAvailable = !1, this.fallbackStorage.delete(e);
3395
+ }
3396
+ }
3397
+ init() {
3398
+ try {
3399
+ const e = "__storage_test__", t = window.localStorage;
3400
+ return t.setItem(e, e), t.removeItem(e), t;
3401
+ } catch {
3402
+ return null;
3403
+ }
3404
+ }
3405
+ }
3406
+ class $t extends p {
3407
+ eventManager;
3408
+ reportedByNav = /* @__PURE__ */ new Map();
3409
+ observers = [];
3410
+ lastLongTaskSentAt = 0;
3411
+ constructor(e) {
3412
+ super(), this.eventManager = e;
3413
+ }
3414
+ async startTracking() {
3415
+ i.debug("PerformanceHandler", "Starting performance tracking"), await this.initWebVitals(), this.observeLongTasks(), this.reportTTFB();
3416
+ }
3417
+ stopTracking() {
3418
+ i.debug("PerformanceHandler", "Stopping performance tracking", { observersCount: this.observers.length }), this.observers.forEach((e, t) => {
3419
+ try {
3420
+ e.disconnect();
3421
+ } catch (s) {
3422
+ i.warn("PerformanceHandler", "Failed to disconnect performance observer", {
3423
+ error: s instanceof Error ? s.message : "Unknown error",
3424
+ observerIndex: t
3425
+ });
3426
+ }
3427
+ }), this.observers.length = 0, this.reportedByNav.clear(), i.debug("PerformanceHandler", "Performance tracking cleanup completed", {
3428
+ remainingObservers: this.observers.length,
3429
+ clearedNavReports: !0
3430
+ });
3431
+ }
3432
+ observeWebVitalsFallback() {
3433
+ this.reportTTFB(), this.safeObserve(
3434
+ "largest-contentful-paint",
3435
+ (t) => {
3436
+ const s = t.getEntries(), r = s[s.length - 1];
3437
+ r && this.sendVital({ type: "LCP", value: Number(r.startTime.toFixed(k)) });
3438
+ },
3439
+ { type: "largest-contentful-paint", buffered: !0 },
3440
+ !0
3441
+ );
3442
+ let e = 0;
3443
+ this.safeObserve(
3444
+ "layout-shift",
3445
+ (t) => {
3446
+ const s = t.getEntries();
3447
+ for (const r of s) {
3448
+ if (r.hadRecentInput === !0)
3449
+ continue;
3450
+ const a = typeof r.value == "number" ? r.value : 0;
3451
+ e += a;
3452
+ }
3453
+ this.sendVital({ type: "CLS", value: Number(e.toFixed(Pe)) });
3454
+ },
3455
+ { type: "layout-shift", buffered: !0 }
3456
+ ), this.safeObserve(
3457
+ "paint",
3458
+ (t) => {
3459
+ for (const s of t.getEntries())
3460
+ s.name === "first-contentful-paint" && this.sendVital({ type: "FCP", value: Number(s.startTime.toFixed(k)) });
3461
+ },
3462
+ { type: "paint", buffered: !0 },
3463
+ !0
3464
+ ), this.safeObserve(
3465
+ "event",
3466
+ (t) => {
3467
+ let s = 0;
3468
+ const r = t.getEntries();
3469
+ for (const a of r) {
3470
+ const o = (a.processingEnd ?? 0) - (a.startTime ?? 0);
3471
+ s = Math.max(s, o);
3472
+ }
3473
+ s > 0 && this.sendVital({ type: "INP", value: Number(s.toFixed(k)) });
3474
+ },
3475
+ { type: "event", buffered: !0 }
3476
+ );
3477
+ }
3478
+ async initWebVitals() {
3479
+ try {
3480
+ const { onLCP: e, onCLS: t, onFCP: s, onTTFB: r, onINP: a } = await import("./web-vitals-CCnqwnC8.mjs"), o = (l) => (c) => {
3481
+ const d = Number(c.value.toFixed(k));
3482
+ this.sendVital({ type: l, value: d });
3483
+ };
3484
+ e(o("LCP")), t(o("CLS")), s(o("FCP")), r(o("TTFB")), a(o("INP"));
3485
+ } catch (e) {
3486
+ i.warn("PerformanceHandler", "Failed to load web-vitals library, using fallback", {
3487
+ error: e instanceof Error ? e.message : "Unknown error"
3488
+ }), this.observeWebVitalsFallback();
3489
+ }
3490
+ }
3491
+ reportTTFB() {
3492
+ try {
3493
+ const e = performance.getEntriesByType("navigation")[0];
3494
+ if (!e) {
3495
+ i.debug("PerformanceHandler", "Navigation timing not available for TTFB");
3496
+ return;
3497
+ }
3498
+ const t = e.responseStart;
3499
+ typeof t == "number" && Number.isFinite(t) ? this.sendVital({ type: "TTFB", value: Number(t.toFixed(k)) }) : i.debug("PerformanceHandler", "TTFB value is not a valid number", { ttfb: t });
3500
+ } catch (e) {
3501
+ i.warn("PerformanceHandler", "Failed to report TTFB", {
3502
+ error: e instanceof Error ? e.message : "Unknown error"
3503
+ });
3504
+ }
3505
+ }
3506
+ observeLongTasks() {
3507
+ this.safeObserve(
3508
+ "longtask",
3509
+ (e) => {
3510
+ const t = e.getEntries();
3511
+ for (const s of t) {
3512
+ const r = Number(s.duration.toFixed(k)), a = Date.now();
3513
+ a - this.lastLongTaskSentAt >= Be && (this.trackWebVital("LONG_TASK", r), this.lastLongTaskSentAt = a);
3514
+ }
3515
+ },
3516
+ { type: "longtask", buffered: !0 }
3517
+ );
3518
+ }
3519
+ sendVital(e) {
3520
+ const t = this.getNavigationId(), s = `${e.type}`;
3521
+ if (t) {
3522
+ this.reportedByNav.has(t) || this.reportedByNav.set(t, /* @__PURE__ */ new Set());
3523
+ const r = this.reportedByNav.get(t);
3524
+ if (r.has(s))
3525
+ return;
3526
+ r.add(s);
3527
+ }
3528
+ this.trackWebVital(e.type, e.value);
3529
+ }
3530
+ trackWebVital(e, t) {
3531
+ if (typeof t != "number" || !Number.isFinite(t)) {
3532
+ i.warn("PerformanceHandler", "Invalid web vital value", { type: e, value: t });
3533
+ return;
3534
+ }
3535
+ this.eventManager.track({
3536
+ type: h.WEB_VITALS,
3537
+ web_vitals: {
3538
+ type: e,
3539
+ value: t
3540
+ }
3541
+ });
3542
+ }
3543
+ getNavigationId() {
3544
+ try {
3545
+ const e = performance.getEntriesByType("navigation")[0];
3546
+ return e ? `${Math.round(e.startTime)}_${window.location.pathname}` : null;
3547
+ } catch (e) {
3548
+ return i.warn("PerformanceHandler", "Failed to get navigation ID", {
3549
+ error: e instanceof Error ? e.message : "Unknown error"
3550
+ }), null;
3551
+ }
3552
+ }
3553
+ safeObserve(e, t, s, r = !1) {
3554
+ try {
3555
+ if (typeof PerformanceObserver > "u") return;
3556
+ const a = PerformanceObserver.supportedEntryTypes;
3557
+ if (a && !a.includes(e)) return;
3558
+ const o = new PerformanceObserver((l, c) => {
3559
+ if (t(l, c), r)
3560
+ try {
3561
+ c.disconnect();
3562
+ } catch {
3563
+ }
3564
+ });
3565
+ o.observe(s ?? { type: e, buffered: !0 }), r || this.observers.push(o);
3566
+ } catch (a) {
3567
+ i.warn("PerformanceHandler", "Failed to create performance observer", {
3568
+ type: e,
3569
+ error: a instanceof Error ? a.message : "Unknown error"
3570
+ });
3571
+ }
3572
+ }
3573
+ }
3574
+ class jt extends p {
3575
+ eventManager;
3576
+ piiPatterns = [
3577
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
3578
+ /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g,
3579
+ /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g,
3580
+ /\b[A-Z]{2}\d{2}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g
3581
+ ];
3582
+ constructor(e) {
3583
+ super(), this.eventManager = e;
3584
+ }
3585
+ startTracking() {
3586
+ i.debug("ErrorHandler", "Starting error tracking"), this.setupErrorListener(), this.setupUnhandledRejectionListener();
3587
+ }
3588
+ stopTracking() {
3589
+ i.debug("ErrorHandler", "Stopping error tracking"), window.removeEventListener("error", this.handleError), window.removeEventListener("unhandledrejection", this.handleUnhandledRejection);
3590
+ }
3591
+ setupErrorListener() {
3592
+ window.addEventListener("error", this.handleError);
3593
+ }
3594
+ setupUnhandledRejectionListener() {
3595
+ window.addEventListener("unhandledrejection", this.handleUnhandledRejection);
3596
+ }
3597
+ handleError = (e) => {
3598
+ const t = this.get("config");
3599
+ if (!this.shouldSample(t?.errorSampling ?? 0.1)) {
3600
+ i.debug("ErrorHandler", `Error not sampled, skipping (errorSampling: ${t?.errorSampling})`, {
3601
+ errorSampling: t?.errorSampling
3602
+ });
3603
+ return;
3604
+ }
3605
+ i.warn(
3606
+ "ErrorHandler",
3607
+ `JavaScript error captured: ${e.message} (filename: ${e.filename}, lineno: ${e.lineno})`,
3608
+ {
3609
+ message: e.message,
3610
+ filename: e.filename,
3611
+ lineno: e.lineno
3612
+ }
3613
+ ), this.eventManager.track({
3614
+ type: h.ERROR,
3615
+ error_data: {
3616
+ type: D.JS_ERROR,
3617
+ message: this.sanitizeText(e.message || "Unknown error")
3618
+ }
3619
+ });
3620
+ };
3621
+ handleUnhandledRejection = (e) => {
3622
+ const t = this.get("config");
3623
+ if (!this.shouldSample(t?.errorSampling ?? 0.1)) {
3624
+ i.debug("ErrorHandler", "Promise rejection not sampled, skipping", {
3625
+ errorSampling: t?.errorSampling
3626
+ });
3627
+ return;
3628
+ }
3629
+ i.warn("ErrorHandler", `Unhandled promise rejection captured (reason: ${typeof e.reason})`, {
3630
+ reason: typeof e.reason
3631
+ });
3632
+ let s = "Unknown rejection";
3633
+ e.reason && (typeof e.reason == "string" ? s = e.reason : e.reason instanceof Error ? s = e.reason.message || e.reason.toString() : s = String(e.reason)), this.eventManager.track({
3634
+ type: h.ERROR,
3635
+ error_data: {
3636
+ type: D.PROMISE_REJECTION,
3637
+ message: this.sanitizeText(s)
3638
+ }
3639
+ });
3640
+ };
3641
+ sanitizeText(e) {
3642
+ let t = e;
3643
+ for (const s of this.piiPatterns)
3644
+ t = t.replace(s, "[REDACTED]");
3645
+ return t;
3646
+ }
3647
+ shouldSample(e) {
3648
+ return Math.random() < e;
3649
+ }
3650
+ }
3651
+ class Gt extends p {
3652
+ eventManager;
3653
+ originalFetch;
3654
+ originalXHROpen;
3655
+ originalXHRSend;
3656
+ constructor(e) {
3657
+ super(), this.eventManager = e, this.originalFetch = window.fetch, this.originalXHROpen = XMLHttpRequest.prototype.open, this.originalXHRSend = XMLHttpRequest.prototype.send;
3658
+ }
3659
+ startTracking() {
3660
+ i.debug("NetworkHandler", "Starting network error tracking"), this.interceptFetch(), this.interceptXHR();
3661
+ }
3662
+ stopTracking() {
3663
+ i.debug("NetworkHandler", "Stopping network error tracking"), window.fetch = this.originalFetch, XMLHttpRequest.prototype.open = this.originalXHROpen, XMLHttpRequest.prototype.send = this.originalXHRSend;
3664
+ }
3665
+ interceptFetch() {
3666
+ window.fetch = async (e, t) => {
3667
+ const s = Date.now(), r = typeof e == "string" ? e : e.toString(), a = t?.method ?? "GET";
3668
+ try {
3669
+ const o = await this.originalFetch(e, t), l = Date.now() - s;
3670
+ return o.ok || (i.debug("NetworkHandler", "Fetch error detected", {
3671
+ method: a,
3672
+ url: this.normalizeUrlForTracking(r),
3673
+ status: o.status,
3674
+ statusText: o.statusText
3675
+ }), this.trackNetworkError(
3676
+ a.toUpperCase(),
3677
+ this.normalizeUrlForTracking(r),
3678
+ o.status,
3679
+ o.statusText,
3680
+ l
3681
+ )), o;
3682
+ } catch (o) {
3683
+ const l = Date.now() - s, c = o instanceof Error ? o.message : "Network Error";
3684
+ throw i.debug("NetworkHandler", "Fetch exception caught", {
3685
+ method: a,
3686
+ url: this.normalizeUrlForTracking(r),
3687
+ error: c
3688
+ }), this.trackNetworkError(
3689
+ a.toUpperCase(),
3690
+ this.normalizeUrlForTracking(r),
3691
+ void 0,
3692
+ c,
3693
+ l
3694
+ ), o;
3695
+ }
3696
+ };
3697
+ }
3698
+ interceptXHR() {
3699
+ const e = this.trackNetworkError.bind(this), t = this.normalizeUrlForTracking.bind(this), s = this.originalXHROpen, r = this.originalXHRSend;
3700
+ XMLHttpRequest.prototype.open = function(a, o, l, c, d) {
3701
+ const u = l ?? !0, g = this;
3702
+ return g._tracelogStartTime = Date.now(), g._tracelogMethod = a.toUpperCase(), g._tracelogUrl = o.toString(), s.call(this, a, o, u, c, d);
3703
+ }, XMLHttpRequest.prototype.send = function(a) {
3704
+ const o = this, l = o._tracelogStartTime ?? Date.now(), c = o._tracelogMethod ?? "GET", d = o._tracelogUrl ?? "", u = o.onreadystatechange;
3705
+ return o.onreadystatechange = (g) => {
3706
+ if (o.readyState === XMLHttpRequest.DONE) {
3707
+ const y = Date.now() - l;
3708
+ if (o.status === 0 || o.status >= 400) {
3709
+ const I = o.statusText || "Request Failed";
3710
+ i.debug("NetworkHandler", "XHR error detected", {
3711
+ method: c,
3712
+ url: t(d),
3713
+ status: o.status,
3714
+ statusText: I
3715
+ }), e(c, t(d), o.status, I, y);
3716
+ }
3717
+ }
3718
+ if (u)
3719
+ return u.call(o, g);
3720
+ }, r.call(this, a);
3721
+ };
3722
+ }
3723
+ trackNetworkError(e, t, s, r, a) {
3724
+ const o = this.get("config");
3725
+ if (!this.shouldSample(o?.errorSampling ?? 0.1)) {
3726
+ i.debug(
3727
+ "NetworkHandler",
3728
+ `Network error not sampled, skipping (errorSampling: ${o?.errorSampling}, method: ${e}, url: ${t})`,
3729
+ {
3730
+ errorSampling: o?.errorSampling,
3731
+ method: e,
3732
+ url: t
3733
+ }
3734
+ );
3735
+ return;
3736
+ }
3737
+ i.warn(
3738
+ "NetworkHandler",
3739
+ `Network error tracked: ${e} ${t} (status: ${s}, statusText: ${r}, duration: ${a}ms)`,
3740
+ { method: e, url: t, status: s, statusText: r, duration: a }
3741
+ ), this.eventManager.track({
3742
+ type: h.ERROR,
3743
+ error_data: {
3744
+ type: D.NETWORK_ERROR,
3745
+ message: r,
3746
+ method: e,
3747
+ url: t,
3748
+ status: s,
3749
+ statusText: r,
3750
+ duration: a
3751
+ }
3752
+ });
3753
+ }
3754
+ normalizeUrlForTracking(e) {
3755
+ try {
3756
+ const t = this.get("config");
3757
+ return se(e, t?.sensitiveQueryParams);
3758
+ } catch {
3759
+ return e;
3760
+ }
3761
+ }
3762
+ shouldSample(e) {
3763
+ return Math.random() < e;
3764
+ }
3765
+ }
3766
+ class Qt extends p {
3767
+ isInitialized = !1;
3768
+ googleAnalytics = null;
3769
+ storageManager;
3770
+ eventManager;
3771
+ sessionHandler;
3772
+ pageViewHandler;
3773
+ clickHandler;
3774
+ scrollHandler;
3775
+ performanceHandler;
3776
+ errorHandler;
3777
+ networkHandler;
3778
+ suppressNextScrollTimer = null;
3779
+ /**
3780
+ * Returns the initialization status of the app
3781
+ * @returns true if the app is fully initialized, false otherwise
3782
+ */
3783
+ get initialized() {
3784
+ return this.isInitialized;
3785
+ }
3786
+ async init(e) {
3787
+ if (this.isInitialized) {
3788
+ i.debug("App", "App already initialized, skipping re-initialization", { projectId: e.id });
3789
+ return;
3790
+ }
3791
+ i.info("App", "App initialization started", { projectId: e.id }), this.validateAppReadiness(e);
3792
+ try {
3793
+ this.initStorage(), await this.setState(e), await this.setIntegrations(), this.setEventManager(), await this.initHandlers(), this.isInitialized = !0, i.info("App", "App initialization completed successfully", {
3794
+ projectId: e.id
3795
+ });
3796
+ } catch (t) {
3797
+ throw this.isInitialized = !1, i.error("App", "App initialization failed", { projectId: e.id, error: t }), t;
3798
+ }
3799
+ }
3800
+ /**
3801
+ * Validates that the app is ready to initialize with the provided config
3802
+ * This is a lightweight runtime validation layer that ensures the app receives proper config
3803
+ * @param appConfig - The validated and normalized configuration
3804
+ * @throws {ProjectIdValidationError} If project ID is invalid at runtime
3805
+ */
3806
+ validateAppReadiness(e) {
3807
+ if (!e?.id)
3808
+ throw i.clientError("App", "Configuration integrity check failed - missing project ID", {
3809
+ hasConfig: !!e,
3810
+ hasId: !!e?.id
3811
+ }), new Q("Configuration integrity check failed", "app");
3812
+ }
3813
+ sendCustomEvent(e, t) {
3814
+ if (!this.eventManager) {
3815
+ i.warn("App", "Custom event attempted before eventManager initialization", { eventName: e });
3816
+ return;
3817
+ }
3818
+ const { valid: s, error: r, sanitizedMetadata: a } = St(e, t);
3819
+ if (s)
3820
+ i.debug("App", "Custom event validated and queued", { eventName: e, hasMetadata: !!a }), this.eventManager.track({
3821
+ type: h.CUSTOM,
3822
+ custom_event: {
3823
+ name: e,
3824
+ ...a && { metadata: a }
3825
+ }
3826
+ });
3827
+ else {
3828
+ const o = this.get("config")?.mode;
3829
+ if (i.clientError("App", `Custom event validation failed: ${r ?? "unknown error"}`, {
3830
+ eventName: e,
3831
+ validationError: r,
3832
+ hasMetadata: !!t,
3833
+ mode: o
3834
+ }), o === "qa" || o === "debug")
3835
+ throw new Error(
3836
+ `custom event "${e}" validation failed (${r ?? "unknown error"}). Please, review your event data and try again.`
3837
+ );
3838
+ }
3839
+ }
3840
+ destroy() {
3841
+ if (!this.isInitialized) {
3842
+ i.warn("App", "Destroy called but app was not initialized");
3843
+ return;
3844
+ }
3845
+ i.info("App", "App cleanup started"), this.googleAnalytics && this.googleAnalytics.cleanup(), this.sessionHandler && this.sessionHandler.stopTracking(), this.pageViewHandler && this.pageViewHandler.stopTracking(), this.clickHandler && this.clickHandler.stopTracking(), this.scrollHandler && this.scrollHandler.stopTracking(), this.performanceHandler && this.performanceHandler.stopTracking(), this.errorHandler && this.errorHandler.stopTracking(), this.networkHandler && this.networkHandler.stopTracking(), this.suppressNextScrollTimer && (clearTimeout(this.suppressNextScrollTimer), this.suppressNextScrollTimer = null), this.eventManager && this.eventManager.stop(), this.set("hasStartSession", !1), this.set("suppressNextScroll", !1), this.set("sessionId", null), this.isInitialized = !1, i.info("App", "App cleanup completed successfully");
3846
+ }
3847
+ async setState(e) {
3848
+ this.setApiUrl(e.id, e.allowHttp), await this.setConfig(e), this.setUserId(), this.setDevice(), this.setPageUrl();
3849
+ }
3850
+ setApiUrl(e, t = !1) {
3851
+ const s = new Et();
3852
+ this.set("apiUrl", s.getUrl(e, t));
3853
+ }
3854
+ async setConfig(e) {
3855
+ const s = await new wt().get(this.get("apiUrl"), e);
3856
+ this.set("config", s);
3857
+ }
3858
+ setUserId() {
3859
+ const t = new _t(this.storageManager).getId();
3860
+ this.set("userId", t);
3861
+ }
3862
+ setDevice() {
3863
+ const e = we();
3864
+ this.set("device", e);
3865
+ }
3866
+ setPageUrl() {
3867
+ const e = se(window.location.href, this.get("config").sensitiveQueryParams);
3868
+ this.set("pageUrl", e);
3869
+ }
3870
+ async setIntegrations() {
3871
+ const e = this.get("config").ipExcluded, t = this.get("config").integrations?.googleAnalytics?.measurementId;
3872
+ !e && t?.trim() && (this.googleAnalytics = new zt(), await this.googleAnalytics.initialize());
3873
+ }
3874
+ async initHandlers() {
3875
+ if (!this.eventManager)
3876
+ throw new Error("EventManager must be initialized before handlers");
3877
+ if (!this.storageManager)
3878
+ throw new Error("StorageManager must be initialized before handlers");
3879
+ this.initSessionHandler(), this.initPageViewHandler(), this.initClickHandler(), this.initScrollHandler(), await this.initPerformanceHandler(), this.initErrorHandler(), this.initNetworkHandler();
3880
+ }
3881
+ initStorage() {
3882
+ this.storageManager = new Vt();
3883
+ }
3884
+ setEventManager() {
3885
+ if (!this.storageManager)
3886
+ throw new Error("StorageManager must be initialized before EventManager");
3887
+ this.eventManager = new At(this.storageManager, this.googleAnalytics);
3888
+ }
3889
+ initSessionHandler() {
3890
+ if (!this.storageManager || !this.eventManager)
3891
+ throw new Error("StorageManager and EventManager must be initialized before SessionHandler");
3892
+ this.sessionHandler = new Dt(this.storageManager, this.eventManager), this.sessionHandler.startTracking();
3893
+ }
3894
+ initPageViewHandler() {
3895
+ if (!this.eventManager)
3896
+ throw new Error("EventManager must be initialized before PageViewHandler");
3897
+ const e = () => this.onPageViewTrack();
3898
+ this.pageViewHandler = new xt(this.eventManager, e), this.pageViewHandler.startTracking();
3899
+ }
3900
+ onPageViewTrack() {
3901
+ this.set("suppressNextScroll", !0), this.suppressNextScrollTimer && (clearTimeout(this.suppressNextScrollTimer), this.suppressNextScrollTimer = null), this.suppressNextScrollTimer = window.setTimeout(() => {
3902
+ this.set("suppressNextScroll", !1);
3903
+ }, Me * et.SUPPRESS_MULTIPLIER);
3904
+ }
3905
+ initClickHandler() {
3906
+ if (!this.eventManager)
3907
+ throw new Error("EventManager must be initialized before ClickHandler");
3908
+ this.clickHandler = new Ot(this.eventManager), this.clickHandler.startTracking();
3909
+ }
3910
+ initScrollHandler() {
3911
+ if (!this.eventManager)
3912
+ throw new Error("EventManager must be initialized before ScrollHandler");
3913
+ this.scrollHandler = new Ft(this.eventManager), this.scrollHandler.startTracking();
3914
+ }
3915
+ async initPerformanceHandler() {
3916
+ if (!this.eventManager)
3917
+ throw new Error("EventManager must be initialized before PerformanceHandler");
3918
+ this.performanceHandler = new $t(this.eventManager), await this.performanceHandler.startTracking();
3919
+ }
3920
+ initErrorHandler() {
3921
+ if (!this.eventManager)
3922
+ throw new Error("EventManager must be initialized before ErrorHandler");
3923
+ this.errorHandler = new jt(this.eventManager), this.errorHandler.startTracking();
3924
+ }
3925
+ initNetworkHandler() {
3926
+ if (!this.eventManager)
3927
+ throw new Error("EventManager must be initialized before NetworkHandler");
3928
+ this.networkHandler = new Gt(this.eventManager), this.networkHandler.startTracking();
3929
+ }
3930
+ }
3931
+ const Bt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3932
+ __proto__: null,
3933
+ DeviceType: E,
3934
+ ErrorType: D,
3935
+ EventType: h,
3936
+ ScrollDirection: j,
3937
+ TagConditionOperator: f,
3938
+ TagConditionType: m,
3939
+ TagLogicalOperator: G
3940
+ }, Symbol.toStringTag, { value: "Module" })), qt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3941
+ __proto__: null,
3942
+ DEFAULT_SESSION_TIMEOUT_MS: L
3943
+ }, Symbol.toStringTag, { value: "Module" }));
3944
+ let v = null, T = !1;
3945
+ const Wt = async (n) => {
3946
+ try {
3947
+ if (i.info("API", "Library initialization started", { id: n.id }), typeof window > "u" || typeof document > "u")
3948
+ throw i.clientError(
3949
+ "API",
3950
+ "Browser environment required - this library can only be used in a browser environment",
3951
+ {
3952
+ hasWindow: typeof window < "u",
3953
+ hasDocument: typeof document < "u"
3954
+ }
3955
+ ), new Error("This library can only be used in a browser environment");
3956
+ if (v) {
3957
+ i.debug("API", "Library already initialized, skipping duplicate initialization", {
3958
+ projectId: n.id
3959
+ });
3960
+ return;
3961
+ }
3962
+ if (T) {
3963
+ i.debug("API", "Concurrent initialization detected, waiting for completion", { projectId: n.id });
3964
+ let s = 0;
3965
+ const r = me.MAX_CONCURRENT_RETRIES, a = me.CONCURRENT_RETRY_DELAY_MS;
3966
+ for (; T && s < r; )
3967
+ await new Promise((o) => setTimeout(o, a)), s++;
3968
+ if (v) {
3969
+ i.debug("API", "Concurrent initialization completed successfully", {
3970
+ projectId: n.id,
3971
+ retriesUsed: s
3972
+ });
3973
+ return;
3974
+ }
3975
+ if (T)
3976
+ throw i.error("API", "Initialization timeout - concurrent initialization took too long", {
3977
+ projectId: n.id,
3978
+ retriesUsed: s,
3979
+ maxRetries: r
3980
+ }), new Error("App initialization timeout - concurrent initialization took too long");
3981
+ }
3982
+ T = !0, i.debug("API", "Validating and normalizing configuration", { projectId: n.id });
3983
+ const e = ht(n);
3984
+ i.debug("API", "Creating App instance", { projectId: e.id });
3985
+ const t = new Qt();
3986
+ await t.init(e), v = t, i.info("API", "Library initialization completed successfully", {
3987
+ projectId: e.id
3988
+ });
3989
+ } catch (e) {
3990
+ if (v && !v.initialized)
3991
+ try {
3992
+ v.destroy();
3993
+ } catch (t) {
3994
+ i.warn("API", "Failed to cleanup partially initialized app", { cleanupError: t });
3995
+ }
3996
+ throw v = null, i.error("API", "Initialization failed", { error: e }), e;
3997
+ } finally {
3998
+ T = !1;
3999
+ }
4000
+ }, Xt = (n, e) => {
4001
+ try {
4002
+ if (!v)
4003
+ throw i.clientError("API", "Custom event failed - Library not initialized. Please call TraceLog.init() first", {
4004
+ eventName: n,
4005
+ hasMetadata: !!e
4006
+ }), new Error("App not initialized");
4007
+ i.debug("API", "Sending custom event", {
4008
+ eventName: n,
4009
+ hasMetadata: !!e,
4010
+ metadataKeys: e ? Object.keys(e) : []
4011
+ }), v.sendCustomEvent(n, e);
4012
+ } catch (t) {
4013
+ if (i.error("API", "Event tracking failed", { eventName: n, error: t, hasMetadata: !!e }), t instanceof Error && (t.message === "App not initialized" || t.message.includes("validation failed")))
4014
+ throw t;
4015
+ }
4016
+ }, Kt = () => v !== null, Yt = () => ({
4017
+ isInitialized: v !== null,
4018
+ isInitializing: T,
4019
+ hasInstance: v !== null
4020
+ }), Jt = () => {
4021
+ try {
4022
+ if (i.info("API", "Library cleanup initiated"), !v)
4023
+ throw i.warn("API", "Cleanup called but Library was not initialized"), new Error("App not initialized");
4024
+ v.destroy(), v = null, T = !1, i.info("API", "Library cleanup completed successfully");
4025
+ } catch (n) {
4026
+ i.error("API", "Cleanup failed", { error: n, hadApp: !!v, wasInitializing: T });
4027
+ }
4028
+ }, Zt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4029
+ __proto__: null,
4030
+ Constants: qt,
4031
+ Types: Bt,
4032
+ destroy: Jt,
4033
+ event: Xt,
4034
+ getInitializationStatus: Yt,
4035
+ init: Wt,
4036
+ isInitialized: Kt
4037
+ }, Symbol.toStringTag, { value: "Module" }));
4038
+ export {
4039
+ Zt as TraceLog
4040
+ };