@levo-so/insights 0.1.86 → 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.
@@ -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-DC99Tbqq.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,85 +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
157
  }).catch((e) => {
157
158
  console.warn("[@levo-so/insights] Event flush failed, batch lost:", e);
158
159
  });
159
- }, A = () => {
160
- if (D.get()) return;
160
+ }, k = () => {
161
+ if (E.get()) return;
161
162
  let e = setTimeout(() => {
162
- k(O()), D.set(null);
163
- }, re);
164
- D.set(e);
165
- }, j = () => {
166
- let e = D.get();
167
- e && (clearTimeout(e), D.set(null));
168
- }, ae = (e) => {
169
- let { debug: t } = s(), n = T.get();
170
- if (n >= C) {
171
- 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
+ });
172
176
  return;
173
177
  }
174
- T.set(n + 1);
175
178
  let r = w.get();
176
- w.set([...r, e]);
177
- let { isIdentified: i } = x();
178
- ie() >= ne ? (j(), k(O())) : i && A();
179
- }, 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;
180
189
  g.subscribe((e) => {
181
- e.isIdentified && !M && w.get().length > 0 && (e.insightsBaseUrl ? k(O()) : A()), M = e.isIdentified;
190
+ e.isIdentified && !j && C.get().length > 0 && (e.insightsBaseUrl ? O(D()) : k()), j = e.isIdentified;
182
191
  });
183
- var se = () => {
184
- w.set([]), T.set(0), j();
185
- }, N = n({
192
+ var M = g.get().insightsBaseUrl;
193
+ g.subscribe((e) => {
194
+ e.insightsBaseUrl && !M && e.isIdentified && C.get().length > 0 && O(D()), M = e.insightsBaseUrl;
195
+ });
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({
186
199
  status: "active",
187
200
  seconds: 0
188
- }), ce = (e) => {
201
+ }), le = (e) => {
189
202
  if (typeof window > "u") return () => {};
190
203
  let t = r({
191
204
  timeout: 6e4,
192
205
  throttle: 1e3,
193
206
  onIdle: (e) => {
194
- N.set({
207
+ P.set({
195
208
  status: "idle",
196
209
  seconds: e
197
210
  });
198
211
  },
199
212
  onWakeUp: (e) => {
200
- N.set({
213
+ P.set({
201
214
  status: "active",
202
215
  seconds: e
203
216
  });
204
217
  }
205
- }), n = N.listen((t) => {
218
+ }), n = P.listen((t) => {
206
219
  t.seconds > 0 && (t.status === "idle" ? e.track("user.idle", { seconds_active: t.seconds }) : e.track("user.active", { seconds_idle: t.seconds }));
207
220
  });
208
221
  return () => {
209
- n(), t.disable(), N.set({
222
+ n(), t.disable(), P.set({
210
223
  status: "active",
211
224
  seconds: 0
212
225
  });
213
226
  };
214
- }, le = () => N.get(), P = /* @__PURE__ */ function(e) {
227
+ }, ue = () => P.get(), F = /* @__PURE__ */ function(e) {
215
228
  return e.ACTIVE = "active", e.PASSIVE = "passive", e.HIDDEN = "hidden", e.FROZEN = "frozen", e.TERMINATED = "terminated", e;
216
- }(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) => {
217
230
  if (typeof window > "u") return () => {};
218
231
  let t = !1, n = (n) => {
219
232
  if (t) return;
220
233
  t = !0;
221
- let r = le(), a = {};
234
+ let r = ue(), a = {};
222
235
  r.seconds > 0 && (r.status === "idle" ? a.seconds_idle = r.seconds : a.seconds_active = r.seconds);
223
236
  try {
224
237
  e.track("page.bounce", {
225
238
  resource: "page",
226
239
  ...a,
227
240
  ...n
228
- }, {}, () => k(O()));
241
+ }, {}, () => O(D()));
229
242
  } catch (e) {
230
243
  console.error("[insights] Failed to send bounce event", i(e));
231
244
  }
@@ -249,13 +262,13 @@ var se = () => {
249
262
  case "blur":
250
263
  case "focus":
251
264
  case "visibilitychange":
252
- F.set(ue());
265
+ I.set(de());
253
266
  break;
254
267
  case "freeze":
255
- F.set(P.FROZEN);
268
+ I.set(F.FROZEN);
256
269
  break;
257
270
  case "pagehide":
258
- F.set(e.persisted ? P.FROZEN : P.TERMINATED);
271
+ I.set(e.persisted ? F.FROZEN : F.TERMINATED);
259
272
  break;
260
273
  }
261
274
  }, s = [
@@ -270,32 +283,32 @@ var se = () => {
270
283
  s.forEach((e) => {
271
284
  window.addEventListener(e, a, { capture: !0 });
272
285
  }), window.addEventListener("beforeunload", r);
273
- let c = F.listen((e) => {
274
- e === P.FROZEN || e === P.TERMINATED ? n({
286
+ let c = I.listen((e) => {
287
+ e === F.FROZEN || e === F.TERMINATED ? n({
275
288
  state: e,
276
289
  trigger: "lifecycle"
277
- }) : e === P.HIDDEN && k(O());
290
+ }) : e === F.HIDDEN && O(D());
278
291
  }), l = o.get().page_id, u = o.listen((e) => {
279
292
  e.page_id !== l && (t = !1, l = e.page_id);
280
- }), d = T.listen((e) => {
293
+ }), d = w.listen((e) => {
281
294
  e === 0 && (t = !1);
282
295
  });
283
296
  return () => {
284
297
  s.forEach((e) => {
285
298
  window.removeEventListener(e, a, { capture: !0 });
286
- }), window.removeEventListener("beforeunload", r), c(), u(), d(), t = !1, F.set(P.ACTIVE);
299
+ }), window.removeEventListener("beforeunload", r), c(), u(), d(), t = !1, I.set(F.ACTIVE);
287
300
  };
288
- }, 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;
289
- 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) {
290
303
  return !!e && e?.nodeType === 1;
291
304
  }
292
- function he(e, t) {
305
+ function ge(e, t) {
293
306
  return !!e && !!e?.tagName && e?.tagName.toLowerCase() === t.toLowerCase();
294
307
  }
295
- function ge(e) {
296
- return e ? L(e).split(/\s+/) : [];
297
- }
298
308
  function _e(e) {
309
+ return e ? R(e).split(/\s+/) : [];
310
+ }
311
+ function ve(e) {
299
312
  let t = "";
300
313
  switch (typeof e?.className) {
301
314
  case "string":
@@ -306,9 +319,9 @@ function _e(e) {
306
319
  break;
307
320
  default: t = "";
308
321
  }
309
- return ge(t);
322
+ return _e(t);
310
323
  }
311
- 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 = [
312
325
  "span",
313
326
  "strong",
314
327
  "em",
@@ -320,25 +333,25 @@ var V = (e, t = 255) => z(e) ? "" : L(e).split(/(\s+)/).filter((e) => q(e)).join
320
333
  "sub",
321
334
  "sup",
322
335
  "label"
323
- ], U = (e) => {
336
+ ], W = (e) => {
324
337
  let t = "";
325
- if (be(e) && e?.childNodes && e?.childNodes.length) {
326
- for (let n of e.childNodes) B(n) && n.textContent && (t += V(n.textContent) ?? "");
327
- if (!L(t)) {
328
- 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(", "));
329
342
  for (let e of n) {
330
343
  let n = e.textContent;
331
- n && (t += `${V(n)} `);
344
+ n && (t += `${H(n)} `);
332
345
  }
333
346
  }
334
- if (!L(t) && e instanceof HTMLElement) {
347
+ if (!R(t) && e instanceof HTMLElement) {
335
348
  let n = e.innerText;
336
- n && (t = V(n.substring(0, 500)));
349
+ n && (t = H(n.substring(0, 500)));
337
350
  }
338
351
  }
339
- return L(t);
340
- }, W = (e) => R(e.target) ? e?.srcElement || null : e?.composedPath?.()[0] || e?.target || null, ve = (e, t) => {
341
- 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;
342
355
  let n = window.getComputedStyle(e);
343
356
  if (n && n.getPropertyValue("cursor") === "pointer" && t.type === "click") return !0;
344
357
  switch (e?.tagName.toLowerCase()) {
@@ -349,7 +362,7 @@ var V = (e, t = 255) => z(e) ? "" : L(e).split(/(\s+)/).filter((e) => q(e)).join
349
362
  case "textarea": return ["change", "click"].includes(t.type);
350
363
  default: return t.type === "click";
351
364
  }
352
- }, ye = new Set([
365
+ }, be = new Set([
353
366
  "password",
354
367
  "passwd",
355
368
  "pwd",
@@ -370,73 +383,73 @@ var V = (e, t = 255) => z(e) ? "" : L(e).split(/(\s+)/).filter((e) => q(e)).join
370
383
  "apikey",
371
384
  "accesstoken"
372
385
  ]);
373
- function be(e) {
374
- if (fe(_e(e), "ph-include")) return !0;
386
+ function xe(e) {
387
+ if (pe(ve(e), "ph-include")) return !0;
375
388
  let t = e.type || "";
376
- if (I(t)) switch (t.toLowerCase()) {
389
+ if (L(t)) switch (t.toLowerCase()) {
377
390
  case "hidden": return !1;
378
391
  case "password": return !1;
379
392
  }
380
393
  let n = e.name || e?.id || "";
381
- if (I(n)) {
394
+ if (L(n)) {
382
395
  let e = n.toLowerCase().replace(/[^a-z]/g, "");
383
- if (ye.has(e)) return !1;
396
+ if (be.has(e)) return !1;
384
397
  }
385
398
  return !0;
386
399
  }
387
- 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) => {
388
- let t = U(e);
389
- 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 : "";
390
403
  };
391
404
  function Y(e) {
392
405
  let t = "";
393
406
  if (e?.childNodes.length) for (let n of e.childNodes) {
394
407
  let e = n.tagName?.toLowerCase();
395
- if (n && e && H.includes(e)) try {
396
- let e = U(n);
408
+ if (n && e && U.includes(e)) try {
409
+ let e = W(n);
397
410
  t = `${t} ${e}`.trim(), n?.childNodes.length && (t = `${t} ${Y(n)}`.trim());
398
411
  } catch {}
399
412
  }
400
413
  return t;
401
414
  }
402
- var Ee = (e) => {
403
- 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);
404
417
  if (n) return {
405
418
  text: n,
406
419
  source: "text"
407
420
  };
408
421
  let r = e.getAttribute("aria-label");
409
422
  if (r) return {
410
- text: V(r),
423
+ text: H(r),
411
424
  source: "aria-label"
412
425
  };
413
426
  let i = e.getAttribute("title");
414
427
  if (i) return {
415
- text: V(i),
428
+ text: H(i),
416
429
  source: "title"
417
430
  };
418
431
  let a = e.getAttribute("data-track-label");
419
432
  if (a) return {
420
- text: V(a),
433
+ text: H(a),
421
434
  source: "data-track-label"
422
435
  };
423
436
  let o = e.querySelector("img[alt], svg[aria-label]");
424
437
  if (o) {
425
438
  let e = o.getAttribute("alt") || o.getAttribute("aria-label");
426
439
  if (e) return {
427
- text: V(e),
440
+ text: H(e),
428
441
  source: "alt"
429
442
  };
430
443
  }
431
444
  let s = e.getAttribute("placeholder");
432
445
  if (s) return {
433
- text: V(s),
446
+ text: H(s),
434
447
  source: "placeholder"
435
448
  };
436
449
  if (e instanceof HTMLElement && e.innerText) {
437
450
  let t = e.innerText.substring(0, 500);
438
451
  if (t.trim()) return {
439
- text: V(t),
452
+ text: H(t),
440
453
  source: "innerText"
441
454
  };
442
455
  }
@@ -444,48 +457,48 @@ var Ee = (e) => {
444
457
  text: "",
445
458
  source: "none"
446
459
  };
447
- }, De = [
460
+ }, ke = [
448
461
  "input",
449
462
  "textarea",
450
463
  "select"
451
464
  ], X = (e) => {
452
- let t = e?.tagName?.toLowerCase(), n = { element: t }, r = Ee(e);
465
+ let t = e?.tagName?.toLowerCase(), n = { element: t }, r = Oe(e);
453
466
  n.text = r.text, n.text_source = r.source;
454
467
  let i = e.getAttribute("aria-label");
455
- i && (n.aria_label = V(i));
468
+ i && (n.aria_label = H(i));
456
469
  let a = e.getAttribute("role");
457
470
  a && (n.role = a);
458
471
  let o = {};
459
- 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));
460
473
  if (Object.keys(o).length && (n.tracking_data = o), n.attributes = Array.from(e.attributes).reduce((e, t) => {
461
- if (q(t.value) && !Te(t.name)) {
474
+ if (J(t.value) && !Ee(t.name)) {
462
475
  if (t.name === "class" || t.name.startsWith("data-track-") || t.name.startsWith("data-analytics-")) return e;
463
- 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;
464
477
  }
465
478
  return e;
466
- }, {}), De.includes(t) || e?.getAttribute("contenteditable") === "true") {
479
+ }, {}), ke.includes(t) || e?.getAttribute("contenteditable") === "true") {
467
480
  let t;
468
- 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);
469
482
  let r = "";
470
- 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() || ""));
471
484
  let i = e.closest("label");
472
- r ||= V(i?.textContent?.trim() || ""), r && (n.label = r);
485
+ r ||= H(i?.textContent?.trim() || ""), r && (n.label = r);
473
486
  }
474
487
  return n;
475
- }, Oe = (e, t) => {
476
- let n = W(e);
477
- B(n) && (n = n.parentNode || null);
488
+ }, Ae = (e, t) => {
489
+ let n = G(e);
490
+ V(n) && (n = n.parentNode || null);
478
491
  let r = ["copy", "cut"].includes(t);
479
- if (n && (r || ve(n, e))) {
492
+ if (n && (r || ye(n, e))) {
480
493
  let t = X(n);
481
494
  if (r) {
482
- let n = V(window?.getSelection()?.toString()), r = e?.type || "clipboard";
495
+ let n = H(window?.getSelection()?.toString()), r = e?.type || "clipboard";
483
496
  t.copy_text = n ?? "", t.copy_type = r ?? "";
484
497
  }
485
498
  return t;
486
499
  }
487
500
  return null;
488
- }, Z = e(null), ke = (e) => {
501
+ }, Z = e(null), je = (e) => {
489
502
  if (typeof window > "u") return () => {};
490
503
  let t = null;
491
504
  try {
@@ -499,21 +512,21 @@ var Ee = (e) => {
499
512
  } catch {}
500
513
  }
501
514
  let r = t;
502
- Z.set(r), n.includes(r) || S({ allTabs: [...n, r] });
515
+ Z.set(r), n.includes(r) || x({ allTabs: [...n, r] });
503
516
  let i = () => {
504
517
  if (!r) return;
505
518
  let { allTabs: e } = g.get();
506
- e.includes(r) && S({ allTabs: e.filter((e) => e !== r) });
519
+ e.includes(r) && x({ allTabs: e.filter((e) => e !== r) });
507
520
  };
508
521
  return window.addEventListener("beforeunload", i), () => {
509
522
  window.removeEventListener("beforeunload", i), i(), Z.set(null);
510
523
  };
511
- }, Ae = () => Z.get(), Q = n({
524
+ }, Me = () => Z.get(), Q = n({
512
525
  depth: 0,
513
526
  milestone: 0,
514
527
  speedCategory: "read",
515
528
  direction: "down"
516
- }), je = (e) => {
529
+ }), Ne = (e) => {
517
530
  if (typeof window > "u") return () => {};
518
531
  let t = {
519
532
  prevMilestone: -1,
@@ -550,7 +563,7 @@ var Ee = (e) => {
550
563
  direction: "down"
551
564
  });
552
565
  };
553
- }, $ = e(null), Me = (e) => {
566
+ }, $ = e(null), Pe = (e) => {
554
567
  if (typeof window > "u") return () => {};
555
568
  let t = a(() => {
556
569
  let e = window.getSelection();
@@ -561,11 +574,11 @@ var Ee = (e) => {
561
574
  }, 1e3);
562
575
  document.addEventListener("selectionchange", t);
563
576
  let n = $.listen((t) => {
564
- t && e.track("page.selection", { text: V(t) });
577
+ t && e.track("page.selection", { text: H(t) });
565
578
  });
566
579
  return () => {
567
580
  document.removeEventListener("selectionchange", t), t.cancel(), n(), $.set(null);
568
581
  };
569
582
  };
570
583
  //#endregion
571
- 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);