@levo-so/insights 0.1.80 → 0.1.82

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.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { k as H, l as F, $ as U, e as q, m as P, n as C, o as z, u as T, p as $, r as G, s as K, q as W, t as j, v as Q, w as N, x as X, y as Y, d as Z, z as J, A as V, B as ee, C as te, D as ne, E as re } from "./textSelection-CnZdywep.js";
1
+ import { k as H, l as F, a as U, i as q, m as P, n as C, o as z, u as T, p as G, r as $, s as K, q as W, t as j, v as Q, w as N, x as X, y as Y, d as Z, z as J, A as V, B as ee, C as te, D as ne, E as re } from "./textSelection-N3vefCQW.js";
2
2
  import { getLevoError as E } from "@levo-so/core";
3
3
  import { AnalyticsEvents as Pe } from "@levo-so/core";
4
4
  import { getVisitorSource as ie } from "@analytics/visitor-source";
@@ -437,7 +437,7 @@ const de = (a, i, e) => {
437
437
  */
438
438
  page: ({ payload: c }) => {
439
439
  if (!e()) return;
440
- G();
440
+ $();
441
441
  const r = c?.properties || {};
442
442
  i("page.view", {
443
443
  ...r,
@@ -508,7 +508,7 @@ const de = (a, i, e) => {
508
508
  const d = {
509
509
  Accept: "application/json",
510
510
  "Content-Type": "application/json",
511
- ...$({ sessionToken: h, deviceToken: y, mode: s })
511
+ ...G({ sessionToken: h, deviceToken: y, mode: s })
512
512
  };
513
513
  a.fetch.url(w, !0).url("/v1/insights/event/welcome").options({
514
514
  keepAlive: !0,
package/dist/stores.js CHANGED
@@ -1,4 +1,4 @@
1
- import { f as s, $ as t, h as $, a as i, d as o, b as c, g as n, i as l, j as r, e as f, c as u } from "./textSelection-CnZdywep.js";
1
+ import { $ as s, a as t, b as $, c as i, d as o, e as c, f as n, g as l, h as r, i as f, j as u } from "./textSelection-N3vefCQW.js";
2
2
  export {
3
3
  s as $activity,
4
4
  t as $config,
@@ -1,20 +1,20 @@
1
- import { getLevoError as V } from "@levo-so/core";
2
- import { map as B, computed as K, atom as b } from "nanostores";
3
- import { onUserActivity as fe } from "@analytics/activity-utils";
4
- import { debounce as G } from "lodash-es";
5
- const T = B({
1
+ import { getLevoError as z } from "@levo-so/core";
2
+ import { map as B, computed as J, atom as b } from "nanostores";
3
+ import { onUserActivity as de } from "@analytics/activity-utils";
4
+ import { debounce as K } from "lodash-es";
5
+ const x = B({
6
6
  workspace: null,
7
7
  page_id: null,
8
8
  site_id: null,
9
9
  debug: !1
10
- }), U = () => T.get(), Ve = (e) => T.set({ ...U(), ...e }), Qe = (e, t) => T.setKey(e, t), ge = ({
10
+ }), U = () => x.get(), ze = (e) => x.set({ ...U(), ...e }), Ve = (e, t) => x.setKey(e, t), fe = ({
11
11
  deviceToken: e,
12
12
  sessionToken: t,
13
13
  mode: n
14
14
  }) => {
15
15
  const i = {};
16
16
  return n === "direct" && (e && (i["Levo-Audience-Device"] = e), t && (i["Levo-Audience-Session"] = t)), i;
17
- }, y = {
17
+ }, v = {
18
18
  /** Legacy: Lock key for identify operation (no longer used internally) */
19
19
  identify: "lock:lv_aud_identify",
20
20
  /** Key for tracking open tab count */
@@ -31,7 +31,7 @@ const T = B({
31
31
  mode: "lv_aud_mode",
32
32
  /** Key to store session data **/
33
33
  session: "lv_insights_session"
34
- }, pe = () => {
34
+ }, ge = () => {
35
35
  if (typeof window > "u" || !("localStorage" in window))
36
36
  return !1;
37
37
  try {
@@ -51,65 +51,65 @@ const T = B({
51
51
  mode: "proxy",
52
52
  insightsBaseUrl: null,
53
53
  identity: null
54
- }, he = JSON.stringify, Q = (e) => {
54
+ }, pe = JSON.stringify, V = (e) => {
55
55
  if (!e) return M;
56
56
  try {
57
57
  return JSON.parse(e);
58
58
  } catch {
59
59
  return M;
60
60
  }
61
- }, p = b(M), v = p.set;
62
- let L = !1;
63
- const O = [], k = () => {
64
- L || (L = !0, O.forEach((e) => {
61
+ }, p = b(M), y = p.set;
62
+ let N = !1;
63
+ const O = [], L = () => {
64
+ N || (N = !0, O.forEach((e) => {
65
65
  e();
66
66
  }), O.length = 0);
67
- }, We = (e) => {
68
- L ? e() : O.push(e);
67
+ }, Qe = (e) => {
68
+ N ? e() : O.push(e);
69
69
  };
70
- if (pe()) {
71
- const e = localStorage.getItem(y.session);
72
- e && v(Q(e)), k(), p.set = (t) => {
73
- v(t);
70
+ if (ge()) {
71
+ const e = localStorage.getItem(v.session);
72
+ e && y(V(e)), L(), p.set = (t) => {
73
+ y(t);
74
74
  try {
75
- localStorage.setItem(y.session, he(t));
75
+ localStorage.setItem(v.session, pe(t));
76
76
  } catch {
77
77
  }
78
78
  }, window.addEventListener("storage", (t) => {
79
- t.key === y.session && v(Q(t.newValue));
79
+ t.key === v.session && y(V(t.newValue));
80
80
  });
81
81
  } else if (typeof BroadcastChannel < "u") {
82
- const e = new BroadcastChannel(y.session);
82
+ const e = new BroadcastChannel(v.session);
83
83
  e.onmessage = (t) => {
84
84
  const { type: n, value: i } = t.data;
85
- n === "UPDATE" && i ? v(i) : n === "QUERY" ? L && e.postMessage({ type: "RESPONSE", value: p.get() }) : n === "RESPONSE" && i && (v(i), k());
85
+ n === "UPDATE" && i ? y(i) : n === "QUERY" ? N && e.postMessage({ type: "RESPONSE", value: p.get() }) : n === "RESPONSE" && i && (y(i), L());
86
86
  }, p.set = (t) => {
87
- v(t), e.postMessage({ type: "UPDATE", value: t });
87
+ y(t), e.postMessage({ type: "UPDATE", value: t });
88
88
  }, e.postMessage({ type: "QUERY" }), setTimeout(() => {
89
- k();
89
+ L();
90
90
  }, 150);
91
91
  } else
92
- k();
93
- const H = () => p.get(), W = (e) => {
92
+ L();
93
+ const H = () => p.get(), Q = (e) => {
94
94
  p.set({ ...H(), ...e });
95
- }, Je = K(
95
+ }, We = J(
96
96
  p,
97
97
  (e) => e.insightsBaseUrl !== null && e.identity !== null
98
- ), me = 10, be = 2e3, J = 500, m = b([]), N = b(0), ve = K(m, (e) => e.length ?? 0), S = b(null), Ke = () => {
98
+ ), me = 10, he = 2e3, W = 500, h = b([]), I = b(0), be = J(h, (e) => e.length ?? 0), T = b(null), Je = () => {
99
99
  let e = !1;
100
100
  return p.subscribe((t) => {
101
101
  if (t.isIdentified && !e) {
102
102
  e = !0;
103
- const n = m.get();
104
- n.length > 0 && x(n);
103
+ const n = h.get();
104
+ n.length > 0 && C(n);
105
105
  }
106
106
  });
107
- }, I = () => m.get(), ye = () => ve.get(), x = (e) => {
107
+ }, A = () => h.get(), ye = () => be.get(), C = (e) => {
108
108
  const { workspace: t, debug: n, site_id: i, page_id: o } = U(), { isIdentified: r, mode: c, sessionId: u, deviceId: a, sessionToken: s, deviceToken: d, insightsBaseUrl: f } = H();
109
109
  if (e.length === 0 || !r || !f || !t)
110
110
  return;
111
- const h = e.map(
112
- (g) => ({
111
+ const g = e.map(
112
+ (m) => ({
113
113
  // Default values from stores
114
114
  workspace_id: t,
115
115
  site_id: i ?? void 0,
@@ -123,59 +123,59 @@ const H = () => p.get(), W = (e) => {
123
123
  tab_id: "",
124
124
  tab_count: 0,
125
125
  // Event values override defaults (includes tab_id, tab_count captured at track time)
126
- ...g,
126
+ ...m,
127
127
  // Ensure required fields have values
128
- event: g.event,
129
- created_at: g.created_at,
130
- resource: g.resource ?? "page",
131
- properties: g.properties ?? {}
128
+ event: m.event,
129
+ created_at: m.created_at,
130
+ resource: m.resource ?? "page",
131
+ properties: m.properties ?? {}
132
132
  })
133
133
  );
134
- if (m.set([]), n) {
135
- console.log("[@levo-so/insights] Debug mode enabled. Events batch:", h);
134
+ if (h.set([]), n) {
135
+ console.log("[@levo-so/insights] Debug mode enabled. Events batch:", g);
136
136
  return;
137
137
  }
138
- const C = `${f}/v1/insights/event/bulk?workspace=${t}`, R = {
138
+ const k = `${f}/v1/insights/event/bulk?workspace=${t}`, E = {
139
139
  "Content-Type": "application/json",
140
- ...ge({ deviceToken: d, sessionToken: s, mode: c })
140
+ ...fe({ deviceToken: d, sessionToken: s, mode: c })
141
141
  };
142
- c === "proxy" && navigator.sendBeacon && navigator.sendBeacon(C, JSON.stringify(h)) || fetch(C, {
142
+ c === "proxy" && navigator.sendBeacon && navigator.sendBeacon(k, JSON.stringify(g)) || fetch(k, {
143
143
  method: "POST",
144
- body: JSON.stringify(h),
145
- headers: R,
144
+ body: JSON.stringify(g),
145
+ headers: E,
146
146
  keepalive: !0,
147
147
  credentials: c === "proxy" ? "include" : "omit"
148
148
  }).catch(() => {
149
149
  });
150
- }, we = () => {
151
- if (S.get()) return;
150
+ }, ve = () => {
151
+ if (T.get()) return;
152
152
  const e = setTimeout(() => {
153
- x(I()), S.set(null);
154
- }, be);
155
- S.set(e);
156
- }, X = () => {
157
- const e = S.get();
158
- e && (clearTimeout(e), S.set(null));
159
- }, Ge = (e) => {
160
- const { debug: t } = U(), n = N.get();
161
- if (n >= J) {
162
- t && console.warn(`[@levo-so/core] Event limit (${J}) reached for this page`);
153
+ C(A()), T.set(null);
154
+ }, he);
155
+ T.set(e);
156
+ }, G = () => {
157
+ const e = T.get();
158
+ e && (clearTimeout(e), T.set(null));
159
+ }, Ke = (e) => {
160
+ const { debug: t } = U(), n = I.get();
161
+ if (n >= W) {
162
+ t && console.warn(`[@levo-so/core] Event limit (${W}) reached for this page`);
163
163
  return;
164
164
  }
165
- N.set(n + 1);
166
- const i = m.get();
167
- m.set([...i, e]);
165
+ I.set(n + 1);
166
+ const i = h.get();
167
+ h.set([...i, e]);
168
168
  const { isIdentified: o } = H();
169
- ye() >= me ? (X(), x(I())) : o && we();
170
- }, Xe = () => N.set(0), Ze = () => {
171
- m.set([]), N.set(0), X();
169
+ ye() >= me ? (G(), C(A())) : o && ve();
170
+ }, Ge = () => I.set(0), Xe = () => {
171
+ h.set([]), I.set(0), G();
172
172
  }, _ = B({
173
173
  status: "active",
174
174
  seconds: 0
175
- }), et = (e) => {
175
+ }), Ze = (e) => {
176
176
  if (typeof window > "u") return () => {
177
177
  };
178
- const i = fe({
178
+ const i = de({
179
179
  timeout: 6e4,
180
180
  throttle: 1e3,
181
181
  onIdle: (r) => {
@@ -194,26 +194,26 @@ const H = () => p.get(), W = (e) => {
194
194
  return () => {
195
195
  o(), i.disable(), _.set({ status: "active", seconds: 0 });
196
196
  };
197
- }, Ee = () => _.get(), E = b(
197
+ }, we = () => _.get(), S = b(
198
198
  "active"
199
199
  /* ACTIVE */
200
- ), _e = () => typeof document > "u" ? "active" : document.visibilityState === "hidden" ? "hidden" : document.hasFocus() ? "active" : "passive", tt = (e) => {
200
+ ), Ee = () => typeof document > "u" ? "active" : document.visibilityState === "hidden" ? "hidden" : document.hasFocus() ? "active" : "passive", et = (e) => {
201
201
  if (typeof window > "u") return () => {
202
202
  };
203
203
  let t = !1;
204
204
  const n = (s) => {
205
205
  if (t) return;
206
206
  t = !0;
207
- const d = Ee(), f = {};
207
+ const d = we(), f = {};
208
208
  d.seconds > 0 && (d.status === "idle" ? f.seconds_idle = d.seconds : f.seconds_active = d.seconds);
209
209
  try {
210
210
  e.track("page.bounce", {
211
211
  resource: "page",
212
212
  ...f,
213
213
  ...s
214
- }), x(I());
215
- } catch (h) {
216
- console.error("[insights] Failed to send bounce event", V(h));
214
+ }), C(A());
215
+ } catch (g) {
216
+ console.error("[insights] Failed to send bounce event", z(g));
217
217
  }
218
218
  }, i = () => {
219
219
  const s = performance.getEntriesByType("navigation")?.[0];
@@ -223,7 +223,7 @@ const H = () => p.get(), W = (e) => {
223
223
  const f = document.activeElement;
224
224
  f?.tagName === "A" && f.href && new URL(f.href).origin !== window.location.origin && (d = !0);
225
225
  } catch (f) {
226
- console.warn("[insights] Failed to parse link destination", V(f));
226
+ console.warn("[insights] Failed to parse link destination", z(f));
227
227
  }
228
228
  n({
229
229
  is_navigating_away: d,
@@ -237,16 +237,16 @@ const H = () => p.get(), W = (e) => {
237
237
  case "blur":
238
238
  case "focus":
239
239
  case "visibilitychange":
240
- E.set(_e());
240
+ S.set(Ee());
241
241
  break;
242
242
  case "freeze":
243
- E.set(
243
+ S.set(
244
244
  "frozen"
245
245
  /* FROZEN */
246
246
  );
247
247
  break;
248
248
  case "pagehide":
249
- E.set(
249
+ S.set(
250
250
  s.persisted ? "frozen" : "terminated"
251
251
  /* TERMINATED */
252
252
  );
@@ -256,35 +256,35 @@ const H = () => p.get(), W = (e) => {
256
256
  r.forEach((s) => {
257
257
  window.addEventListener(s, o, { capture: !0 });
258
258
  }), window.addEventListener("beforeunload", i);
259
- const c = E.listen((s) => {
259
+ const c = S.listen((s) => {
260
260
  s === "frozen" || s === "terminated" ? n({
261
261
  state: s,
262
262
  trigger: "lifecycle"
263
- }) : s === "hidden" && x(I());
263
+ }) : s === "hidden" && C(A());
264
264
  });
265
- let u = T.get().page_id;
266
- const a = T.listen((s) => {
265
+ let u = x.get().page_id;
266
+ const a = x.listen((s) => {
267
267
  s.page_id !== u && (t = !1, u = s.page_id);
268
268
  });
269
269
  return () => {
270
270
  r.forEach((s) => {
271
271
  window.removeEventListener(s, o, { capture: !0 });
272
- }), window.removeEventListener("beforeunload", i), c(), a(), t = !1, E.set(
272
+ }), window.removeEventListener("beforeunload", i), c(), a(), t = !1, S.set(
273
273
  "active"
274
274
  /* ACTIVE */
275
275
  );
276
276
  };
277
- }, A = (e) => Object.prototype.toString.call(e) == "[object String]", Se = (e, t) => e.indexOf(t) !== -1, w = (e) => e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""), Z = (e) => e === void 0, Te = (e) => e === null, ee = (e) => Z(e) || Te(e), te = (e) => !!e && e?.nodeType === 3;
278
- function xe(e) {
277
+ }, R = (e) => Object.prototype.toString.call(e) == "[object String]", Se = (e, t) => e.indexOf(t) !== -1, w = (e) => e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""), X = (e) => e === void 0, _e = (e) => e === null, Z = (e) => X(e) || _e(e), ee = (e) => !!e && e?.nodeType === 3;
278
+ function Te(e) {
279
279
  return !!e && e?.nodeType === 1;
280
280
  }
281
- function Ce(e, t) {
281
+ function xe(e, t) {
282
282
  return !!e && !!e?.tagName && e?.tagName.toLowerCase() === t.toLowerCase();
283
283
  }
284
- function ke(e) {
284
+ function Ce(e) {
285
285
  return e ? w(e).split(/\s+/) : [];
286
286
  }
287
- function Le(e) {
287
+ function ke(e) {
288
288
  let t = "";
289
289
  switch (typeof e?.className) {
290
290
  case "string":
@@ -297,15 +297,15 @@ function Le(e) {
297
297
  default:
298
298
  t = "";
299
299
  }
300
- return ke(t);
300
+ return Ce(t);
301
301
  }
302
- const l = (e, t = 255) => ee(e) ? "" : w(e).split(/(\s+)/).filter((n) => Y(n)).join("").replace(/[\r\n]/g, " ").replace(/[ ]+/g, " ").substring(0, t), ne = ["span", "strong", "em", "i", "b", "mark", "code", "small", "sub", "sup", "label"], F = (e) => {
302
+ const l = (e, t = 255) => Z(e) ? "" : w(e).split(/(\s+)/).filter((n) => Y(n)).join("").replace(/[\r\n]/g, " ").replace(/[ ]+/g, " ").substring(0, t), te = ["span", "strong", "em", "i", "b", "mark", "code", "small", "sub", "sup", "label"], F = (e) => {
303
303
  let t = "";
304
- if (Re(e) && e?.childNodes && e?.childNodes.length) {
304
+ if (Ae(e) && e?.childNodes && e?.childNodes.length) {
305
305
  for (const n of e.childNodes)
306
- te(n) && n.textContent && (t += l(n.textContent) ?? "");
306
+ ee(n) && n.textContent && (t += l(n.textContent) ?? "");
307
307
  if (!w(t)) {
308
- const n = e.querySelectorAll(ne.join(", "));
308
+ const n = e.querySelectorAll(te.join(", "));
309
309
  for (const i of n) {
310
310
  const o = i.textContent;
311
311
  o && (t += `${l(o)} `);
@@ -317,8 +317,8 @@ const l = (e, t = 255) => ee(e) ? "" : w(e).split(/(\s+)/).filter((n) => Y(n)).j
317
317
  }
318
318
  }
319
319
  return w(t);
320
- }, Ne = (e) => Z(e.target) ? e?.srcElement || null : e?.target?.shadowRoot ? e?.composedPath()?.[0] || null : e?.target || null, Ie = (e, t) => {
321
- if (!window || !e || Ce(e, "html") || !xe(e))
320
+ }, Le = (e) => X(e.target) ? e?.srcElement || null : e?.target?.shadowRoot ? e?.composedPath()?.[0] || null : e?.target || null, Ne = (e, t) => {
321
+ if (!window || !e || xe(e, "html") || !Te(e))
322
322
  return !1;
323
323
  const n = window.getComputedStyle(e);
324
324
  if (n && n.getPropertyValue("cursor") === "pointer" && t.type === "click")
@@ -335,7 +335,7 @@ const l = (e, t = 255) => ee(e) ? "" : w(e).split(/(\s+)/).filter((n) => Y(n)).j
335
335
  default:
336
336
  return t.type === "click";
337
337
  }
338
- }, Ae = /* @__PURE__ */ new Set([
338
+ }, Ie = /* @__PURE__ */ new Set([
339
339
  "password",
340
340
  "passwd",
341
341
  "pwd",
@@ -356,11 +356,11 @@ const l = (e, t = 255) => ee(e) ? "" : w(e).split(/(\s+)/).filter((n) => Y(n)).j
356
356
  "apikey",
357
357
  "accesstoken"
358
358
  ]);
359
- function Re(e) {
360
- if (Se(Le(e), "ph-include"))
359
+ function Ae(e) {
360
+ if (Se(ke(e), "ph-include"))
361
361
  return !0;
362
362
  const t = e.type || "";
363
- if (A(t))
363
+ if (R(t))
364
364
  switch (t.toLowerCase()) {
365
365
  case "hidden":
366
366
  return !1;
@@ -368,33 +368,33 @@ function Re(e) {
368
368
  return !1;
369
369
  }
370
370
  const n = e.name || e?.id || "";
371
- if (A(n)) {
371
+ if (R(n)) {
372
372
  const i = n.toLowerCase().replace(/[^a-z]/g, "");
373
- if (Ae.has(i))
373
+ if (Ie.has(i))
374
374
  return !1;
375
375
  }
376
376
  return !0;
377
377
  }
378
- const se = "(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})", $e = new RegExp(`^(?:${se})$`), De = new RegExp(se), ie = "\\d{3}-?\\d{2}-?\\d{4}", Me = new RegExp(`^(${ie})$`), Oe = new RegExp(`(${ie})`), Y = (e, t = !0) => !(ee(e) || A(e) && (e = w(e), (t ? $e : De).test((e || "").replace(/[- ]/g, "")) || (t ? Me : Oe).test(e))), Pe = (e) => A(e) ? e.substring(0, 10) === "_ngcontent" || e.substring(0, 7) === "_nghost" : !1, Be = (e) => {
378
+ const ne = "(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})", Re = new RegExp(`^(?:${ne})$`), $e = new RegExp(ne), se = "\\d{3}-?\\d{2}-?\\d{4}", De = new RegExp(`^(${se})$`), Me = new RegExp(`(${se})`), Y = (e, t = !0) => !(Z(e) || R(e) && (e = w(e), (t ? Re : $e).test((e || "").replace(/[- ]/g, "")) || (t ? De : Me).test(e))), Oe = (e) => R(e) ? e.substring(0, 10) === "_ngcontent" || e.substring(0, 7) === "_nghost" : !1, Pe = (e) => {
379
379
  let t = F(e);
380
- return t = `${t} ${oe(e)}`.trim(), Y(t) ? t : "";
380
+ return t = `${t} ${ie(e)}`.trim(), Y(t) ? t : "";
381
381
  };
382
- function oe(e) {
382
+ function ie(e) {
383
383
  let t = "";
384
384
  if (e?.childNodes.length)
385
385
  for (const n of e.childNodes) {
386
386
  const i = n.tagName?.toLowerCase();
387
- if (n && i && ne.includes(i))
387
+ if (n && i && te.includes(i))
388
388
  try {
389
389
  const o = F(n);
390
- t = `${t} ${o}`.trim(), n?.childNodes.length && (t = `${t} ${oe(n)}`.trim());
390
+ t = `${t} ${o}`.trim(), n?.childNodes.length && (t = `${t} ${ie(n)}`.trim());
391
391
  } catch {
392
392
  }
393
393
  }
394
394
  return t;
395
395
  }
396
- const Ue = (e) => {
397
- const t = e.tagName?.toLowerCase(), n = t === "a" || t === "button" ? Be(e) : F(e);
396
+ const Be = (e) => {
397
+ const t = e.tagName?.toLowerCase(), n = t === "a" || t === "button" ? Pe(e) : F(e);
398
398
  if (n) return { text: n, source: "text" };
399
399
  const i = e.getAttribute("aria-label");
400
400
  if (i) return { text: l(i), source: "aria-label" };
@@ -415,10 +415,10 @@ const Ue = (e) => {
415
415
  return { text: l(a), source: "innerText" };
416
416
  }
417
417
  return { text: "", source: "none" };
418
- }, He = ["input", "textarea", "select"], Fe = (e) => {
418
+ }, Ue = ["input", "textarea", "select"], He = (e) => {
419
419
  const t = e?.tagName?.toLowerCase(), n = {
420
420
  element: t
421
- }, i = Ue(e);
421
+ }, i = Be(e);
422
422
  n.text = i.text, n.text_source = i.source;
423
423
  const o = e.getAttribute("aria-label");
424
424
  o && (n.aria_label = l(o));
@@ -430,7 +430,7 @@ const Ue = (e) => {
430
430
  Object.keys(c).length && (n.tracking_data = c);
431
431
  const u = Array.from(e.attributes).reduce(
432
432
  (a, s) => {
433
- if (Y(s.value) && !Pe(s.name)) {
433
+ if (Y(s.value) && !Oe(s.name)) {
434
434
  if (s.name === "class" || s.name.startsWith("data-track-") || s.name.startsWith("data-analytics-"))
435
435
  return a;
436
436
  a[s.name] = typeof s?.value == "string" ? l(s.value) : s.value;
@@ -439,7 +439,7 @@ const Ue = (e) => {
439
439
  },
440
440
  {}
441
441
  );
442
- if (n.attributes = u, He.includes(t) || e?.getAttribute("contenteditable") === "true") {
442
+ if (n.attributes = u, Ue.includes(t) || e?.getAttribute("contenteditable") === "true") {
443
443
  let a;
444
444
  e.matches('input[type="checkbox"], input[type="radio"]') ? a = e.checked : e.matches("input, select, textarea") && !e.matches('input[type="password"]') && (a = e.value), a !== void 0 && (n.value = typeof a == "string" || a === null ? l(a, 1024) : a);
445
445
  let s = "";
@@ -450,12 +450,12 @@ const Ue = (e) => {
450
450
  s || (s = l(d?.textContent?.trim() || "")), s && (n.label = s);
451
451
  }
452
452
  return n;
453
- }, nt = (e, t) => {
454
- let n = Ne(e);
455
- te(n) && (n = n.parentNode || null);
453
+ }, tt = (e, t) => {
454
+ let n = Le(e);
455
+ ee(n) && (n = n.parentNode || null);
456
456
  const i = ["copy", "cut"].includes(t);
457
- if (n && (i || Ie(n, e))) {
458
- const o = Fe(n);
457
+ if (n && (i || Ne(n, e))) {
458
+ const o = He(n);
459
459
  if (i) {
460
460
  const r = l(window?.getSelection()?.toString()), c = e?.type || "clipboard";
461
461
  o.copy_text = r ?? "", o.copy_type = c;
@@ -463,59 +463,58 @@ const Ue = (e) => {
463
463
  return o;
464
464
  }
465
465
  return null;
466
- }, P = b(null), st = (e) => {
466
+ }, P = b(null), nt = (e) => {
467
467
  if (typeof window > "u") return () => {
468
468
  };
469
- let t = sessionStorage.getItem(y.current_tab);
469
+ let t = sessionStorage.getItem(v.current_tab);
470
470
  const { allTabs: n } = p.get();
471
- t && n.includes(t) && (t = null), t || (t = `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, sessionStorage.setItem(y.current_tab, t));
471
+ t && n.includes(t) && (t = null), t || (t = `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, sessionStorage.setItem(v.current_tab, t));
472
472
  const i = t;
473
- P.set(i), n.includes(i) || W({ allTabs: [...n, i] });
473
+ P.set(i), n.includes(i) || Q({ allTabs: [...n, i] });
474
474
  const o = () => {
475
475
  if (!i) return;
476
476
  const { allTabs: r } = p.get();
477
477
  if (r.includes(i)) {
478
478
  const c = r.filter((u) => u !== i);
479
- W({ allTabs: c });
479
+ Q({ allTabs: c });
480
480
  }
481
481
  };
482
482
  return window.addEventListener("beforeunload", o), () => {
483
483
  window.removeEventListener("beforeunload", o), o(), P.set(null);
484
484
  };
485
- }, it = () => P.get(), $ = B({
485
+ }, st = () => P.get(), $ = B({
486
486
  depth: 0,
487
+ milestone: 0,
487
488
  speedCategory: "read",
488
489
  direction: "down"
489
- }), ot = (e) => {
490
+ }), it = (e) => {
490
491
  if (typeof window > "u") return () => {
491
492
  };
492
493
  let t = {
493
- prevMilestone: -1,
494
494
  // Use -1 to ensure first milestone (even 0) is tracked
495
495
  prevScrollY: 0,
496
496
  prevTime: Date.now()
497
497
  };
498
498
  const n = 5, i = 300, o = 250, r = () => {
499
- const s = window.scrollY, d = document.documentElement.scrollHeight, f = window.innerHeight, h = d - f;
500
- if (h <= 0) return;
501
- const C = Math.round(s / h * 100), R = Math.max(0, Math.min(100, C)), g = Math.floor(R / n) * n, { prevMilestone: j, prevScrollY: re, prevTime: ae } = t;
502
- if (g === j) return;
503
- const q = Date.now(), z = (q - ae) / 1e3, ce = Math.abs(s - re), le = z > 0 ? ce / z : 0, ue = g > j ? "down" : "up", de = le > i ? "scan" : "read";
499
+ const s = window.scrollY, d = document.documentElement.scrollHeight, f = window.innerHeight, g = d - f;
500
+ if (g <= 0) return;
501
+ const k = Math.round(s / g * 100), E = Math.max(0, Math.min(100, k)), m = Math.floor(E / n) * n, { prevScrollY: oe, prevTime: re } = t, j = Date.now(), q = (j - re) / 1e3, ae = Math.abs(s - oe), ce = q > 0 ? ae / q : 0, le = E > t.prevScrollY / g * 100 ? "down" : "up", ue = ce > i ? "scan" : "read";
504
502
  $.set({
505
- depth: g,
506
- speedCategory: de,
507
- direction: ue
503
+ depth: E,
504
+ milestone: m,
505
+ speedCategory: ue,
506
+ direction: le
508
507
  }), t = {
509
- prevMilestone: g,
508
+ prevMilestone: m,
510
509
  prevScrollY: s,
511
- prevTime: q
510
+ prevTime: j
512
511
  };
513
- }, c = G(r, o);
512
+ }, c = K(r, o);
514
513
  window.addEventListener("scroll", c);
515
514
  let u = 0;
516
515
  const a = $.listen((s) => {
517
- s.depth !== u && (u = s.depth, e.track("page.scroll", {
518
- depth: s.depth,
516
+ s.milestone !== u && (u = s.milestone, e.track("page.scroll", {
517
+ depth: s.milestone,
519
518
  speed_category: s.speedCategory,
520
519
  direction: s.direction
521
520
  }));
@@ -523,14 +522,15 @@ const Ue = (e) => {
523
522
  return typeof window.requestIdleCallback == "function" ? window.requestIdleCallback(() => r()) : setTimeout(r, 0), () => {
524
523
  window.removeEventListener("scroll", c), c.cancel(), a(), $.set({
525
524
  depth: 0,
525
+ milestone: 0,
526
526
  speedCategory: "read",
527
527
  direction: "down"
528
528
  });
529
529
  };
530
- }, D = b(null), rt = (e) => {
530
+ }, D = b(null), ot = (e) => {
531
531
  if (typeof window > "u") return () => {
532
532
  };
533
- const n = G(() => {
533
+ const n = K(() => {
534
534
  const o = window.getSelection();
535
535
  if (o && o.rangeCount > 0) {
536
536
  const r = o.toString().trim();
@@ -548,36 +548,36 @@ const Ue = (e) => {
548
548
  };
549
549
  };
550
550
  export {
551
- T as $,
552
- et as A,
553
- tt as B,
554
- st as C,
555
- ot as D,
556
- rt as E,
557
- m as a,
558
- N as b,
559
- ve as c,
560
- Je as d,
551
+ x as $,
552
+ Ze as A,
553
+ et as B,
554
+ nt as C,
555
+ it as D,
556
+ ot as E,
557
+ h as a,
558
+ I as b,
559
+ be as c,
560
+ We as d,
561
561
  p as e,
562
562
  _ as f,
563
- E as g,
563
+ S as g,
564
564
  P as h,
565
565
  $ as i,
566
566
  D as j,
567
- Fe as k,
568
- nt as l,
567
+ He as k,
568
+ tt as l,
569
569
  U as m,
570
570
  H as n,
571
- it as o,
572
- ge as p,
573
- Ge as q,
574
- Xe as r,
575
- Ve as s,
576
- Qe as t,
577
- W as u,
578
- We as v,
579
- x as w,
580
- I as x,
581
- Ze as y,
582
- Ke as z
571
+ st as o,
572
+ fe as p,
573
+ Ke as q,
574
+ Ge as r,
575
+ ze as s,
576
+ Ve as t,
577
+ Q as u,
578
+ Qe as v,
579
+ C as w,
580
+ A as x,
581
+ Xe as y,
582
+ Je as z
583
583
  };
@@ -0,0 +1,583 @@
1
+ import { getLevoError as z } from "@levo-so/core";
2
+ import { map as B, computed as J, atom as b } from "nanostores";
3
+ import { onUserActivity as de } from "@analytics/activity-utils";
4
+ import { debounce as K } from "lodash-es";
5
+ const x = B({
6
+ workspace: null,
7
+ page_id: null,
8
+ site_id: null,
9
+ debug: !1
10
+ }), U = () => x.get(), ze = (e) => x.set({ ...U(), ...e }), Ve = (e, t) => x.setKey(e, t), fe = ({
11
+ deviceToken: e,
12
+ sessionToken: t,
13
+ mode: n
14
+ }) => {
15
+ const i = {};
16
+ return n === "direct" && (e && (i["Levo-Audience-Device"] = e), t && (i["Levo-Audience-Session"] = t)), i;
17
+ }, v = {
18
+ /** Legacy: Lock key for identify operation (no longer used internally) */
19
+ identify: "lock:lv_aud_identify",
20
+ /** Key for tracking open tab count */
21
+ open_tabs: "lv_insights_ot",
22
+ /** Key for current tab ID (sessionStorage) */
23
+ current_tab: "lv_insights_ct",
24
+ /** Key for all tabs registry */
25
+ all_tabs: "lv_insights_at",
26
+ /** Device JWT for direct mode (1yr expiry, rotated on /welcome, analytics-only scope) */
27
+ device_token: "lv_aud_device",
28
+ /** Session JWT for direct mode (1yr expiry, rotated on /welcome, analytics-only scope) */
29
+ session_token: "lv_aud_session",
30
+ /** Cached insights mode ('proxy' | 'direct') */
31
+ mode: "lv_aud_mode",
32
+ /** Key to store session data **/
33
+ session: "lv_insights_session"
34
+ }, ge = () => {
35
+ if (typeof window > "u" || !("localStorage" in window))
36
+ return !1;
37
+ try {
38
+ const e = "__levo_test__";
39
+ return window.localStorage.setItem(e, "test"), window.localStorage.removeItem(e), !0;
40
+ } catch {
41
+ return !1;
42
+ }
43
+ }, M = {
44
+ isIdentified: !1,
45
+ isIdentifying: !1,
46
+ sessionId: null,
47
+ deviceId: null,
48
+ deviceToken: null,
49
+ sessionToken: null,
50
+ allTabs: [],
51
+ mode: "proxy",
52
+ insightsBaseUrl: null,
53
+ identity: null
54
+ }, pe = JSON.stringify, V = (e) => {
55
+ if (!e) return M;
56
+ try {
57
+ return JSON.parse(e);
58
+ } catch {
59
+ return M;
60
+ }
61
+ }, p = b(M), y = p.set;
62
+ let N = !1;
63
+ const O = [], L = () => {
64
+ N || (N = !0, O.forEach((e) => {
65
+ e();
66
+ }), O.length = 0);
67
+ }, Qe = (e) => {
68
+ N ? e() : O.push(e);
69
+ };
70
+ if (ge()) {
71
+ const e = localStorage.getItem(v.session);
72
+ e && y(V(e)), L(), p.set = (t) => {
73
+ y(t);
74
+ try {
75
+ localStorage.setItem(v.session, pe(t));
76
+ } catch {
77
+ }
78
+ }, window.addEventListener("storage", (t) => {
79
+ t.key === v.session && y(V(t.newValue));
80
+ });
81
+ } else if (typeof BroadcastChannel < "u") {
82
+ const e = new BroadcastChannel(v.session);
83
+ e.onmessage = (t) => {
84
+ const { type: n, value: i } = t.data;
85
+ n === "UPDATE" && i ? y(i) : n === "QUERY" ? N && e.postMessage({ type: "RESPONSE", value: p.get() }) : n === "RESPONSE" && i && (y(i), L());
86
+ }, p.set = (t) => {
87
+ y(t), e.postMessage({ type: "UPDATE", value: t });
88
+ }, e.postMessage({ type: "QUERY" }), setTimeout(() => {
89
+ L();
90
+ }, 150);
91
+ } else
92
+ L();
93
+ const H = () => p.get(), Q = (e) => {
94
+ p.set({ ...H(), ...e });
95
+ }, We = J(
96
+ p,
97
+ (e) => e.insightsBaseUrl !== null && e.identity !== null
98
+ ), me = 10, he = 2e3, W = 500, h = b([]), I = b(0), be = J(h, (e) => e.length ?? 0), T = b(null), Je = () => {
99
+ let e = !1;
100
+ return p.subscribe((t) => {
101
+ if (t.isIdentified && !e) {
102
+ e = !0;
103
+ const n = h.get();
104
+ n.length > 0 && C(n);
105
+ }
106
+ });
107
+ }, A = () => h.get(), ye = () => be.get(), C = (e) => {
108
+ const { workspace: t, debug: n, site_id: i, page_id: o } = U(), { isIdentified: r, mode: c, sessionId: u, deviceId: a, sessionToken: s, deviceToken: d, insightsBaseUrl: f } = H();
109
+ if (e.length === 0 || !r || !f || !t)
110
+ return;
111
+ const g = e.map(
112
+ (m) => ({
113
+ // Default values from stores
114
+ workspace_id: t,
115
+ site_id: i ?? void 0,
116
+ session_id: u ?? void 0,
117
+ device_id: a ?? void 0,
118
+ identifier: o ?? void 0,
119
+ hostname: window.location.hostname,
120
+ pathname: window.location.pathname,
121
+ page_title: document.title,
122
+ url: window.location.href,
123
+ tab_id: "",
124
+ tab_count: 0,
125
+ // Event values override defaults (includes tab_id, tab_count captured at track time)
126
+ ...m,
127
+ // Ensure required fields have values
128
+ event: m.event,
129
+ created_at: m.created_at,
130
+ resource: m.resource ?? "page",
131
+ properties: m.properties ?? {}
132
+ })
133
+ );
134
+ if (h.set([]), n) {
135
+ console.log("[@levo-so/insights] Debug mode enabled. Events batch:", g);
136
+ return;
137
+ }
138
+ const k = `${f}/v1/insights/event/bulk?workspace=${t}`, E = {
139
+ "Content-Type": "application/json",
140
+ ...fe({ deviceToken: d, sessionToken: s, mode: c })
141
+ };
142
+ c === "proxy" && navigator.sendBeacon && navigator.sendBeacon(k, JSON.stringify(g)) || fetch(k, {
143
+ method: "POST",
144
+ body: JSON.stringify(g),
145
+ headers: E,
146
+ keepalive: !0,
147
+ credentials: c === "proxy" ? "include" : "omit"
148
+ }).catch(() => {
149
+ });
150
+ }, ve = () => {
151
+ if (T.get()) return;
152
+ const e = setTimeout(() => {
153
+ C(A()), T.set(null);
154
+ }, he);
155
+ T.set(e);
156
+ }, G = () => {
157
+ const e = T.get();
158
+ e && (clearTimeout(e), T.set(null));
159
+ }, Ke = (e) => {
160
+ const { debug: t } = U(), n = I.get();
161
+ if (n >= W) {
162
+ t && console.warn(`[@levo-so/core] Event limit (${W}) reached for this page`);
163
+ return;
164
+ }
165
+ I.set(n + 1);
166
+ const i = h.get();
167
+ h.set([...i, e]);
168
+ const { isIdentified: o } = H();
169
+ ye() >= me ? (G(), C(A())) : o && ve();
170
+ }, Ge = () => I.set(0), Xe = () => {
171
+ h.set([]), I.set(0), G();
172
+ }, _ = B({
173
+ status: "active",
174
+ seconds: 0
175
+ }), Ze = (e) => {
176
+ if (typeof window > "u") return () => {
177
+ };
178
+ const i = de({
179
+ timeout: 6e4,
180
+ throttle: 1e3,
181
+ onIdle: (r) => {
182
+ _.set({ status: "idle", seconds: r });
183
+ },
184
+ onWakeUp: (r) => {
185
+ _.set({ status: "active", seconds: r });
186
+ }
187
+ }), o = _.listen((r) => {
188
+ r.seconds > 0 && (r.status === "idle" ? e.track("user.idle", {
189
+ seconds_active: r.seconds
190
+ }) : e.track("user.active", {
191
+ seconds_idle: r.seconds
192
+ }));
193
+ });
194
+ return () => {
195
+ o(), i.disable(), _.set({ status: "active", seconds: 0 });
196
+ };
197
+ }, we = () => _.get(), S = b(
198
+ "active"
199
+ /* ACTIVE */
200
+ ), Ee = () => typeof document > "u" ? "active" : document.visibilityState === "hidden" ? "hidden" : document.hasFocus() ? "active" : "passive", et = (e) => {
201
+ if (typeof window > "u") return () => {
202
+ };
203
+ let t = !1;
204
+ const n = (s) => {
205
+ if (t) return;
206
+ t = !0;
207
+ const d = we(), f = {};
208
+ d.seconds > 0 && (d.status === "idle" ? f.seconds_idle = d.seconds : f.seconds_active = d.seconds);
209
+ try {
210
+ e.track("page.bounce", {
211
+ resource: "page",
212
+ ...f,
213
+ ...s
214
+ }), C(A());
215
+ } catch (g) {
216
+ console.error("[insights] Failed to send bounce event", z(g));
217
+ }
218
+ }, i = () => {
219
+ const s = performance.getEntriesByType("navigation")?.[0];
220
+ let d = s?.type === "navigate";
221
+ if (!d)
222
+ try {
223
+ const f = document.activeElement;
224
+ f?.tagName === "A" && f.href && new URL(f.href).origin !== window.location.origin && (d = !0);
225
+ } catch (f) {
226
+ console.warn("[insights] Failed to parse link destination", z(f));
227
+ }
228
+ n({
229
+ is_navigating_away: d,
230
+ navigation_type: s?.type,
231
+ trigger: "beforeunload"
232
+ });
233
+ }, o = (s) => {
234
+ switch (s.type) {
235
+ case "pageshow":
236
+ case "resume":
237
+ case "blur":
238
+ case "focus":
239
+ case "visibilitychange":
240
+ S.set(Ee());
241
+ break;
242
+ case "freeze":
243
+ S.set(
244
+ "frozen"
245
+ /* FROZEN */
246
+ );
247
+ break;
248
+ case "pagehide":
249
+ S.set(
250
+ s.persisted ? "frozen" : "terminated"
251
+ /* TERMINATED */
252
+ );
253
+ break;
254
+ }
255
+ }, r = ["pageshow", "pagehide", "focus", "blur", "visibilitychange", "resume", "freeze"];
256
+ r.forEach((s) => {
257
+ window.addEventListener(s, o, { capture: !0 });
258
+ }), window.addEventListener("beforeunload", i);
259
+ const c = S.listen((s) => {
260
+ s === "frozen" || s === "terminated" ? n({
261
+ state: s,
262
+ trigger: "lifecycle"
263
+ }) : s === "hidden" && C(A());
264
+ });
265
+ let u = x.get().page_id;
266
+ const a = x.listen((s) => {
267
+ s.page_id !== u && (t = !1, u = s.page_id);
268
+ });
269
+ return () => {
270
+ r.forEach((s) => {
271
+ window.removeEventListener(s, o, { capture: !0 });
272
+ }), window.removeEventListener("beforeunload", i), c(), a(), t = !1, S.set(
273
+ "active"
274
+ /* ACTIVE */
275
+ );
276
+ };
277
+ }, R = (e) => Object.prototype.toString.call(e) == "[object String]", Se = (e, t) => e.indexOf(t) !== -1, w = (e) => e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""), X = (e) => e === void 0, _e = (e) => e === null, Z = (e) => X(e) || _e(e), ee = (e) => !!e && e?.nodeType === 3;
278
+ function Te(e) {
279
+ return !!e && e?.nodeType === 1;
280
+ }
281
+ function xe(e, t) {
282
+ return !!e && !!e?.tagName && e?.tagName.toLowerCase() === t.toLowerCase();
283
+ }
284
+ function Ce(e) {
285
+ return e ? w(e).split(/\s+/) : [];
286
+ }
287
+ function ke(e) {
288
+ let t = "";
289
+ switch (typeof e?.className) {
290
+ case "string":
291
+ t = e?.className;
292
+ break;
293
+ // TODO: when is this ever used?
294
+ case "object":
295
+ t = (e?.className && "baseVal" in e.className ? (e?.className).baseVal : null) || e?.getAttribute("class") || "";
296
+ break;
297
+ default:
298
+ t = "";
299
+ }
300
+ return Ce(t);
301
+ }
302
+ const l = (e, t = 255) => Z(e) ? "" : w(e).split(/(\s+)/).filter((n) => Y(n)).join("").replace(/[\r\n]/g, " ").replace(/[ ]+/g, " ").substring(0, t), te = ["span", "strong", "em", "i", "b", "mark", "code", "small", "sub", "sup", "label"], F = (e) => {
303
+ let t = "";
304
+ if (Ae(e) && e?.childNodes && e?.childNodes.length) {
305
+ for (const n of e.childNodes)
306
+ ee(n) && n.textContent && (t += l(n.textContent) ?? "");
307
+ if (!w(t)) {
308
+ const n = e.querySelectorAll(te.join(", "));
309
+ for (const i of n) {
310
+ const o = i.textContent;
311
+ o && (t += `${l(o)} `);
312
+ }
313
+ }
314
+ if (!w(t) && e instanceof HTMLElement) {
315
+ const n = e.innerText;
316
+ n && (t = l(n.substring(0, 500)));
317
+ }
318
+ }
319
+ return w(t);
320
+ }, Le = (e) => X(e.target) ? e?.srcElement || null : e?.target?.shadowRoot ? e?.composedPath()?.[0] || null : e?.target || null, Ne = (e, t) => {
321
+ if (!window || !e || xe(e, "html") || !Te(e))
322
+ return !1;
323
+ const n = window.getComputedStyle(e);
324
+ if (n && n.getPropertyValue("cursor") === "pointer" && t.type === "click")
325
+ return !0;
326
+ switch (e?.tagName.toLowerCase()) {
327
+ case "html":
328
+ return !1;
329
+ case "form":
330
+ return t.type === "submit";
331
+ case "input":
332
+ case "select":
333
+ case "textarea":
334
+ return ["change", "click"].includes(t.type);
335
+ default:
336
+ return t.type === "click";
337
+ }
338
+ }, Ie = /* @__PURE__ */ new Set([
339
+ "password",
340
+ "passwd",
341
+ "pwd",
342
+ "creditcard",
343
+ "cardnumber",
344
+ "ccnum",
345
+ "ccnumber",
346
+ "cvv",
347
+ "cvc",
348
+ "csc",
349
+ "securitycode",
350
+ "ssn",
351
+ "socialsecurity",
352
+ "routingnumber",
353
+ "accountnumber",
354
+ "bankaccount",
355
+ "secretkey",
356
+ "apikey",
357
+ "accesstoken"
358
+ ]);
359
+ function Ae(e) {
360
+ if (Se(ke(e), "ph-include"))
361
+ return !0;
362
+ const t = e.type || "";
363
+ if (R(t))
364
+ switch (t.toLowerCase()) {
365
+ case "hidden":
366
+ return !1;
367
+ case "password":
368
+ return !1;
369
+ }
370
+ const n = e.name || e?.id || "";
371
+ if (R(n)) {
372
+ const i = n.toLowerCase().replace(/[^a-z]/g, "");
373
+ if (Ie.has(i))
374
+ return !1;
375
+ }
376
+ return !0;
377
+ }
378
+ const ne = "(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})", Re = new RegExp(`^(?:${ne})$`), $e = new RegExp(ne), se = "\\d{3}-?\\d{2}-?\\d{4}", De = new RegExp(`^(${se})$`), Me = new RegExp(`(${se})`), Y = (e, t = !0) => !(Z(e) || R(e) && (e = w(e), (t ? Re : $e).test((e || "").replace(/[- ]/g, "")) || (t ? De : Me).test(e))), Oe = (e) => R(e) ? e.substring(0, 10) === "_ngcontent" || e.substring(0, 7) === "_nghost" : !1, Pe = (e) => {
379
+ let t = F(e);
380
+ return t = `${t} ${ie(e)}`.trim(), Y(t) ? t : "";
381
+ };
382
+ function ie(e) {
383
+ let t = "";
384
+ if (e?.childNodes.length)
385
+ for (const n of e.childNodes) {
386
+ const i = n.tagName?.toLowerCase();
387
+ if (n && i && te.includes(i))
388
+ try {
389
+ const o = F(n);
390
+ t = `${t} ${o}`.trim(), n?.childNodes.length && (t = `${t} ${ie(n)}`.trim());
391
+ } catch {
392
+ }
393
+ }
394
+ return t;
395
+ }
396
+ const Be = (e) => {
397
+ const t = e.tagName?.toLowerCase(), n = t === "a" || t === "button" ? Pe(e) : F(e);
398
+ if (n) return { text: n, source: "text" };
399
+ const i = e.getAttribute("aria-label");
400
+ if (i) return { text: l(i), source: "aria-label" };
401
+ const o = e.getAttribute("title");
402
+ if (o) return { text: l(o), source: "title" };
403
+ const r = e.getAttribute("data-track-label");
404
+ if (r) return { text: l(r), source: "data-track-label" };
405
+ const c = e.querySelector("img[alt], svg[aria-label]");
406
+ if (c) {
407
+ const a = c.getAttribute("alt") || c.getAttribute("aria-label");
408
+ if (a) return { text: l(a), source: "alt" };
409
+ }
410
+ const u = e.getAttribute("placeholder");
411
+ if (u) return { text: l(u), source: "placeholder" };
412
+ if (e instanceof HTMLElement && e.innerText) {
413
+ const a = e.innerText.substring(0, 500);
414
+ if (a.trim())
415
+ return { text: l(a), source: "innerText" };
416
+ }
417
+ return { text: "", source: "none" };
418
+ }, Ue = ["input", "textarea", "select"], He = (e) => {
419
+ const t = e?.tagName?.toLowerCase(), n = {
420
+ element: t
421
+ }, i = Be(e);
422
+ n.text = i.text, n.text_source = i.source;
423
+ const o = e.getAttribute("aria-label");
424
+ o && (n.aria_label = l(o));
425
+ const r = e.getAttribute("role");
426
+ r && (n.role = r);
427
+ const c = {};
428
+ for (const a of e.attributes)
429
+ (a.name.startsWith("data-track-") || a.name.startsWith("data-analytics-")) && (c[a.name] = l(a.value));
430
+ Object.keys(c).length && (n.tracking_data = c);
431
+ const u = Array.from(e.attributes).reduce(
432
+ (a, s) => {
433
+ if (Y(s.value) && !Oe(s.name)) {
434
+ if (s.name === "class" || s.name.startsWith("data-track-") || s.name.startsWith("data-analytics-"))
435
+ return a;
436
+ a[s.name] = typeof s?.value == "string" ? l(s.value) : s.value;
437
+ }
438
+ return a;
439
+ },
440
+ {}
441
+ );
442
+ if (n.attributes = u, Ue.includes(t) || e?.getAttribute("contenteditable") === "true") {
443
+ let a;
444
+ e.matches('input[type="checkbox"], input[type="radio"]') ? a = e.checked : e.matches("input, select, textarea") && !e.matches('input[type="password"]') && (a = e.value), a !== void 0 && (n.value = typeof a == "string" || a === null ? l(a, 1024) : a);
445
+ let s = "";
446
+ n?.attributes.id && (s = l(
447
+ document.querySelector(`label[for="${n?.attributes.id}"]`)?.textContent?.trim() || ""
448
+ ));
449
+ const d = e.closest("label");
450
+ s || (s = l(d?.textContent?.trim() || "")), s && (n.label = s);
451
+ }
452
+ return n;
453
+ }, tt = (e, t) => {
454
+ let n = Le(e);
455
+ ee(n) && (n = n.parentNode || null);
456
+ const i = ["copy", "cut"].includes(t);
457
+ if (n && (i || Ne(n, e))) {
458
+ const o = He(n);
459
+ if (i) {
460
+ const r = l(window?.getSelection()?.toString()), c = e?.type || "clipboard";
461
+ o.copy_text = r ?? "", o.copy_type = c ?? "";
462
+ }
463
+ return o;
464
+ }
465
+ return null;
466
+ }, P = b(null), nt = (e) => {
467
+ if (typeof window > "u") return () => {
468
+ };
469
+ let t = sessionStorage.getItem(v.current_tab);
470
+ const { allTabs: n } = p.get();
471
+ t && n.includes(t) && (t = null), t || (t = `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, sessionStorage.setItem(v.current_tab, t));
472
+ const i = t;
473
+ P.set(i), n.includes(i) || Q({ allTabs: [...n, i] });
474
+ const o = () => {
475
+ if (!i) return;
476
+ const { allTabs: r } = p.get();
477
+ if (r.includes(i)) {
478
+ const c = r.filter((u) => u !== i);
479
+ Q({ allTabs: c });
480
+ }
481
+ };
482
+ return window.addEventListener("beforeunload", o), () => {
483
+ window.removeEventListener("beforeunload", o), o(), P.set(null);
484
+ };
485
+ }, st = () => P.get(), $ = B({
486
+ depth: 0,
487
+ milestone: 0,
488
+ speedCategory: "read",
489
+ direction: "down"
490
+ }), it = (e) => {
491
+ if (typeof window > "u") return () => {
492
+ };
493
+ let t = {
494
+ // Use -1 to ensure first milestone (even 0) is tracked
495
+ prevScrollY: 0,
496
+ prevTime: Date.now()
497
+ };
498
+ const n = 5, i = 300, o = 250, r = () => {
499
+ const s = window.scrollY, d = document.documentElement.scrollHeight, f = window.innerHeight, g = d - f;
500
+ if (g <= 0) return;
501
+ const k = Math.round(s / g * 100), E = Math.max(0, Math.min(100, k)), m = Math.floor(E / n) * n, { prevScrollY: oe, prevTime: re } = t, j = Date.now(), q = (j - re) / 1e3, ae = Math.abs(s - oe), ce = q > 0 ? ae / q : 0, le = E > t.prevScrollY / g * 100 ? "down" : "up", ue = ce > i ? "scan" : "read";
502
+ $.set({
503
+ depth: E,
504
+ milestone: m,
505
+ speedCategory: ue,
506
+ direction: le
507
+ }), t = {
508
+ prevMilestone: m,
509
+ prevScrollY: s,
510
+ prevTime: j
511
+ };
512
+ }, c = K(r, o);
513
+ window.addEventListener("scroll", c);
514
+ let u = 0;
515
+ const a = $.listen((s) => {
516
+ s.milestone !== u && (u = s.milestone, e.track("page.scroll", {
517
+ depth: s.milestone,
518
+ speed_category: s.speedCategory,
519
+ direction: s.direction
520
+ }));
521
+ });
522
+ return typeof window.requestIdleCallback == "function" ? window.requestIdleCallback(() => r()) : setTimeout(r, 0), () => {
523
+ window.removeEventListener("scroll", c), c.cancel(), a(), $.set({
524
+ depth: 0,
525
+ milestone: 0,
526
+ speedCategory: "read",
527
+ direction: "down"
528
+ });
529
+ };
530
+ }, D = b(null), ot = (e) => {
531
+ if (typeof window > "u") return () => {
532
+ };
533
+ const n = K(() => {
534
+ const o = window.getSelection();
535
+ if (o && o.rangeCount > 0) {
536
+ const r = o.toString().trim();
537
+ r && D.set(r);
538
+ }
539
+ }, 1e3);
540
+ document.addEventListener("selectionchange", n);
541
+ const i = D.listen((o) => {
542
+ o && e.track("page.selection", {
543
+ text: l(o)
544
+ });
545
+ });
546
+ return () => {
547
+ document.removeEventListener("selectionchange", n), n.cancel(), i(), D.set(null);
548
+ };
549
+ };
550
+ export {
551
+ _ as $,
552
+ Ze as A,
553
+ et as B,
554
+ nt as C,
555
+ it as D,
556
+ ot as E,
557
+ x as a,
558
+ P as b,
559
+ h as c,
560
+ We as d,
561
+ I as e,
562
+ S as f,
563
+ $ as g,
564
+ D as h,
565
+ p as i,
566
+ be as j,
567
+ He as k,
568
+ tt as l,
569
+ U as m,
570
+ H as n,
571
+ st as o,
572
+ fe as p,
573
+ Ke as q,
574
+ Ge as r,
575
+ ze as s,
576
+ Ve as t,
577
+ Q as u,
578
+ Qe as v,
579
+ C as w,
580
+ A as x,
581
+ Xe as y,
582
+ Je as z
583
+ };
@@ -2,8 +2,9 @@ import { AnalyticsInstance } from 'analytics';
2
2
  /**
3
3
  * Scroll state interface.
4
4
  */
5
- interface IScrollState {
5
+ export interface IScrollState {
6
6
  depth: number;
7
+ milestone: number;
7
8
  speedCategory: "read" | "scan";
8
9
  direction: "down" | "up";
9
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levo-so/insights",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "author": "Levo Engineering <devs@theinternetfolks.com>",
5
5
  "description": "Levo analytics",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  "nanostores": "0.11.4"
22
22
  },
23
23
  "peerDependencies": {
24
- "@levo-so/core": "0.1.80"
24
+ "@levo-so/core": "0.1.82"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@playwright/test": "1.58.2",
@@ -30,7 +30,8 @@
30
30
  "vite": "7.2.4",
31
31
  "vite-plugin-dts": "4.1.0",
32
32
  "vite-plugin-externalize-deps": "0.10.0",
33
- "@levo/ts-config": "0.0.0"
33
+ "@levo/ts-config": "0.0.0",
34
+ "@levo-so/core": "0.1.82"
34
35
  },
35
36
  "main": "./dist/index.js",
36
37
  "module": "./dist/index.js",
@@ -50,6 +51,6 @@
50
51
  "check-types": "tsc --noEmit",
51
52
  "build": "vite build",
52
53
  "dev": "vite build --watch",
53
- "test:e2e": "playwright test"
54
+ "lib-dev": "vite"
54
55
  }
55
56
  }