@levo-so/insights 0.1.109 → 0.1.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,13 @@
1
1
  import { ILevoAudience } from '../types/analytics';
2
2
  /**
3
3
  * An event pending delivery in the queue.
4
- * Carries only the data captured at track time; store context (session, workspace, etc.)
5
- * is merged in at flush time by {@link flushEventQueue}.
4
+ *
5
+ * track() captures every per-event field at track time (host/page/tab context,
6
+ * resource, identifier, timestamp), so they are all required here — flush trusts
7
+ * the queued event and does not re-default them. The only fields absent are the
8
+ * session/config ones, which {@link flushEventQueue} merges in at send time.
6
9
  */
7
- type IQueuedEvent = Partial<ILevoAudience.CollectInput> & {
8
- /** The event name (e.g. "page.view", "element.click"). */
9
- event: string;
10
- /** ISO 8601 timestamp when the event was recorded. */
11
- created_at: string;
12
- };
10
+ type IQueuedEvent = Omit<ILevoAudience.CollectInput, "workspace_id" | "site_id" | "session_id" | "device_id">;
13
11
  declare const $eventQueue: import('nanostores').PreinitializedWritableAtom<IQueuedEvent[]> & object;
14
12
  declare const $lifetimePageCount: import('nanostores').PreinitializedWritableAtom<number> & object;
15
13
  declare const $totalEventCount: import('nanostores').ReadableAtom<number>;
@@ -18,7 +18,6 @@ interface ISession {
18
18
  deviceToken: string | null;
19
19
  /** Direct-mode JWT. Stored in localStorage; survives browser restarts. */
20
20
  sessionToken: string | null;
21
- allTabs: string[];
22
21
  mode: "proxy" | "direct";
23
22
  insightsBaseUrl: string | null;
24
23
  identity: IIdentity | null;
package/dist/stores.d.ts CHANGED
@@ -4,6 +4,6 @@ export { $isIdentityContextReady } from './stores/readiness';
4
4
  export { $session } from './stores/session';
5
5
  export { $activity } from './tracking/activity';
6
6
  export { $pageLifecycle } from './tracking/bounce';
7
- export { $currentTab } from './tracking/currentTab';
7
+ export { $currentTab, getTabCount } from './tracking/currentTab';
8
8
  export { $scrollBehavior } from './tracking/scrollBehavior';
9
9
  export { $selectedText } from './tracking/textSelection';
package/dist/stores.js CHANGED
@@ -1,2 +1,2 @@
1
- import { M as e, O as t, a as n, b as r, d as i, g as a, p as o, r as s, t as c, x as l, y as u } from "./textSelection-eSp_MmZH.js";
2
- export { o as $activity, e as $config, n as $currentTab, u as $eventQueue, a as $isIdentityContextReady, r as $lifetimePageCount, i as $pageLifecycle, s as $scrollBehavior, c as $selectedText, t as $session, l as $totalEventCount };
1
+ import { P as e, S as t, _ as n, a as r, b as i, f as a, k as o, m as s, r as c, s as l, t as u, x as d } from "./textSelection-Cwq6lqKg.js";
2
+ export { s as $activity, e as $config, r as $currentTab, i as $eventQueue, n as $isIdentityContextReady, d as $lifetimePageCount, a as $pageLifecycle, c as $scrollBehavior, u as $selectedText, o as $session, t as $totalEventCount, l as getTabCount };
@@ -19,12 +19,12 @@ var o = n({
19
19
  identify: "lock:lv_aud_identify",
20
20
  open_tabs: "lv_insights_ot",
21
21
  current_tab: "lv_insights_ct",
22
- all_tabs: "lv_insights_at",
23
22
  device_token: "lv_aud_device",
24
23
  session_token: "lv_aud_session",
25
24
  mode: "lv_aud_mode",
26
25
  session: "lv_insights_session",
27
- session_flags: "lv_insights_sf"
26
+ session_flags: "lv_insights_sf",
27
+ attribution: "lv_insights_attr"
28
28
  }, f = () => {
29
29
  if (typeof window > "u" || !("localStorage" in window)) return !1;
30
30
  try {
@@ -40,7 +40,6 @@ var o = n({
40
40
  deviceId: null,
41
41
  deviceToken: null,
42
42
  sessionToken: null,
43
- allTabs: [],
44
43
  mode: "proxy",
45
44
  insightsBaseUrl: null,
46
45
  identity: null
@@ -106,67 +105,56 @@ if (f()) {
106
105
  y();
107
106
  }, 150);
108
107
  } else y();
109
- var b = () => g.get(), x = (e) => {
108
+ var b = () => g.get(), ee = (e) => {
110
109
  g.set({
111
110
  ...b(),
112
111
  ...e
113
112
  });
114
- }, ee = 10, te = 2e3, S = 500, C = e([]), w = e(0), T = t(C, (e) => e.length ?? 0), E = e(null), D = () => C.get(), ne = () => T.get(), O = (e) => {
115
- let { workspace: t, debug: n, site_id: r, page_id: i } = s(), { isIdentified: a, mode: o, sessionId: c, deviceId: l, sessionToken: d, deviceToken: f, insightsBaseUrl: p } = b();
116
- if (e.length === 0 || !a || !p || !t) return;
117
- let m = e.map((e) => ({
113
+ }, te = 10, ne = 2e3, x = 1e3, S = e([]), C = e(0), w = t(S, (e) => e.length ?? 0), T = e(null), E = () => S.get(), re = () => w.get(), D = (e) => {
114
+ let { workspace: t, debug: n, site_id: r } = s(), { isIdentified: i, mode: a, sessionId: o, deviceId: c, sessionToken: l, deviceToken: d, insightsBaseUrl: f } = b();
115
+ if (e.length === 0 || !i || !f || !t) return;
116
+ let p = e.map((e) => ({
118
117
  workspace_id: t,
119
118
  site_id: r ?? void 0,
120
- session_id: c ?? void 0,
121
- device_id: l ?? void 0,
122
- identifier: i ?? void 0,
123
- hostname: window.location.hostname,
124
- pathname: window.location.pathname,
125
- page_title: document.title,
126
- url: window.location.href,
127
- tab_id: "",
128
- tab_count: 0,
129
- ...e,
130
- event: e.event,
131
- created_at: e.created_at,
132
- resource: e.resource ?? "page",
133
- properties: e.properties ?? {}
119
+ session_id: o ?? void 0,
120
+ device_id: c ?? void 0,
121
+ ...e
134
122
  }));
135
- if (C.set([]), n) {
136
- console.log("[@levo-so/insights] Debug mode enabled. Events batch:", m);
123
+ if (S.set([]), n) {
124
+ console.log("[@levo-so/insights] Debug mode enabled. Events batch:", p);
137
125
  return;
138
126
  }
139
- let h = new URLSearchParams({ workspace: t });
140
- o === "direct" && (f && h.set("device_token", f), d && h.set("session_token", d));
141
- let g = `${p}/v1/insights/event/bulk?${h.toString()}`;
142
- if (navigator.sendBeacon && navigator.sendBeacon(g, JSON.stringify(m))) return;
143
- let _ = {
127
+ let m = new URLSearchParams({ workspace: t });
128
+ a === "direct" && (d && m.set("device_token", d), l && m.set("session_token", l));
129
+ let h = `${f}/v1/insights/event/bulk?${m.toString()}`;
130
+ if (navigator.sendBeacon && navigator.sendBeacon(h, JSON.stringify(p))) return;
131
+ let g = {
144
132
  "Content-Type": "application/json",
145
133
  ...u({
146
- deviceToken: f,
147
- sessionToken: d,
148
- mode: o
134
+ deviceToken: d,
135
+ sessionToken: l,
136
+ mode: a
149
137
  })
150
138
  };
151
- fetch(g, {
139
+ fetch(h, {
152
140
  method: "POST",
153
- body: JSON.stringify(m),
154
- headers: _,
141
+ body: JSON.stringify(p),
142
+ headers: g,
155
143
  keepalive: !0,
156
- credentials: o === "proxy" ? "include" : "omit"
144
+ credentials: a === "proxy" ? "include" : "omit"
157
145
  }).catch((e) => {
158
146
  console.warn("[@levo-so/insights] Event flush failed, batch lost:", e);
159
147
  });
160
- }, k = () => {
161
- if (E.get()) return;
148
+ }, O = () => {
149
+ if (T.get()) return;
162
150
  let e = setTimeout(() => {
163
- O(D()), E.set(null);
164
- }, te);
165
- E.set(e);
166
- }, A = () => {
167
- let e = E.get();
168
- e && (clearTimeout(e), E.set(null));
169
- }, re = (e) => {
151
+ D(E()), T.set(null);
152
+ }, ne);
153
+ T.set(e);
154
+ }, k = () => {
155
+ let e = T.get();
156
+ e && (clearTimeout(e), T.set(null));
157
+ }, ie = (e) => {
170
158
  let { debug: t, insightsUrl: n } = s();
171
159
  if (!n) {
172
160
  t && console.warn("[@levo-so/insights] Event dropped — insightsUrl not configured.", {
@@ -175,70 +163,70 @@ var b = () => g.get(), x = (e) => {
175
163
  });
176
164
  return;
177
165
  }
178
- let r = w.get();
179
- if (r >= S) {
180
- t && console.warn(`[@levo-so/core] Event limit (${S}) reached for this page`);
166
+ let r = C.get();
167
+ if (r >= x) {
168
+ t && console.warn(`[@levo-so/core] Event limit (${x}) reached for this page`);
181
169
  return;
182
170
  }
183
- w.set(r + 1);
184
- let i = C.get();
185
- C.set([...i, e]);
171
+ C.set(r + 1);
172
+ let i = S.get();
173
+ S.set([...i, e]);
186
174
  let { isIdentified: a } = b();
187
- ne() >= ee ? (A(), O(D())) : a && k();
188
- }, ie = () => w.set(0), j = g.get().isIdentified;
175
+ re() >= te ? (k(), D(E())) : a && O();
176
+ }, ae = () => C.set(0), oe = g.get().isIdentified;
189
177
  g.subscribe((e) => {
190
- e.isIdentified && !j && C.get().length > 0 && (e.insightsBaseUrl ? O(D()) : k()), j = e.isIdentified;
178
+ e.isIdentified && !oe && S.get().length > 0 && (e.insightsBaseUrl ? D(E()) : O()), oe = e.isIdentified;
191
179
  });
192
- var M = g.get().insightsBaseUrl;
180
+ var se = g.get().insightsBaseUrl;
193
181
  g.subscribe((e) => {
194
- e.insightsBaseUrl && !M && e.isIdentified && C.get().length > 0 && O(D()), M = e.insightsBaseUrl;
182
+ e.insightsBaseUrl && !se && e.isIdentified && S.get().length > 0 && D(E()), se = e.insightsBaseUrl;
195
183
  });
196
- var ae = () => {
197
- C.set([]), w.set(0), A();
198
- }, N = e(!1), oe = e(!1), se = t([v, N], (e, t) => e && t), ce = t([se, oe], (e, t) => e && t), P = n({
184
+ var ce = () => {
185
+ S.set([]), C.set(0), k();
186
+ }, le = e(!1), A = e(!1), j = t([v, le], (e, t) => e && t), ue = t([j, A], (e, t) => e && t), M = n({
199
187
  status: "active",
200
188
  seconds: 0
201
- }), le = (e) => {
189
+ }), de = (e) => {
202
190
  if (typeof window > "u") return () => {};
203
191
  let t = r({
204
192
  timeout: 6e4,
205
193
  throttle: 1e3,
206
194
  onIdle: (e) => {
207
- P.set({
195
+ M.set({
208
196
  status: "idle",
209
197
  seconds: e
210
198
  });
211
199
  },
212
200
  onWakeUp: (e) => {
213
- P.set({
201
+ M.set({
214
202
  status: "active",
215
203
  seconds: e
216
204
  });
217
205
  }
218
- }), n = P.listen((t) => {
206
+ }), n = M.listen((t) => {
219
207
  t.seconds > 0 && (t.status === "idle" ? e.track("user.idle", { seconds_active: t.seconds }) : e.track("user.active", { seconds_idle: t.seconds }));
220
208
  });
221
209
  return () => {
222
- n(), t.disable(), P.set({
210
+ n(), t.disable(), M.set({
223
211
  status: "active",
224
212
  seconds: 0
225
213
  });
226
214
  };
227
- }, ue = () => P.get(), F = /* @__PURE__ */ function(e) {
215
+ }, fe = () => M.get(), N = /* @__PURE__ */ function(e) {
228
216
  return e.ACTIVE = "active", e.PASSIVE = "passive", e.HIDDEN = "hidden", e.FROZEN = "frozen", e.TERMINATED = "terminated", e;
229
- }(F || {}), I = e(F.ACTIVE), de = () => typeof document > "u" ? F.ACTIVE : document.visibilityState === "hidden" ? F.HIDDEN : document.hasFocus() ? F.ACTIVE : F.PASSIVE, fe = (e) => {
217
+ }(N || {}), P = e(N.ACTIVE), pe = () => typeof document > "u" ? N.ACTIVE : document.visibilityState === "hidden" ? N.HIDDEN : document.hasFocus() ? N.ACTIVE : N.PASSIVE, me = (e) => {
230
218
  if (typeof window > "u") return () => {};
231
219
  let t = !1, n = (n) => {
232
220
  if (t) return;
233
221
  t = !0;
234
- let r = ue(), a = {};
235
- r.seconds > 0 && (r.status === "idle" ? a.seconds_idle = r.seconds : a.seconds_active = r.seconds);
222
+ let r = fe(), a = {};
223
+ r.seconds > 0 && (r.status === "idle" ? a.seconds_active = r.seconds : a.seconds_idle = r.seconds);
236
224
  try {
237
225
  e.track("page.bounce", {
238
226
  resource: "page",
239
227
  ...a,
240
228
  ...n
241
- }, {}, () => O(D()));
229
+ }, {}, () => D(E()));
242
230
  } catch (e) {
243
231
  console.error("[insights] Failed to send bounce event", i(e));
244
232
  }
@@ -262,13 +250,13 @@ var ae = () => {
262
250
  case "blur":
263
251
  case "focus":
264
252
  case "visibilitychange":
265
- I.set(de());
253
+ P.set(pe());
266
254
  break;
267
255
  case "freeze":
268
- I.set(F.FROZEN);
256
+ P.set(N.FROZEN);
269
257
  break;
270
258
  case "pagehide":
271
- I.set(e.persisted ? F.FROZEN : F.TERMINATED);
259
+ P.set(e.persisted ? N.FROZEN : N.TERMINATED);
272
260
  break;
273
261
  }
274
262
  }, s = [
@@ -283,32 +271,32 @@ var ae = () => {
283
271
  s.forEach((e) => {
284
272
  window.addEventListener(e, a, { capture: !0 });
285
273
  }), window.addEventListener("beforeunload", r);
286
- let c = I.listen((e) => {
287
- e === F.FROZEN || e === F.TERMINATED ? n({
274
+ let c = P.listen((e) => {
275
+ e === N.FROZEN || e === N.TERMINATED ? n({
288
276
  state: e,
289
277
  trigger: "lifecycle"
290
- }) : e === F.HIDDEN && O(D());
278
+ }) : e === N.HIDDEN && D(E());
291
279
  }), l = o.get().page_id, u = o.listen((e) => {
292
280
  e.page_id !== l && (t = !1, l = e.page_id);
293
- }), d = w.listen((e) => {
281
+ }), d = C.listen((e) => {
294
282
  e === 0 && (t = !1);
295
283
  });
296
284
  return () => {
297
285
  s.forEach((e) => {
298
286
  window.removeEventListener(e, a, { capture: !0 });
299
- }), window.removeEventListener("beforeunload", r), c(), u(), d(), t = !1, I.set(F.ACTIVE);
287
+ }), window.removeEventListener("beforeunload", r), c(), u(), d(), t = !1, P.set(N.ACTIVE);
300
288
  };
301
- }, L = (e) => Object.prototype.toString.call(e) == "[object String]", pe = (e, t) => e.indexOf(t) !== -1, R = (e) => e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""), z = (e) => e === void 0, me = (e) => e === null, B = (e) => z(e) || me(e), V = (e) => !!e && e?.nodeType === 3;
302
- function he(e) {
289
+ }, F = (e) => Object.prototype.toString.call(e) == "[object String]", he = (e, t) => e.indexOf(t) !== -1, I = (e) => e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""), L = (e) => e === void 0, ge = (e) => e === null, R = (e) => L(e) || ge(e), z = (e) => !!e && e?.nodeType === 3;
290
+ function _e(e) {
303
291
  return !!e && e?.nodeType === 1;
304
292
  }
305
- function ge(e, t) {
293
+ function ve(e, t) {
306
294
  return !!e && !!e?.tagName && e?.tagName.toLowerCase() === t.toLowerCase();
307
295
  }
308
- function _e(e) {
309
- return e ? R(e).split(/\s+/) : [];
296
+ function ye(e) {
297
+ return e ? I(e).split(/\s+/) : [];
310
298
  }
311
- function ve(e) {
299
+ function be(e) {
312
300
  let t = "";
313
301
  switch (typeof e?.className) {
314
302
  case "string":
@@ -319,9 +307,9 @@ function ve(e) {
319
307
  break;
320
308
  default: t = "";
321
309
  }
322
- return _e(t);
310
+ return ye(t);
323
311
  }
324
- var H = (e, t = 255) => B(e) ? "" : R(e).split(/(\s+)/).filter((e) => J(e)).join("").replace(/[\r\n]/g, " ").replace(/[ ]+/g, " ").substring(0, t), U = [
312
+ var B = (e, t = 255) => R(e) ? "" : I(e).split(/(\s+)/).filter((e) => K(e)).join("").replace(/[\r\n]/g, " ").replace(/[ ]+/g, " ").substring(0, t), V = [
325
313
  "span",
326
314
  "strong",
327
315
  "em",
@@ -333,25 +321,25 @@ var H = (e, t = 255) => B(e) ? "" : R(e).split(/(\s+)/).filter((e) => J(e)).join
333
321
  "sub",
334
322
  "sup",
335
323
  "label"
336
- ], W = (e) => {
324
+ ], H = (e) => {
337
325
  let t = "";
338
- if (xe(e) && e?.childNodes && e?.childNodes.length) {
339
- for (let n of e.childNodes) V(n) && n.textContent && (t += H(n.textContent) ?? "");
340
- if (!R(t)) {
341
- let n = e.querySelectorAll(U.join(", "));
326
+ if (Ce(e) && e?.childNodes && e?.childNodes.length) {
327
+ for (let n of e.childNodes) z(n) && n.textContent && (t += B(n.textContent) ?? "");
328
+ if (!I(t)) {
329
+ let n = e.querySelectorAll(V.join(", "));
342
330
  for (let e of n) {
343
331
  let n = e.textContent;
344
- n && (t += `${H(n)} `);
332
+ n && (t += `${B(n)} `);
345
333
  }
346
334
  }
347
- if (!R(t) && e instanceof HTMLElement) {
335
+ if (!I(t) && e instanceof HTMLElement) {
348
336
  let n = e.innerText;
349
- n && (t = H(n.substring(0, 500)));
337
+ n && (t = B(n.substring(0, 500)));
350
338
  }
351
339
  }
352
- return R(t);
353
- }, G = (e) => z(e.target) ? e?.srcElement || null : e?.composedPath?.()[0] || e?.target || null, ye = (e, t) => {
354
- if (!window || !e || ge(e, "html") || !he(e)) return !1;
340
+ return I(t);
341
+ }, U = (e) => L(e.target) ? e?.srcElement || null : e?.composedPath?.()[0] || e?.target || null, xe = (e, t) => {
342
+ if (!window || !e || ve(e, "html") || !_e(e)) return !1;
355
343
  let n = window.getComputedStyle(e);
356
344
  if (n && n.getPropertyValue("cursor") === "pointer" && t.type === "click") return !0;
357
345
  switch (e?.tagName.toLowerCase()) {
@@ -362,7 +350,7 @@ var H = (e, t = 255) => B(e) ? "" : R(e).split(/(\s+)/).filter((e) => J(e)).join
362
350
  case "textarea": return ["change", "click"].includes(t.type);
363
351
  default: return t.type === "click";
364
352
  }
365
- }, be = new Set([
353
+ }, Se = new Set([
366
354
  "password",
367
355
  "passwd",
368
356
  "pwd",
@@ -383,73 +371,73 @@ var H = (e, t = 255) => B(e) ? "" : R(e).split(/(\s+)/).filter((e) => J(e)).join
383
371
  "apikey",
384
372
  "accesstoken"
385
373
  ]);
386
- function xe(e) {
387
- if (pe(ve(e), "ph-include")) return !0;
374
+ function Ce(e) {
375
+ if (he(be(e), "ph-include")) return !0;
388
376
  let t = e.type || "";
389
- if (L(t)) switch (t.toLowerCase()) {
377
+ if (F(t)) switch (t.toLowerCase()) {
390
378
  case "hidden": return !1;
391
379
  case "password": return !1;
392
380
  }
393
381
  let n = e.name || e?.id || "";
394
- if (L(n)) {
382
+ if (F(n)) {
395
383
  let e = n.toLowerCase().replace(/[^a-z]/g, "");
396
- if (be.has(e)) return !1;
384
+ if (Se.has(e)) return !1;
397
385
  }
398
386
  return !0;
399
387
  }
400
- var K = "(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})", Se = RegExp(`^(?:${K})$`), Ce = new RegExp(K), q = "\\d{3}-?\\d{2}-?\\d{4}", we = RegExp(`^(${q})$`), Te = RegExp(`(${q})`), J = (e, t = !0) => !(B(e) || L(e) && (e = R(e), (t ? Se : Ce).test((e || "").replace(/[- ]/g, "")) || (t ? we : Te).test(e))), Ee = (e) => L(e) ? e.substring(0, 10) === "_ngcontent" || e.substring(0, 7) === "_nghost" : !1, De = (e) => {
401
- let t = W(e);
402
- return t = `${t} ${Y(e)}`.trim(), J(t) ? t : "";
388
+ var W = "(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})", we = RegExp(`^(?:${W})$`), Te = new RegExp(W), G = "\\d{3}-?\\d{2}-?\\d{4}", Ee = RegExp(`^(${G})$`), De = RegExp(`(${G})`), K = (e, t = !0) => !(R(e) || F(e) && (e = I(e), (t ? we : Te).test((e || "").replace(/[- ]/g, "")) || (t ? Ee : De).test(e))), Oe = (e) => F(e) ? e.substring(0, 10) === "_ngcontent" || e.substring(0, 7) === "_nghost" : !1, ke = (e) => {
389
+ let t = H(e);
390
+ return t = `${t} ${q(e)}`.trim(), K(t) ? t : "";
403
391
  };
404
- function Y(e) {
392
+ function q(e) {
405
393
  let t = "";
406
394
  if (e?.childNodes.length) for (let n of e.childNodes) {
407
395
  let e = n.tagName?.toLowerCase();
408
- if (n && e && U.includes(e)) try {
409
- let e = W(n);
410
- t = `${t} ${e}`.trim(), n?.childNodes.length && (t = `${t} ${Y(n)}`.trim());
396
+ if (n && e && V.includes(e)) try {
397
+ let e = H(n);
398
+ t = `${t} ${e}`.trim(), n?.childNodes.length && (t = `${t} ${q(n)}`.trim());
411
399
  } catch {}
412
400
  }
413
401
  return t;
414
402
  }
415
- var Oe = (e) => {
416
- let t = e.tagName?.toLowerCase(), n = t === "a" || t === "button" ? De(e) : W(e);
403
+ var Ae = (e) => {
404
+ let t = e.tagName?.toLowerCase(), n = t === "a" || t === "button" ? ke(e) : H(e);
417
405
  if (n) return {
418
406
  text: n,
419
407
  source: "text"
420
408
  };
421
409
  let r = e.getAttribute("aria-label");
422
410
  if (r) return {
423
- text: H(r),
411
+ text: B(r),
424
412
  source: "aria-label"
425
413
  };
426
414
  let i = e.getAttribute("title");
427
415
  if (i) return {
428
- text: H(i),
416
+ text: B(i),
429
417
  source: "title"
430
418
  };
431
419
  let a = e.getAttribute("data-track-label");
432
420
  if (a) return {
433
- text: H(a),
421
+ text: B(a),
434
422
  source: "data-track-label"
435
423
  };
436
424
  let o = e.querySelector("img[alt], svg[aria-label]");
437
425
  if (o) {
438
426
  let e = o.getAttribute("alt") || o.getAttribute("aria-label");
439
427
  if (e) return {
440
- text: H(e),
428
+ text: B(e),
441
429
  source: "alt"
442
430
  };
443
431
  }
444
432
  let s = e.getAttribute("placeholder");
445
433
  if (s) return {
446
- text: H(s),
434
+ text: B(s),
447
435
  source: "placeholder"
448
436
  };
449
437
  if (e instanceof HTMLElement && e.innerText) {
450
438
  let t = e.innerText.substring(0, 500);
451
439
  if (t.trim()) return {
452
- text: H(t),
440
+ text: B(t),
453
441
  source: "innerText"
454
442
  };
455
443
  }
@@ -457,76 +445,108 @@ var Oe = (e) => {
457
445
  text: "",
458
446
  source: "none"
459
447
  };
460
- }, ke = [
448
+ }, je = [
461
449
  "input",
462
450
  "textarea",
463
451
  "select"
464
- ], X = (e) => {
465
- let t = e?.tagName?.toLowerCase(), n = { element: t }, r = Oe(e);
452
+ ], J = (e) => {
453
+ let t = e?.tagName?.toLowerCase(), n = { element: t }, r = Ae(e);
466
454
  n.text = r.text, n.text_source = r.source;
467
455
  let i = e.getAttribute("aria-label");
468
- i && (n.aria_label = H(i));
456
+ i && (n.aria_label = B(i));
469
457
  let a = e.getAttribute("role");
470
458
  a && (n.role = a);
471
459
  let o = {};
472
- for (let t of e.attributes) (t.name.startsWith("data-track-") || t.name.startsWith("data-analytics-")) && (o[t.name] = H(t.value));
460
+ for (let t of e.attributes) (t.name.startsWith("data-track-") || t.name.startsWith("data-analytics-")) && (o[t.name] = B(t.value));
473
461
  if (Object.keys(o).length && (n.tracking_data = o), n.attributes = Array.from(e.attributes).reduce((e, t) => {
474
- if (J(t.value) && !Ee(t.name)) {
462
+ if (K(t.value) && !Oe(t.name)) {
475
463
  if (t.name === "class" || t.name.startsWith("data-track-") || t.name.startsWith("data-analytics-")) return e;
476
- e[t.name] = typeof t?.value == "string" ? H(t.value) : t.value;
464
+ e[t.name] = typeof t?.value == "string" ? B(t.value) : t.value;
477
465
  }
478
466
  return e;
479
- }, {}), ke.includes(t) || e?.getAttribute("contenteditable") === "true") {
467
+ }, {}), je.includes(t) || e?.getAttribute("contenteditable") === "true") {
480
468
  let t;
481
- e.matches("input[type=\"checkbox\"], input[type=\"radio\"]") ? t = e.checked : e.matches("input, select, textarea") && !e.matches("input[type=\"password\"]") && (t = e.value), t !== void 0 && (n.value = typeof t == "string" || t === null ? H(t, 1024) : t);
469
+ e.matches("input[type=\"checkbox\"], input[type=\"radio\"]") ? t = e.checked : e.matches("input, select, textarea") && !e.matches("input[type=\"password\"]") && (t = e.value), t !== void 0 && (n.value = typeof t == "string" || t === null ? B(t, 1024) : t);
482
470
  let r = "";
483
- n?.attributes.id && (r = H(document.querySelector(`label[for="${n?.attributes.id}"]`)?.textContent?.trim() || ""));
471
+ n?.attributes.id && (r = B(document.querySelector(`label[for="${n?.attributes.id}"]`)?.textContent?.trim() || ""));
484
472
  let i = e.closest("label");
485
- r ||= H(i?.textContent?.trim() || ""), r && (n.label = r);
473
+ r ||= B(i?.textContent?.trim() || ""), r && (n.label = r);
486
474
  }
487
475
  return n;
488
- }, Ae = (e, t) => {
489
- let n = G(e);
490
- V(n) && (n = n.parentNode || null);
476
+ }, Me = (e, t) => {
477
+ let n = U(e);
478
+ z(n) && (n = n.parentNode || null);
491
479
  let r = ["copy", "cut"].includes(t);
492
- if (n && (r || ye(n, e))) {
493
- let t = X(n);
480
+ if (n && (r || xe(n, e))) {
481
+ let t = J(n);
494
482
  if (r) {
495
- let n = H(window?.getSelection()?.toString()), r = e?.type || "clipboard";
483
+ let n = B(window?.getSelection()?.toString()), r = e?.type || "clipboard";
496
484
  t.copy_text = n ?? "", t.copy_type = r ?? "";
497
485
  }
498
486
  return t;
499
487
  }
500
488
  return null;
501
- }, Z = e(null), je = (e) => {
489
+ }, Y = e(null), Ne = 3e3, Pe = 1e4, Fe = `${d.open_tabs}:`, X = (e) => `${Fe}${e}`, Ie = (e) => {
490
+ try {
491
+ localStorage.setItem(X(e), String(Date.now()));
492
+ } catch {}
493
+ }, Z = (e) => {
494
+ try {
495
+ localStorage.removeItem(X(e));
496
+ } catch {}
497
+ }, Le = (e) => {
498
+ try {
499
+ let t = Number(localStorage.getItem(X(e)));
500
+ return Number.isFinite(t) && Date.now() - t <= Pe;
501
+ } catch {
502
+ return !1;
503
+ }
504
+ }, Re = () => {
505
+ try {
506
+ let e = Date.now(), t = 0, n = [];
507
+ for (let r = 0; r < localStorage.length; r++) {
508
+ let i = localStorage.key(r);
509
+ if (!i || !i.startsWith(Fe)) continue;
510
+ let a = Number(localStorage.getItem(i));
511
+ Number.isFinite(a) && e - a <= Pe ? t += 1 : n.push(i);
512
+ }
513
+ for (let e of n) try {
514
+ localStorage.removeItem(e);
515
+ } catch {}
516
+ return Math.max(1, t);
517
+ } catch {
518
+ return 1;
519
+ }
520
+ }, ze = 1, Be = () => {
521
+ ze = Re();
522
+ }, Ve = (e) => {
502
523
  if (typeof window > "u") return () => {};
503
524
  let t = null;
504
525
  try {
505
526
  t = sessionStorage.getItem(d.current_tab);
506
527
  } catch {}
507
- let { allTabs: n } = g.get();
508
- if (t && n.includes(t) && (t = null), !t) {
528
+ if (t && Le(t) && (t = null), !t) {
509
529
  t = `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
510
530
  try {
511
531
  sessionStorage.setItem(d.current_tab, t);
512
532
  } catch {}
513
533
  }
514
- let r = t;
515
- Z.set(r), n.includes(r) || x({ allTabs: [...n, r] });
516
- let i = () => {
517
- if (!r) return;
518
- let { allTabs: e } = g.get();
519
- e.includes(r) && x({ allTabs: e.filter((e) => e !== r) });
534
+ let n = t;
535
+ Y.set(n), Ie(n), Be();
536
+ let r = window.setInterval(() => {
537
+ Ie(n), Be();
538
+ }, Ne), i = (e) => {
539
+ e.persisted || Z(n);
520
540
  };
521
- return window.addEventListener("beforeunload", i), () => {
522
- window.removeEventListener("beforeunload", i), i(), Z.set(null);
541
+ return window.addEventListener("beforeunload", i), window.addEventListener("pagehide", i), () => {
542
+ window.clearInterval(r), window.removeEventListener("beforeunload", i), window.removeEventListener("pagehide", i), Z(n), Y.set(null);
523
543
  };
524
- }, Me = () => Z.get(), Q = n({
544
+ }, He = () => Y.get(), Ue = () => ze, Q = n({
525
545
  depth: 0,
526
546
  milestone: 0,
527
547
  speedCategory: "read",
528
548
  direction: "down"
529
- }), Ne = (e) => {
549
+ }), We = (e) => {
530
550
  if (typeof window > "u") return () => {};
531
551
  let t = {
532
552
  prevMilestone: -1,
@@ -563,7 +583,7 @@ var Oe = (e) => {
563
583
  direction: "down"
564
584
  });
565
585
  };
566
- }, $ = e(null), Pe = (e) => {
586
+ }, $ = e(null), Ge = (e) => {
567
587
  if (typeof window > "u") return () => {};
568
588
  let t = a(() => {
569
589
  let e = window.getSelection();
@@ -574,11 +594,11 @@ var Oe = (e) => {
574
594
  }, 1e3);
575
595
  document.addEventListener("selectionchange", t);
576
596
  let n = $.listen((t) => {
577
- t && e.track("page.selection", { text: H(t) });
597
+ t && e.track("page.selection", { text: B(t) });
578
598
  });
579
599
  return () => {
580
600
  document.removeEventListener("selectionchange", t), t.cancel(), n(), $.set(null);
581
601
  };
582
602
  };
583
603
  //#endregion
584
- export { x as A, O as C, v as D, ie as E, l as F, o as M, s as N, g as O, c as P, ae as S, re as T, N as _, Z as a, w as b, X as c, I as d, fe as f, se as g, oe as h, Ne as i, u as j, b as k, G as l, le as m, Pe as n, Me as o, P as p, Q as r, je as s, $ as t, Ae as u, ce as v, D as w, T as x, C as y };
604
+ export { b as A, ce as C, ae as D, ie as E, s as F, c as I, l as L, d as M, u as N, v as O, o as P, w as S, E as T, j as _, Y as a, S as b, Ve as c, Me as d, P as f, A as g, de as h, We as i, ee as j, g as k, J as l, M as m, Ge as n, He as o, me as p, Q as r, Ue as s, $ as t, U as u, le as v, D as w, C as x, ue as y };
@@ -6,11 +6,12 @@ import { AnalyticsInstance } from 'analytics';
6
6
  declare const $currentTab: import('nanostores').PreinitializedWritableAtom<string | null> & object;
7
7
  /**
8
8
  * Initialize current tab tracking.
9
- * Assumes the session store is already synchronized (isReady).
10
9
  *
11
10
  * @param _instance - Analytics instance
12
11
  * @returns Cleanup function
13
12
  */
14
13
  declare const initCurrentTabTracker: (_instance: AnalyticsInstance) => () => void;
15
14
  declare const getCurrentTab: () => string | null;
16
- export { $currentTab, initCurrentTabTracker, getCurrentTab };
15
+ /** Number of tabs currently open for this site (live heartbeats, cached snapshot). */
16
+ declare const getTabCount: () => number;
17
+ export { $currentTab, initCurrentTabTracker, getCurrentTab, getTabCount };