@levo-so/insights 0.1.85 → 0.1.105

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.
@@ -7,6 +7,11 @@ interface IInsightsConfig {
7
7
  page_id: string | null;
8
8
  site_id: string | null;
9
9
  debug: boolean;
10
+ /**
11
+ * Reverse-proxy URL passed at init. Null when the consumer opts out of
12
+ * network tracking — gates queueEvent and the network-readiness pipeline.
13
+ */
14
+ insightsUrl: string | null;
10
15
  }
11
16
  declare const $config: import('nanostores').PreinitializedMapStore<IInsightsConfig> & object;
12
17
  declare const getConfig: () => IInsightsConfig;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Identity pipeline gate — flipped when getUserProperties + getReferrer
3
+ * have settled and the result has been written to $session.identity.
4
+ * Independent of network configuration.
5
+ */
6
+ declare const $isIdentityReady: import('nanostores').PreinitializedWritableAtom<boolean> & object;
7
+ /**
8
+ * Network pipeline gate — flipped when detectMode has resolved (or was
9
+ * skipped because $session.insightsBaseUrl was already cached). Stays
10
+ * false forever when no insightsUrl is configured.
11
+ */
12
+ declare const $isBaseUrlReady: import('nanostores').PreinitializedWritableAtom<boolean> & object;
13
+ /**
14
+ * Local-store readiness. Trackers and tracker-only consumers (popups) gate
15
+ * on this — they need session + identity, not a resolved base URL.
16
+ */
17
+ declare const $isIdentityContextReady: import('nanostores').ReadableAtom<boolean>;
18
+ /**
19
+ * Network readiness. /welcome and the initial page view gate on this.
20
+ * Implies identity context is ready and a usable base URL is known.
21
+ */
22
+ declare const $isNetworkReady: import('nanostores').ReadableAtom<boolean>;
23
+ export { $isIdentityReady, $isBaseUrlReady, $isIdentityContextReady, $isNetworkReady };
@@ -24,17 +24,13 @@ interface ISession {
24
24
  identity: IIdentity | null;
25
25
  }
26
26
  declare const $session: import('nanostores').PreinitializedWritableAtom<ISession> & object;
27
- /** Calls cb immediately if the session is already loaded, otherwise queues it. */
28
- declare const onSessionReady: (cb: () => void) => void;
29
- declare const isSessionReady: () => boolean;
30
- declare const getSession: () => ISession;
31
- declare const updateSession: (v: Partial<ISession>) => void;
32
- export { $session, getSession, updateSession, onSessionReady, isSessionReady, type ISession };
33
27
  /**
34
- * True when the analytics instance has everything it needs to call /welcome and
35
- * start tracking: a resolved base URL (from detectMode) and identity data (from
36
- * getUserProperties + getReferrer). Both are set asynchronously on init and may
37
- * arrive in either order — this computed fires once both are present.
28
+ * Becomes true once the session is loaded from storage (synchronous for
29
+ * localStorage, up to 150ms for BroadcastChannel handshake). Downstream
30
+ * pipelines (identity, network) gate on this so writes via updateSession
31
+ * survive the BroadcastChannel full-replace handshake.
38
32
  */
39
- declare const $isContextReady: import('nanostores').ReadableAtom<boolean>;
40
- export { $isContextReady };
33
+ declare const $isSessionHydrated: import('nanostores').PreinitializedWritableAtom<boolean> & object;
34
+ declare const getSession: () => ISession;
35
+ declare const updateSession: (v: Partial<ISession>) => void;
36
+ export { $session, $isSessionHydrated, getSession, updateSession, type ISession };
package/dist/stores.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { $config } from './stores/config';
2
2
  export { $eventQueue, $lifetimePageCount, $totalEventCount } from './stores/events';
3
- export { $isContextReady, $session } from './stores/session';
3
+ export { $isIdentityContextReady } from './stores/readiness';
4
+ export { $session } from './stores/session';
4
5
  export { $activity } from './tracking/activity';
5
6
  export { $pageLifecycle } from './tracking/bounce';
6
7
  export { $currentTab } from './tracking/currentTab';
package/dist/stores.js CHANGED
@@ -1,2 +1,2 @@
1
- import { C as e, _ as t, a as n, d as r, g as i, h as a, k as o, p as s, r as c, t as l, w as u } from "./textSelection-DJ23SrA-.js";
2
- export { s as $activity, o as $config, n as $currentTab, a as $eventQueue, e as $isContextReady, i as $lifetimePageCount, r as $pageLifecycle, c as $scrollBehavior, l as $selectedText, u as $session, t as $totalEventCount };
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 };
@@ -7,7 +7,8 @@ var o = n({
7
7
  workspace: null,
8
8
  page_id: null,
9
9
  site_id: null,
10
- debug: !1
10
+ debug: !1,
11
+ insightsUrl: null
11
12
  }), s = () => o.get(), c = (e) => o.set({
12
13
  ...s(),
13
14
  ...e
@@ -50,12 +51,8 @@ var o = n({
50
51
  } catch {
51
52
  return p;
52
53
  }
53
- }, g = e(p), _ = g.set, v = !1, y = [], b = () => {
54
- v || (v = !0, y.forEach((e) => {
55
- e();
56
- }), y.length = 0);
57
- }, ee = (e) => {
58
- v ? e() : y.push(e);
54
+ }, g = e(p), _ = g.set, v = e(!1), y = () => {
55
+ v.get() || v.set(!0);
59
56
  };
60
57
  if (f()) {
61
58
  let e = () => {
@@ -80,7 +77,7 @@ if (f()) {
80
77
  ...h(n),
81
78
  isIdentified: r.isIdentified,
82
79
  isIdentifying: !1
83
- }), b(), g.set = (e) => {
80
+ }), y(), g.set = (e) => {
84
81
  _(e);
85
82
  try {
86
83
  localStorage.setItem(d.session, m(e));
@@ -96,26 +93,26 @@ if (f()) {
96
93
  let e = new BroadcastChannel(d.session);
97
94
  e.onmessage = (t) => {
98
95
  let { type: n, value: r } = t.data;
99
- n === "UPDATE" && r ? _(r) : n === "QUERY" ? v && e.postMessage({
96
+ n === "UPDATE" && r ? _(r) : n === "QUERY" ? v.get() && e.postMessage({
100
97
  type: "RESPONSE",
101
98
  value: g.get()
102
- }) : n === "RESPONSE" && r && (_(r), b());
99
+ }) : n === "RESPONSE" && r && (_(r), y());
103
100
  }, g.set = (t) => {
104
101
  _(t), e.postMessage({
105
102
  type: "UPDATE",
106
103
  value: t
107
104
  });
108
105
  }, e.postMessage({ type: "QUERY" }), setTimeout(() => {
109
- b();
106
+ y();
110
107
  }, 150);
111
- } else b();
112
- var x = () => g.get(), S = (e) => {
108
+ } else y();
109
+ var b = () => g.get(), x = (e) => {
113
110
  g.set({
114
- ...x(),
111
+ ...b(),
115
112
  ...e
116
113
  });
117
- }, te = t(g, (e) => e.insightsBaseUrl !== null && e.identity !== null), ne = 10, re = 2e3, C = 500, w = e([]), T = e(0), E = t(w, (e) => e.length ?? 0), D = e(null), O = () => w.get(), ie = () => E.get(), k = (e) => {
118
- 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 } = x();
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();
119
116
  if (e.length === 0 || !a || !p || !t) return;
120
117
  let m = e.map((e) => ({
121
118
  workspace_id: t,
@@ -135,11 +132,15 @@ var x = () => g.get(), S = (e) => {
135
132
  resource: e.resource ?? "page",
136
133
  properties: e.properties ?? {}
137
134
  }));
138
- if (w.set([]), n) {
135
+ if (C.set([]), n) {
139
136
  console.log("[@levo-so/insights] Debug mode enabled. Events batch:", m);
140
137
  return;
141
138
  }
142
- let h = `${p}/v1/insights/event/bulk?workspace=${t}`, g = {
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 _ = {
143
144
  "Content-Type": "application/json",
144
145
  ...u({
145
146
  deviceToken: f,
@@ -147,83 +148,97 @@ var x = () => g.get(), S = (e) => {
147
148
  mode: o
148
149
  })
149
150
  };
150
- o === "proxy" && navigator.sendBeacon && navigator.sendBeacon(h, JSON.stringify(m)) || fetch(h, {
151
+ fetch(g, {
151
152
  method: "POST",
152
153
  body: JSON.stringify(m),
153
- headers: g,
154
+ headers: _,
154
155
  keepalive: !0,
155
156
  credentials: o === "proxy" ? "include" : "omit"
156
- }).catch(() => {});
157
- }, A = () => {
158
- if (D.get()) return;
157
+ }).catch((e) => {
158
+ console.warn("[@levo-so/insights] Event flush failed, batch lost:", e);
159
+ });
160
+ }, k = () => {
161
+ if (E.get()) return;
159
162
  let e = setTimeout(() => {
160
- k(O()), D.set(null);
161
- }, re);
162
- D.set(e);
163
- }, j = () => {
164
- let e = D.get();
165
- e && (clearTimeout(e), D.set(null));
166
- }, ae = (e) => {
167
- let { debug: t } = s(), n = T.get();
168
- if (n >= C) {
169
- t && console.warn(`[@levo-so/core] Event limit (${C}) reached for this page`);
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) => {
170
+ let { debug: t, insightsUrl: n } = s();
171
+ if (!n) {
172
+ t && console.warn("[@levo-so/insights] Event dropped insightsUrl not configured.", {
173
+ event: e.event,
174
+ resource: e.resource
175
+ });
170
176
  return;
171
177
  }
172
- T.set(n + 1);
173
178
  let r = w.get();
174
- w.set([...r, e]);
175
- let { isIdentified: i } = x();
176
- ie() >= ne ? (j(), k(O())) : i && A();
177
- }, oe = () => T.set(0), M = g.get().isIdentified;
179
+ if (r >= S) {
180
+ t && console.warn(`[@levo-so/core] Event limit (${S}) reached for this page`);
181
+ return;
182
+ }
183
+ w.set(r + 1);
184
+ let i = C.get();
185
+ C.set([...i, e]);
186
+ let { isIdentified: a } = b();
187
+ ne() >= ee ? (A(), O(D())) : a && k();
188
+ }, ie = () => w.set(0), j = g.get().isIdentified;
189
+ g.subscribe((e) => {
190
+ e.isIdentified && !j && C.get().length > 0 && (e.insightsBaseUrl ? O(D()) : k()), j = e.isIdentified;
191
+ });
192
+ var M = g.get().insightsBaseUrl;
178
193
  g.subscribe((e) => {
179
- e.isIdentified && !M && w.get().length > 0 && (e.insightsBaseUrl ? k(O()) : A()), M = e.isIdentified;
194
+ e.insightsBaseUrl && !M && e.isIdentified && C.get().length > 0 && O(D()), M = e.insightsBaseUrl;
180
195
  });
181
- var se = () => {
182
- w.set([]), T.set(0), j();
183
- }, N = n({
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
199
  status: "active",
185
200
  seconds: 0
186
- }), ce = (e) => {
201
+ }), le = (e) => {
187
202
  if (typeof window > "u") return () => {};
188
203
  let t = r({
189
204
  timeout: 6e4,
190
205
  throttle: 1e3,
191
206
  onIdle: (e) => {
192
- N.set({
207
+ P.set({
193
208
  status: "idle",
194
209
  seconds: e
195
210
  });
196
211
  },
197
212
  onWakeUp: (e) => {
198
- N.set({
213
+ P.set({
199
214
  status: "active",
200
215
  seconds: e
201
216
  });
202
217
  }
203
- }), n = N.listen((t) => {
218
+ }), n = P.listen((t) => {
204
219
  t.seconds > 0 && (t.status === "idle" ? e.track("user.idle", { seconds_active: t.seconds }) : e.track("user.active", { seconds_idle: t.seconds }));
205
220
  });
206
221
  return () => {
207
- n(), t.disable(), N.set({
222
+ n(), t.disable(), P.set({
208
223
  status: "active",
209
224
  seconds: 0
210
225
  });
211
226
  };
212
- }, le = () => N.get(), P = /* @__PURE__ */ function(e) {
227
+ }, ue = () => P.get(), F = /* @__PURE__ */ function(e) {
213
228
  return e.ACTIVE = "active", e.PASSIVE = "passive", e.HIDDEN = "hidden", e.FROZEN = "frozen", e.TERMINATED = "terminated", e;
214
- }(P || {}), F = e(P.ACTIVE), ue = () => typeof document > "u" ? P.ACTIVE : document.visibilityState === "hidden" ? P.HIDDEN : document.hasFocus() ? P.ACTIVE : P.PASSIVE, de = (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) => {
215
230
  if (typeof window > "u") return () => {};
216
231
  let t = !1, n = (n) => {
217
232
  if (t) return;
218
233
  t = !0;
219
- let r = le(), a = {};
234
+ let r = ue(), a = {};
220
235
  r.seconds > 0 && (r.status === "idle" ? a.seconds_idle = r.seconds : a.seconds_active = r.seconds);
221
236
  try {
222
237
  e.track("page.bounce", {
223
238
  resource: "page",
224
239
  ...a,
225
240
  ...n
226
- }, {}, () => k(O()));
241
+ }, {}, () => O(D()));
227
242
  } catch (e) {
228
243
  console.error("[insights] Failed to send bounce event", i(e));
229
244
  }
@@ -247,13 +262,13 @@ var se = () => {
247
262
  case "blur":
248
263
  case "focus":
249
264
  case "visibilitychange":
250
- F.set(ue());
265
+ I.set(de());
251
266
  break;
252
267
  case "freeze":
253
- F.set(P.FROZEN);
268
+ I.set(F.FROZEN);
254
269
  break;
255
270
  case "pagehide":
256
- F.set(e.persisted ? P.FROZEN : P.TERMINATED);
271
+ I.set(e.persisted ? F.FROZEN : F.TERMINATED);
257
272
  break;
258
273
  }
259
274
  }, s = [
@@ -268,30 +283,32 @@ var se = () => {
268
283
  s.forEach((e) => {
269
284
  window.addEventListener(e, a, { capture: !0 });
270
285
  }), window.addEventListener("beforeunload", r);
271
- let c = F.listen((e) => {
272
- e === P.FROZEN || e === P.TERMINATED ? n({
286
+ let c = I.listen((e) => {
287
+ e === F.FROZEN || e === F.TERMINATED ? n({
273
288
  state: e,
274
289
  trigger: "lifecycle"
275
- }) : e === P.HIDDEN && k(O());
290
+ }) : e === F.HIDDEN && O(D());
276
291
  }), l = o.get().page_id, u = o.listen((e) => {
277
292
  e.page_id !== l && (t = !1, l = e.page_id);
293
+ }), d = w.listen((e) => {
294
+ e === 0 && (t = !1);
278
295
  });
279
296
  return () => {
280
297
  s.forEach((e) => {
281
298
  window.removeEventListener(e, a, { capture: !0 });
282
- }), window.removeEventListener("beforeunload", r), c(), u(), t = !1, F.set(P.ACTIVE);
299
+ }), window.removeEventListener("beforeunload", r), c(), u(), d(), t = !1, I.set(F.ACTIVE);
283
300
  };
284
- }, I = (e) => Object.prototype.toString.call(e) == "[object String]", fe = (e, t) => e.indexOf(t) !== -1, L = (e) => e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""), R = (e) => e === void 0, pe = (e) => e === null, z = (e) => R(e) || pe(e), B = (e) => !!e && e?.nodeType === 3;
285
- function me(e) {
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) {
286
303
  return !!e && e?.nodeType === 1;
287
304
  }
288
- function he(e, t) {
305
+ function ge(e, t) {
289
306
  return !!e && !!e?.tagName && e?.tagName.toLowerCase() === t.toLowerCase();
290
307
  }
291
- function ge(e) {
292
- return e ? L(e).split(/\s+/) : [];
293
- }
294
308
  function _e(e) {
309
+ return e ? R(e).split(/\s+/) : [];
310
+ }
311
+ function ve(e) {
295
312
  let t = "";
296
313
  switch (typeof e?.className) {
297
314
  case "string":
@@ -302,9 +319,9 @@ function _e(e) {
302
319
  break;
303
320
  default: t = "";
304
321
  }
305
- return ge(t);
322
+ return _e(t);
306
323
  }
307
- var V = (e, t = 255) => z(e) ? "" : L(e).split(/(\s+)/).filter((e) => q(e)).join("").replace(/[\r\n]/g, " ").replace(/[ ]+/g, " ").substring(0, t), H = [
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 = [
308
325
  "span",
309
326
  "strong",
310
327
  "em",
@@ -316,25 +333,25 @@ var V = (e, t = 255) => z(e) ? "" : L(e).split(/(\s+)/).filter((e) => q(e)).join
316
333
  "sub",
317
334
  "sup",
318
335
  "label"
319
- ], U = (e) => {
336
+ ], W = (e) => {
320
337
  let t = "";
321
- if (be(e) && e?.childNodes && e?.childNodes.length) {
322
- for (let n of e.childNodes) B(n) && n.textContent && (t += V(n.textContent) ?? "");
323
- if (!L(t)) {
324
- let n = e.querySelectorAll(H.join(", "));
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(", "));
325
342
  for (let e of n) {
326
343
  let n = e.textContent;
327
- n && (t += `${V(n)} `);
344
+ n && (t += `${H(n)} `);
328
345
  }
329
346
  }
330
- if (!L(t) && e instanceof HTMLElement) {
347
+ if (!R(t) && e instanceof HTMLElement) {
331
348
  let n = e.innerText;
332
- n && (t = V(n.substring(0, 500)));
349
+ n && (t = H(n.substring(0, 500)));
333
350
  }
334
351
  }
335
- return L(t);
336
- }, W = (e) => R(e.target) ? e?.srcElement || null : e?.composedPath?.()[0] || e?.target || null, ve = (e, t) => {
337
- if (!window || !e || he(e, "html") || !me(e)) return !1;
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;
338
355
  let n = window.getComputedStyle(e);
339
356
  if (n && n.getPropertyValue("cursor") === "pointer" && t.type === "click") return !0;
340
357
  switch (e?.tagName.toLowerCase()) {
@@ -345,7 +362,7 @@ var V = (e, t = 255) => z(e) ? "" : L(e).split(/(\s+)/).filter((e) => q(e)).join
345
362
  case "textarea": return ["change", "click"].includes(t.type);
346
363
  default: return t.type === "click";
347
364
  }
348
- }, ye = new Set([
365
+ }, be = new Set([
349
366
  "password",
350
367
  "passwd",
351
368
  "pwd",
@@ -366,73 +383,73 @@ var V = (e, t = 255) => z(e) ? "" : L(e).split(/(\s+)/).filter((e) => q(e)).join
366
383
  "apikey",
367
384
  "accesstoken"
368
385
  ]);
369
- function be(e) {
370
- if (fe(_e(e), "ph-include")) return !0;
386
+ function xe(e) {
387
+ if (pe(ve(e), "ph-include")) return !0;
371
388
  let t = e.type || "";
372
- if (I(t)) switch (t.toLowerCase()) {
389
+ if (L(t)) switch (t.toLowerCase()) {
373
390
  case "hidden": return !1;
374
391
  case "password": return !1;
375
392
  }
376
393
  let n = e.name || e?.id || "";
377
- if (I(n)) {
394
+ if (L(n)) {
378
395
  let e = n.toLowerCase().replace(/[^a-z]/g, "");
379
- if (ye.has(e)) return !1;
396
+ if (be.has(e)) return !1;
380
397
  }
381
398
  return !0;
382
399
  }
383
- var G = "(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})", xe = RegExp(`^(?:${G})$`), Se = new RegExp(G), K = "\\d{3}-?\\d{2}-?\\d{4}", Ce = RegExp(`^(${K})$`), we = RegExp(`(${K})`), q = (e, t = !0) => !(z(e) || I(e) && (e = L(e), (t ? xe : Se).test((e || "").replace(/[- ]/g, "")) || (t ? Ce : we).test(e))), Te = (e) => I(e) ? e.substring(0, 10) === "_ngcontent" || e.substring(0, 7) === "_nghost" : !1, J = (e) => {
384
- let t = U(e);
385
- return t = `${t} ${Y(e)}`.trim(), q(t) ? t : "";
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 : "";
386
403
  };
387
404
  function Y(e) {
388
405
  let t = "";
389
406
  if (e?.childNodes.length) for (let n of e.childNodes) {
390
407
  let e = n.tagName?.toLowerCase();
391
- if (n && e && H.includes(e)) try {
392
- let e = U(n);
408
+ if (n && e && U.includes(e)) try {
409
+ let e = W(n);
393
410
  t = `${t} ${e}`.trim(), n?.childNodes.length && (t = `${t} ${Y(n)}`.trim());
394
411
  } catch {}
395
412
  }
396
413
  return t;
397
414
  }
398
- var Ee = (e) => {
399
- let t = e.tagName?.toLowerCase(), n = t === "a" || t === "button" ? J(e) : U(e);
415
+ var Oe = (e) => {
416
+ let t = e.tagName?.toLowerCase(), n = t === "a" || t === "button" ? De(e) : W(e);
400
417
  if (n) return {
401
418
  text: n,
402
419
  source: "text"
403
420
  };
404
421
  let r = e.getAttribute("aria-label");
405
422
  if (r) return {
406
- text: V(r),
423
+ text: H(r),
407
424
  source: "aria-label"
408
425
  };
409
426
  let i = e.getAttribute("title");
410
427
  if (i) return {
411
- text: V(i),
428
+ text: H(i),
412
429
  source: "title"
413
430
  };
414
431
  let a = e.getAttribute("data-track-label");
415
432
  if (a) return {
416
- text: V(a),
433
+ text: H(a),
417
434
  source: "data-track-label"
418
435
  };
419
436
  let o = e.querySelector("img[alt], svg[aria-label]");
420
437
  if (o) {
421
438
  let e = o.getAttribute("alt") || o.getAttribute("aria-label");
422
439
  if (e) return {
423
- text: V(e),
440
+ text: H(e),
424
441
  source: "alt"
425
442
  };
426
443
  }
427
444
  let s = e.getAttribute("placeholder");
428
445
  if (s) return {
429
- text: V(s),
446
+ text: H(s),
430
447
  source: "placeholder"
431
448
  };
432
449
  if (e instanceof HTMLElement && e.innerText) {
433
450
  let t = e.innerText.substring(0, 500);
434
451
  if (t.trim()) return {
435
- text: V(t),
452
+ text: H(t),
436
453
  source: "innerText"
437
454
  };
438
455
  }
@@ -440,67 +457,76 @@ var Ee = (e) => {
440
457
  text: "",
441
458
  source: "none"
442
459
  };
443
- }, De = [
460
+ }, ke = [
444
461
  "input",
445
462
  "textarea",
446
463
  "select"
447
464
  ], X = (e) => {
448
- let t = e?.tagName?.toLowerCase(), n = { element: t }, r = Ee(e);
465
+ let t = e?.tagName?.toLowerCase(), n = { element: t }, r = Oe(e);
449
466
  n.text = r.text, n.text_source = r.source;
450
467
  let i = e.getAttribute("aria-label");
451
- i && (n.aria_label = V(i));
468
+ i && (n.aria_label = H(i));
452
469
  let a = e.getAttribute("role");
453
470
  a && (n.role = a);
454
471
  let o = {};
455
- for (let t of e.attributes) (t.name.startsWith("data-track-") || t.name.startsWith("data-analytics-")) && (o[t.name] = V(t.value));
472
+ for (let t of e.attributes) (t.name.startsWith("data-track-") || t.name.startsWith("data-analytics-")) && (o[t.name] = H(t.value));
456
473
  if (Object.keys(o).length && (n.tracking_data = o), n.attributes = Array.from(e.attributes).reduce((e, t) => {
457
- if (q(t.value) && !Te(t.name)) {
474
+ if (J(t.value) && !Ee(t.name)) {
458
475
  if (t.name === "class" || t.name.startsWith("data-track-") || t.name.startsWith("data-analytics-")) return e;
459
- e[t.name] = typeof t?.value == "string" ? V(t.value) : t.value;
476
+ e[t.name] = typeof t?.value == "string" ? H(t.value) : t.value;
460
477
  }
461
478
  return e;
462
- }, {}), De.includes(t) || e?.getAttribute("contenteditable") === "true") {
479
+ }, {}), ke.includes(t) || e?.getAttribute("contenteditable") === "true") {
463
480
  let t;
464
- 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 ? V(t, 1024) : 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);
465
482
  let r = "";
466
- n?.attributes.id && (r = V(document.querySelector(`label[for="${n?.attributes.id}"]`)?.textContent?.trim() || ""));
483
+ n?.attributes.id && (r = H(document.querySelector(`label[for="${n?.attributes.id}"]`)?.textContent?.trim() || ""));
467
484
  let i = e.closest("label");
468
- r ||= V(i?.textContent?.trim() || ""), r && (n.label = r);
485
+ r ||= H(i?.textContent?.trim() || ""), r && (n.label = r);
469
486
  }
470
487
  return n;
471
- }, Oe = (e, t) => {
472
- let n = W(e);
473
- B(n) && (n = n.parentNode || null);
488
+ }, Ae = (e, t) => {
489
+ let n = G(e);
490
+ V(n) && (n = n.parentNode || null);
474
491
  let r = ["copy", "cut"].includes(t);
475
- if (n && (r || ve(n, e))) {
492
+ if (n && (r || ye(n, e))) {
476
493
  let t = X(n);
477
494
  if (r) {
478
- let n = V(window?.getSelection()?.toString()), r = e?.type || "clipboard";
495
+ let n = H(window?.getSelection()?.toString()), r = e?.type || "clipboard";
479
496
  t.copy_text = n ?? "", t.copy_type = r ?? "";
480
497
  }
481
498
  return t;
482
499
  }
483
500
  return null;
484
- }, Z = e(null), ke = (e) => {
501
+ }, Z = e(null), je = (e) => {
485
502
  if (typeof window > "u") return () => {};
486
- let t = sessionStorage.getItem(d.current_tab), { allTabs: n } = g.get();
487
- t && n.includes(t) && (t = null), t || (t = `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, sessionStorage.setItem(d.current_tab, t));
503
+ let t = null;
504
+ try {
505
+ t = sessionStorage.getItem(d.current_tab);
506
+ } catch {}
507
+ let { allTabs: n } = g.get();
508
+ if (t && n.includes(t) && (t = null), !t) {
509
+ t = `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
510
+ try {
511
+ sessionStorage.setItem(d.current_tab, t);
512
+ } catch {}
513
+ }
488
514
  let r = t;
489
- Z.set(r), n.includes(r) || S({ allTabs: [...n, r] });
515
+ Z.set(r), n.includes(r) || x({ allTabs: [...n, r] });
490
516
  let i = () => {
491
517
  if (!r) return;
492
518
  let { allTabs: e } = g.get();
493
- e.includes(r) && S({ allTabs: e.filter((e) => e !== r) });
519
+ e.includes(r) && x({ allTabs: e.filter((e) => e !== r) });
494
520
  };
495
521
  return window.addEventListener("beforeunload", i), () => {
496
522
  window.removeEventListener("beforeunload", i), i(), Z.set(null);
497
523
  };
498
- }, Ae = () => Z.get(), Q = n({
524
+ }, Me = () => Z.get(), Q = n({
499
525
  depth: 0,
500
526
  milestone: 0,
501
527
  speedCategory: "read",
502
528
  direction: "down"
503
- }), je = (e) => {
529
+ }), Ne = (e) => {
504
530
  if (typeof window > "u") return () => {};
505
531
  let t = {
506
532
  prevMilestone: -1,
@@ -537,7 +563,7 @@ var Ee = (e) => {
537
563
  direction: "down"
538
564
  });
539
565
  };
540
- }, $ = e(null), Me = (e) => {
566
+ }, $ = e(null), Pe = (e) => {
541
567
  if (typeof window > "u") return () => {};
542
568
  let t = a(() => {
543
569
  let e = window.getSelection();
@@ -548,11 +574,11 @@ var Ee = (e) => {
548
574
  }, 1e3);
549
575
  document.addEventListener("selectionchange", t);
550
576
  let n = $.listen((t) => {
551
- t && e.track("page.selection", { text: V(t) });
577
+ t && e.track("page.selection", { text: H(t) });
552
578
  });
553
579
  return () => {
554
580
  document.removeEventListener("selectionchange", t), t.cancel(), n(), $.set(null);
555
581
  };
556
582
  };
557
583
  //#endregion
558
- export { s as A, te as C, S as D, ee as E, l as M, u as O, oe as S, x as T, E as _, Z as a, O as b, X as c, F as d, de as f, T as g, w as h, je as i, c as j, o as k, W as l, ce as m, Me as n, Ae as o, N as p, Q as r, ke as s, $ as t, Oe as u, se as v, g as w, ae as x, k as y };
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 };
@@ -1,5 +1,10 @@
1
1
  export interface IInsightOptions {
2
- insightsUrl: string;
2
+ /**
3
+ * Reverse-proxy URL for analytics. When null/undefined, identity tracking
4
+ * and DOM trackers still run (popup triggers stay functional), but no
5
+ * network calls are made and queued events are dropped.
6
+ */
7
+ insightsUrl: string | null;
3
8
  /** Site ID for analytics tracking */
4
9
  site?: string | null;
5
10
  /** Page ID for analytics tracking */
@@ -0,0 +1,10 @@
1
+ import { ReadableAtom } from 'nanostores';
2
+ /**
3
+ * Run `cb` once when `store` becomes (or already is) true.
4
+ *
5
+ * Returns an unsubscribe function so callers can cancel the pending callback
6
+ * during teardown (e.g. push into the cleanups array in init/destroy). If the
7
+ * store is already true at call time, fires synchronously and returns a no-op
8
+ * unsubscribe.
9
+ */
10
+ export declare const onceTrue: (store: ReadableAtom<boolean>, cb: () => void) => (() => void);