@levo-so/insights 0.1.65 → 0.1.76

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,888 +1,585 @@
1
- import { onUserActivity } from '@analytics/activity-utils';
2
- import { getVisitorSource } from '@analytics/visitor-source';
3
- import { listToRecord, getLevoError } from '@levo-so/core';
4
- import { Analytics } from 'analytics';
5
- import getUserLocale from 'get-user-locale';
6
-
7
- // src/index.ts
8
-
9
- // src/utils/isIncognito.ts
10
- var isIncognito = () => new Promise((resolve, reject) => {
11
- let browserName = "Unknown";
12
- function __callback(isPrivate) {
13
- resolve({
14
- isPrivate,
15
- browserName
16
- });
1
+ import { p as E, k as H, l as R, $ as U, e as q, m as P, n as C, o as z, u as T, q as G, r as $, s as K, t as W, v as j, w as Q, x as N, y as X, z as Y, d as Z, A as J, B as V, C as ee, D as te, E as ne, F as re } from "./DA6yh6E3HQLu.js";
2
+ import { G as xe } from "./DA6yh6E3HQLu.js";
3
+ import { getVisitorSource as ie } from "@analytics/visitor-source";
4
+ import oe from "get-user-locale";
5
+ import { Analytics as ae } from "analytics";
6
+ const se = 30, ce = 1e3, le = 3;
7
+ let S = [];
8
+ const de = (a, i, e) => {
9
+ const t = S[S.length - 1];
10
+ if (t && Math.abs(a - t.x) + Math.abs(i - t.y) < se && e - t.timestamp < ce) {
11
+ if (S.push({ x: a, y: i, timestamp: e }), S.length >= le)
12
+ return !0;
13
+ } else
14
+ S = [{ x: a, y: i, timestamp: e }];
15
+ return !1;
16
+ }, ue = (a) => {
17
+ let i = a, e = 0;
18
+ for (; i && e < 10; ) {
19
+ const t = i.getAttribute?.("href");
20
+ if (t)
21
+ return t;
22
+ i = i.parentElement, e++;
17
23
  }
18
- function identifyChromium() {
19
- const ua = navigator.userAgent;
20
- if (ua.match(/Chrome/)) {
21
- if (navigator.brave !== void 0) {
22
- return "Brave";
23
- }
24
- if (ua.match(/Edg/)) {
25
- return "Edge";
24
+ return null;
25
+ }, A = (a) => {
26
+ const i = a.closest("a[href]"), e = i?.getAttribute("href") || ue(a);
27
+ if (!e) return { type: null, href: null };
28
+ if (i?.hasAttribute("download"))
29
+ return { type: "download", href: e };
30
+ if (e.startsWith("tel:")) return { type: "tel", href: e };
31
+ if (e.startsWith("mailto:")) return { type: "mailto", href: e };
32
+ if (e.startsWith("#")) return { type: "anchor", href: e };
33
+ try {
34
+ return {
35
+ type: new URL(e, window.location.origin).origin === window.location.origin ? "internal" : "external",
36
+ href: e
37
+ };
38
+ } catch (t) {
39
+ return console.warn("[insights] Failed to classify link", E(t)), { type: "internal", href: e };
40
+ }
41
+ }, fe = (a) => {
42
+ if (typeof window > "u") return () => {
43
+ };
44
+ let i = 0, e = null;
45
+ const t = (s, o = !1) => {
46
+ const l = s.target, h = "clientX" in s ? s.clientX : s.changedTouches?.[0]?.clientX ?? 0, y = "clientY" in s ? s.clientY : s.changedTouches?.[0]?.clientY ?? 0;
47
+ let w = {
48
+ hostname: window.location.hostname,
49
+ pathname: window.location.pathname,
50
+ page_title: document.title,
51
+ url: window.location.href,
52
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
53
+ event_type: o ? "dblclick" : "click",
54
+ button_type: "button" in s ? s.button : 0
55
+ };
56
+ return l instanceof HTMLElement && (w.rage_click = de(h, y, Date.now()), w = {
57
+ ...w,
58
+ ...H(l)
59
+ }), w;
60
+ }, n = (s, o) => {
61
+ try {
62
+ if (Date.now() - i < 300) return;
63
+ const l = t(s);
64
+ a.track("page.click", {
65
+ ...l,
66
+ href: o.href || void 0,
67
+ link_type: o.type,
68
+ is_navigation: !0
69
+ });
70
+ } catch (l) {
71
+ console.error("[insights] Failed to track navigation click", E(l));
72
+ }
73
+ }, c = (s, o = !1) => {
74
+ try {
75
+ if (!(s.target instanceof HTMLElement) || Date.now() - i < 300) return;
76
+ const l = t(s, o), h = A(s.target);
77
+ a.track("page.click", {
78
+ ...l,
79
+ href: h.href || void 0,
80
+ link_type: h.type || void 0
81
+ });
82
+ } catch (l) {
83
+ console.error("[insights] Failed to track click", E(l));
84
+ }
85
+ }, r = (s) => {
86
+ if (!(s.target instanceof HTMLElement)) return;
87
+ const o = A(s.target);
88
+ if (o.type === "external" || o.type === "download") {
89
+ n(s, o);
90
+ return;
91
+ }
92
+ if (e) {
93
+ clearTimeout(e), e = null;
94
+ return;
95
+ }
96
+ e = setTimeout(() => {
97
+ c(s, !1), e = null;
98
+ }, 250);
99
+ }, f = (s) => {
100
+ e && (clearTimeout(e), e = null), c(s, !0);
101
+ }, u = (s) => {
102
+ try {
103
+ if (!(s.target instanceof HTMLElement)) return;
104
+ i = Date.now();
105
+ const o = A(s.target);
106
+ if (o.type === "external" || o.type === "download") {
107
+ const h = t(s);
108
+ a.track("page.click", {
109
+ ...h,
110
+ href: o.href || void 0,
111
+ link_type: o.type,
112
+ is_navigation: !0,
113
+ is_touch: !0
114
+ });
115
+ return;
26
116
  }
27
- if (ua.match(/OPR/)) {
28
- return "Opera";
117
+ const l = t(s);
118
+ a.track("page.click", {
119
+ ...l,
120
+ href: o.href || void 0,
121
+ link_type: o.type || void 0,
122
+ is_touch: !0
123
+ });
124
+ } catch (o) {
125
+ console.error("[insights] Failed to track touch", E(o));
126
+ }
127
+ };
128
+ return document.addEventListener("click", r), document.addEventListener("dblclick", f), document.addEventListener("touchend", u, { passive: !0 }), () => {
129
+ document.removeEventListener("click", r), document.removeEventListener("dblclick", f), document.removeEventListener("touchend", u), e && clearTimeout(e);
130
+ };
131
+ }, pe = (a) => {
132
+ if (typeof window > "u") return () => {
133
+ };
134
+ const i = (n) => (c) => {
135
+ try {
136
+ const r = R(c, n);
137
+ r && a.track("page.copy", r);
138
+ } catch (r) {
139
+ console.error(`[insights] Failed to track clipboard ${n}`, E(r));
140
+ }
141
+ }, e = i("copy"), t = i("cut");
142
+ return document.addEventListener("copy", e, { capture: !0 }), document.addEventListener("cut", t, { capture: !0 }), () => {
143
+ document.removeEventListener("copy", e, { capture: !0 }), document.removeEventListener("cut", t, { capture: !0 });
144
+ };
145
+ }, ge = (a) => {
146
+ if (typeof window > "u") return () => {
147
+ };
148
+ const i = (n) => (c) => {
149
+ try {
150
+ const r = R(c, n);
151
+ if (r) {
152
+ const f = n === "submit" ? "form.submit" : "form.change";
153
+ a.track(f, r);
29
154
  }
30
- return "Chrome";
155
+ } catch (r) {
156
+ console.error(`[insights] Failed to track form ${n}`, E(r));
31
157
  }
32
- return "Chromium";
158
+ }, e = i("change"), t = i("submit");
159
+ return document.addEventListener("change", e, { capture: !0 }), document.addEventListener("submit", t, { capture: !0 }), () => {
160
+ document.removeEventListener("change", e, { capture: !0 }), document.removeEventListener("submit", t, { capture: !0 });
161
+ };
162
+ }, me = (a) => {
163
+ if (typeof window > "u") return () => {
164
+ };
165
+ const { page_id: i } = U.get();
166
+ let e = i || "", t = typeof window < "u" ? window.location.href : "";
167
+ const n = () => {
168
+ const { isIdentified: c } = q.get();
169
+ if (c)
170
+ try {
171
+ a.page({
172
+ resource: "page"
173
+ });
174
+ } catch (r) {
175
+ console.error("[insights] Failed to send page view", E(r));
176
+ }
177
+ };
178
+ if (i) {
179
+ const c = U.listen((r, f, u) => {
180
+ const { page_id: s } = r;
181
+ u === "page_id" && s && e !== s && (e = s, n());
182
+ });
183
+ return () => {
184
+ c();
185
+ };
186
+ } else {
187
+ const c = () => {
188
+ if (typeof window > "u") return;
189
+ const u = window.location.href;
190
+ u !== t && (t = u, n());
191
+ };
192
+ window.addEventListener("popstate", c);
193
+ const r = window.history.pushState, f = window.history.replaceState;
194
+ return window.history.pushState = function(...u) {
195
+ r.apply(this, u), c();
196
+ }, window.history.replaceState = function(...u) {
197
+ f.apply(this, u), c();
198
+ }, () => {
199
+ window.removeEventListener("popstate", c), window.history.pushState = r, window.history.replaceState = f;
200
+ };
201
+ }
202
+ }, F = async (a, i) => {
203
+ let e = null;
204
+ try {
205
+ const t = new AbortController();
206
+ return e = setTimeout(() => t.abort(), i), (await fetch(a, {
207
+ method: "GET",
208
+ signal: t.signal,
209
+ credentials: "omit"
210
+ })).ok;
211
+ } catch {
212
+ return !1;
213
+ } finally {
214
+ e && clearTimeout(e);
215
+ }
216
+ }, he = async (a) => {
217
+ const {
218
+ mode: i = "auto",
219
+ insightsUrl: e,
220
+ directInsightsUrl: t,
221
+ pingTimeout: n = 3e3,
222
+ debug: c = !1
223
+ } = a;
224
+ if (c)
225
+ return { mode: "proxy", url: e || "/debug-api" };
226
+ const r = Number.isFinite(n) && n >= 0 ? n : 3e3;
227
+ return i === "direct" ? { mode: "direct", url: t ?? null } : i === "proxy" || !t ? { mode: "proxy", url: e ?? null } : (e ? await F(`${e}/ping`, r) : !1) ? { mode: "proxy", url: e ?? null } : await F(`${t}/ping`, r) ? { mode: "direct", url: t } : { mode: "proxy", url: e ?? null };
228
+ }, we = () => {
229
+ if (typeof document > "u")
230
+ return {};
231
+ const {
232
+ type: a,
233
+ referrer: i,
234
+ data: e = {}
235
+ } = ie({
236
+ referrer: document.referrer,
237
+ currentUrl: window.location.href,
238
+ mapper: null
239
+ }), t = Object.keys(e);
240
+ for (const n of t)
241
+ e[n] && Array.isArray(e[n]) && e[n].length > 0 && (e[n] = e[n]?.[0]);
242
+ return { type: a, referrer: i, data: e, raw: document.referrer };
243
+ }, ve = () => new Promise((a, i) => {
244
+ let e = "Unknown";
245
+ function t(d) {
246
+ a({
247
+ isPrivate: d,
248
+ browserName: e
249
+ });
250
+ }
251
+ function n() {
252
+ const d = navigator.userAgent;
253
+ return d.match(/Chrome/) ? navigator.brave !== void 0 ? "Brave" : d.match(/Edg/) ? "Edge" : d.match(/OPR/) ? "Opera" : "Chrome" : "Chromium";
33
254
  }
34
- function assertEvalToString(value) {
35
- return value === eval.toString().length;
255
+ function c(d) {
256
+ return d === eval.toString().length;
36
257
  }
37
- function isSafari() {
38
- const v = navigator.vendor;
39
- return v !== void 0 && v.indexOf("Apple") === 0 && assertEvalToString(37);
258
+ function r() {
259
+ const d = navigator.vendor;
260
+ return d !== void 0 && d.indexOf("Apple") === 0 && c(37);
40
261
  }
41
- function isChrome() {
42
- const v = navigator.vendor;
43
- return v !== void 0 && v.indexOf("Google") === 0 && assertEvalToString(33);
262
+ function f() {
263
+ const d = navigator.vendor;
264
+ return d !== void 0 && d.indexOf("Google") === 0 && c(33);
44
265
  }
45
- function isFirefox() {
46
- return document.documentElement !== void 0 && document.documentElement.style.MozAppearance !== void 0 && assertEvalToString(37);
266
+ function u() {
267
+ return document.documentElement !== void 0 && document.documentElement.style.MozAppearance !== void 0 && c(37);
47
268
  }
48
- function isMSIE() {
49
- return navigator.msSaveBlob !== void 0 && assertEvalToString(39);
269
+ function s() {
270
+ return navigator.msSaveBlob !== void 0 && c(39);
50
271
  }
51
- function newSafariTest() {
52
- const tmp_name = String(Math.random());
272
+ function o() {
273
+ const d = String(Math.random());
53
274
  try {
54
- const db = window.indexedDB.open(tmp_name, 1);
55
- db.onupgradeneeded = (i) => {
56
- const res = i.target?.result;
275
+ const g = window.indexedDB.open(d, 1);
276
+ g.onupgradeneeded = (b) => {
277
+ const v = b.target?.result;
57
278
  try {
58
- res.createObjectStore("test", {
59
- autoIncrement: true
60
- }).put(new Blob());
61
- __callback(false);
62
- } catch (e) {
63
- let message = e;
64
- if (e instanceof Error) {
65
- message = e.message ?? e;
66
- }
67
- if (typeof message !== "string") {
68
- return __callback(false);
69
- }
70
- const matchesExpectedError = /BlobURLs are not yet supported/.test(message);
71
- return __callback(matchesExpectedError);
279
+ v.createObjectStore("test", {
280
+ autoIncrement: !0
281
+ }).put(new Blob()), t(!1);
282
+ } catch (k) {
283
+ let M = k;
284
+ if (k instanceof Error && (M = k.message ?? k), typeof M != "string")
285
+ return t(!1);
286
+ const O = /BlobURLs are not yet supported/.test(M);
287
+ return t(O);
72
288
  } finally {
73
- res.close();
74
- window.indexedDB.deleteDatabase(tmp_name);
289
+ v.close(), window.indexedDB.deleteDatabase(d);
75
290
  }
76
291
  };
77
- } catch (e) {
78
- return __callback(false);
292
+ } catch {
293
+ return t(!1);
79
294
  }
80
295
  }
81
- function oldSafariTest() {
82
- const openDB = window.openDatabase;
83
- const storage = window.localStorage;
296
+ function l() {
297
+ const d = window.openDatabase, g = window.localStorage;
84
298
  try {
85
- openDB(null, null, null, null);
86
- } catch (e) {
87
- return __callback(true);
299
+ d(null, null, null, null);
300
+ } catch {
301
+ return t(!0);
88
302
  }
89
303
  try {
90
- storage.setItem("test", "1");
91
- storage.removeItem("test");
92
- } catch (e) {
93
- return __callback(true);
304
+ g.setItem("test", "1"), g.removeItem("test");
305
+ } catch {
306
+ return t(!0);
94
307
  }
95
- return __callback(false);
308
+ return t(!1);
96
309
  }
97
- function safariPrivateTest() {
98
- if (navigator.maxTouchPoints !== void 0) {
99
- newSafariTest();
100
- } else {
101
- oldSafariTest();
102
- }
310
+ function h() {
311
+ navigator.maxTouchPoints !== void 0 ? o() : l();
103
312
  }
104
- function getQuotaLimit() {
105
- const w = window;
106
- if (w.performance !== void 0 && w.performance.memory !== void 0 && w.performance.memory.jsHeapSizeLimit !== void 0) {
107
- return performance.memory.jsHeapSizeLimit;
108
- }
109
- return 1073741824;
313
+ function y() {
314
+ const d = window;
315
+ return d.performance !== void 0 && d.performance.memory !== void 0 && d.performance.memory.jsHeapSizeLimit !== void 0 ? performance.memory.jsHeapSizeLimit : 1073741824;
110
316
  }
111
- function storageQuotaChromePrivateTest() {
317
+ function w() {
112
318
  navigator.webkitTemporaryStorage.queryUsageAndQuota(
113
- (_, quota) => {
114
- const quotaInMib = Math.round(quota / (1024 * 1024));
115
- const quotaLimitInMib = Math.round(getQuotaLimit() / (1024 * 1024)) * 2;
116
- __callback(quotaInMib < quotaLimitInMib);
319
+ (d, g) => {
320
+ const b = Math.round(g / 1048576), v = Math.round(y() / (1024 * 1024)) * 2;
321
+ t(b < v);
117
322
  },
118
- (e) => {
119
- reject(new Error(`detectIncognito somehow failed to query storage quota: ${e.message}`));
323
+ (d) => {
324
+ i(new Error(`detectIncognito somehow failed to query storage quota: ${d.message}`));
120
325
  }
121
326
  );
122
327
  }
123
- function oldChromePrivateTest() {
124
- const fs = window.webkitRequestFileSystem;
125
- const success = () => {
126
- __callback(false);
127
- };
128
- const error = () => {
129
- __callback(true);
130
- };
131
- fs(0, 1, success, error);
328
+ function p() {
329
+ const d = window.webkitRequestFileSystem;
330
+ d(0, 1, () => {
331
+ t(!1);
332
+ }, () => {
333
+ t(!0);
334
+ });
132
335
  }
133
- function chromePrivateTest() {
134
- if (self.Promise !== void 0 && self.Promise.allSettled !== void 0) {
135
- storageQuotaChromePrivateTest();
136
- } else {
137
- oldChromePrivateTest();
138
- }
336
+ function I() {
337
+ self.Promise !== void 0 && self.Promise.allSettled !== void 0 ? w() : p();
139
338
  }
140
- function firefoxPrivateTest() {
141
- __callback(navigator.serviceWorker === void 0);
339
+ function D() {
340
+ t(navigator.serviceWorker === void 0);
142
341
  }
143
- function msiePrivateTest() {
144
- __callback(window.indexedDB === void 0);
342
+ function B() {
343
+ t(window.indexedDB === void 0);
145
344
  }
146
- function main() {
147
- if (isSafari()) {
148
- browserName = "Safari";
149
- safariPrivateTest();
150
- } else if (isChrome()) {
151
- browserName = identifyChromium();
152
- chromePrivateTest();
153
- } else if (isFirefox()) {
154
- browserName = "Firefox";
155
- firefoxPrivateTest();
156
- } else if (isMSIE()) {
157
- browserName = "Internet Explorer";
158
- msiePrivateTest();
159
- } else {
160
- reject(new Error("detectIncognito cannot determine the browser"));
161
- }
345
+ function L() {
346
+ r() ? (e = "Safari", h()) : f() ? (e = n(), I()) : u() ? (e = "Firefox", D()) : s() ? (e = "Internet Explorer", B()) : i(new Error("detectIncognito cannot determine the browser"));
162
347
  }
163
- main();
164
- });
165
- var isIncognito_default = isIncognito;
166
-
167
- // src/utils/getUserProperties.ts
168
- var getGPUInfo = () => {
169
- const canvas = document.createElement("canvas");
170
- const gl = canvas?.getContext("webgl") || canvas?.getContext("experimental-webgl");
171
- if (!gl) {
348
+ L();
349
+ }), ye = () => {
350
+ const a = document.createElement("canvas"), i = a?.getContext("webgl") || a?.getContext("experimental-webgl");
351
+ if (!i)
172
352
  return null;
173
- }
174
- const debugInfo = gl?.getExtension("WEBGL_debug_renderer_info");
175
- return debugInfo ? gl?.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : null;
176
- };
177
- var getUserProperties = async () => {
178
- let locale = "";
179
- let timezone = "";
180
- let dark_mode = false;
181
- let private_mode = false;
182
- const properties = {};
183
- if (!window) {
353
+ const e = i?.getExtension("WEBGL_debug_renderer_info");
354
+ return e ? i?.getParameter(e.UNMASKED_RENDERER_WEBGL) : null;
355
+ }, ke = async () => {
356
+ let a = "", i = "", e = !1, t = !1;
357
+ const n = {};
358
+ if (!window)
184
359
  return {
185
- locale,
186
- timezone,
187
- dark_mode,
188
- private_mode,
189
- properties
360
+ locale: a,
361
+ timezone: i,
362
+ dark_mode: e,
363
+ private_mode: t,
364
+ properties: n
190
365
  };
191
- }
192
366
  try {
193
- ({ isPrivate: private_mode } = await isIncognito_default());
194
- } catch (e) {
195
- console.error(e);
367
+ ({ isPrivate: t } = await ve());
368
+ } catch (r) {
369
+ console.error(r);
196
370
  }
197
371
  try {
198
- dark_mode = window.matchMedia?.("(prefers-color-scheme: dark)").matches;
199
- } catch (e) {
200
- console.error(e);
372
+ e = window.matchMedia?.("(prefers-color-scheme: dark)").matches;
373
+ } catch (r) {
374
+ console.error(r);
201
375
  }
202
376
  try {
203
- timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
204
- } catch (e) {
205
- console.error(e);
377
+ i = Intl.DateTimeFormat().resolvedOptions().timeZone;
378
+ } catch (r) {
379
+ console.error(r);
206
380
  }
207
381
  try {
208
- locale = getUserLocale();
209
- } catch (e) {
210
- console.error(e);
382
+ a = oe();
383
+ } catch (r) {
384
+ console.error(r);
211
385
  }
212
386
  if (navigator && "connection" in navigator) {
213
- const connection = navigator.connection || navigator?.mozConnection || navigator.webkitConnection;
214
- properties.network_type = connection.effectiveType;
387
+ const r = navigator.connection || navigator?.mozConnection || navigator.webkitConnection;
388
+ n.network_type = r.effectiveType;
215
389
  }
216
- properties.width = window?.innerWidth;
217
- properties.height = window?.innerHeight;
218
- properties.pixel_ratio = window?.devicePixelRatio;
219
- if (screen) {
220
- properties.screen_width = screen?.width;
221
- properties.screen_height = screen?.height;
222
- properties.color_depth = screen?.colorDepth;
223
- properties.orientation = screen?.orientation?.type;
224
- }
225
- if (navigator && "getBattery" in navigator) {
226
- const battery = await navigator?.getBattery();
227
- if (battery) {
228
- properties.battery_level = Number((battery?.level * 100).toFixed(0));
229
- properties.battery_charging = battery?.charging;
230
- properties.battery_discharging_time = battery?.dischargingTime;
231
- properties.battery_full_charge_capacity = battery?.fullChargeCapacity;
232
- properties.battery_charging_time = battery?.chargingTime;
233
- }
390
+ if (n.width = window?.innerWidth, n.height = window?.innerHeight, n.pixel_ratio = window?.devicePixelRatio, screen && (n.screen_width = screen?.width, n.screen_height = screen?.height, n.color_depth = screen?.colorDepth, n.orientation = screen?.orientation?.type), navigator && "getBattery" in navigator) {
391
+ const r = await navigator?.getBattery();
392
+ r && (n.battery_level = Number((r?.level * 100).toFixed(0)), n.battery_charging = r?.charging, n.battery_discharging_time = r?.dischargingTime, n.battery_full_charge_capacity = r?.fullChargeCapacity, n.battery_charging_time = r?.chargingTime);
234
393
  }
235
- if (navigator && "deviceMemory" in navigator) {
236
- properties.device_memory = navigator?.deviceMemory;
394
+ navigator && "deviceMemory" in navigator && (n.device_memory = navigator?.deviceMemory);
395
+ const c = "ontouchstart" in window || typeof navigator?.maxTouchPoints < "u" && navigator?.maxTouchPoints > 0;
396
+ if (n.touch_supported = c, "hardwareConcurrency" in navigator && (n.hardware_concurrency = navigator?.hardwareConcurrency), navigator && "mediaDevices" in navigator) {
397
+ const f = await navigator?.mediaDevices?.enumerateDevices(), u = f.some((o) => o?.kind === "videoinput");
398
+ n.camera_supported = u;
399
+ const s = f.some((o) => o?.kind === "audioinput");
400
+ n.microphone_supported = s;
237
401
  }
238
- const supportsTouch = "ontouchstart" in window || typeof navigator?.maxTouchPoints !== "undefined" && navigator?.maxTouchPoints > 0;
239
- properties.touch_supported = supportsTouch;
240
- if ("hardwareConcurrency" in navigator) {
241
- properties.hardware_concurrency = navigator?.hardwareConcurrency;
242
- }
243
- if (navigator && "mediaDevices" in navigator) {
244
- const mediaDevices = navigator?.mediaDevices;
245
- const devices = await mediaDevices?.enumerateDevices();
246
- const hasCamera = devices.some((device) => device?.kind === "videoinput");
247
- properties.camera_supported = hasCamera;
248
- const hasMicrophone = devices.some((device) => device?.kind === "audioinput");
249
- properties.microphone_supported = hasMicrophone;
250
- }
251
- properties.gpu = getGPUInfo();
252
- return {
253
- locale,
254
- timezone,
255
- dark_mode,
256
- private_mode,
257
- properties
258
- };
259
- };
260
- var AnalyticsEventsList = [
261
- // Bevy Events
262
- "bevy.collection.filled",
263
- "bevy.collection.submitted",
264
- "bevy.collection.view",
265
- // Block Events
266
- "block.view",
267
- // Blog Events
268
- "blog.post.view",
269
- // Event Events
270
- "event.booking.confirmed",
271
- "event.booking.initiated",
272
- "event.booking.pending",
273
- "event.coupon.applied",
274
- "event.event.view",
275
- // Form Events
276
- "form.change",
277
- "form.submit",
278
- // Membership Events
279
- "membership.account.signin",
280
- "membership.account.signout",
281
- "membership.account.signup",
282
- // UI Events
283
- "button.click",
284
- "page.bounce",
285
- "page.click",
286
- "page.copy",
287
- "page.impression",
288
- "page.scroll",
289
- "page.view",
290
- "page.selection",
291
- // User Activity Events
292
- "user.active",
293
- "user.idle"
294
- ];
295
- var AnalyticsEvents = listToRecord(AnalyticsEventsList);
296
-
297
- // src/index.ts
298
- var BATCH_SIZE = 50;
299
- var BATCH_INTERVAL_MS = 5e3;
300
- var MAX_EVENTS_PER_PAGE = 500;
301
- var BROADCAST_CHANNEL_NAME = "levo_analytics";
302
- var createLevoInsights = (client, options = {}) => {
303
- const { workspace, fetch: levoFetch, NODE_ENV } = client;
304
- const {
305
- insightsMode = "auto",
306
- insightsUrl = "",
307
- directInsightsUrl = "",
308
- pingTimeout: providedPingTimeout = 3e3,
309
- site
310
- } = options;
311
- const pingTimeout = Number.isFinite(providedPingTimeout) && providedPingTimeout >= 0 ? providedPingTimeout : 3e3;
312
- const _IDLE_THROTTLE = 1 * 1e3;
313
- const _IDLE_TIMEOUT = 60 * 1e3;
314
- let instance = null;
315
- let idle_listener = null;
316
- const storage_keys = {
317
- /** Legacy: Lock key for identify operation (no longer used internally) */
318
- identify: "lock:lv_aud_identify",
319
- /** Key for tracking open tab count */
320
- open_tabs: "lv_insights_ot",
321
- /** Key for current tab ID (sessionStorage) */
322
- current_tab: "lv_insights_ct",
323
- /** Key for all tabs registry */
324
- all_tabs: "lv_insights_at",
325
- /** Device JWT for direct mode (1yr expiry, rotated on /welcome, analytics-only scope) */
326
- device_token: "lv_aud_device",
327
- /** Session JWT for direct mode (1yr expiry, rotated on /welcome, analytics-only scope) */
328
- session_token: "lv_aud_session",
329
- /** Cached insights mode ('proxy' | 'direct') */
330
- mode: "lv_aud_mode"
331
- };
332
- let _insightsMode = "proxy";
333
- let _insightsBaseUrl = "";
334
- let _localStorageAvailable = true;
335
- const _memoryStorage = /* @__PURE__ */ new Map();
336
- const checkLocalStorageAvailable = () => {
337
- if (typeof window === "undefined" || !("localStorage" in window)) {
338
- return false;
339
- }
340
- try {
341
- const testKey = "__levo_test__";
342
- window.localStorage.setItem(testKey, "test");
343
- window.localStorage.removeItem(testKey);
344
- return true;
345
- } catch {
346
- return false;
347
- }
348
- };
349
- const getFromStorage = (key) => {
350
- if (typeof window === "undefined") {
351
- return _memoryStorage.get(key) || null;
352
- }
353
- if (_localStorageAvailable) {
354
- try {
355
- return window.localStorage.getItem(key);
356
- } catch {
357
- return _memoryStorage.get(key) || null;
358
- }
359
- }
360
- return _memoryStorage.get(key) || null;
361
- };
362
- const setInStorage = (key, value) => {
363
- if (typeof window === "undefined") {
364
- _memoryStorage.set(key, value);
365
- return;
366
- }
367
- if (_localStorageAvailable) {
368
- try {
369
- window.localStorage.setItem(key, value);
370
- return;
371
- } catch {
372
- }
373
- }
374
- _memoryStorage.set(key, value);
375
- };
376
- const removeFromStorage = (key) => {
377
- _memoryStorage.delete(key);
378
- if (typeof window === "undefined" || !_localStorageAvailable) {
379
- return;
380
- }
381
- try {
382
- window.localStorage.removeItem(key);
383
- } catch {
384
- }
402
+ return n.gpu = ye(), {
403
+ locale: a,
404
+ timezone: i,
405
+ dark_mode: e,
406
+ private_mode: t,
407
+ properties: n
385
408
  };
386
- const getAuthHeaders = () => {
387
- const headers = {};
388
- if (_insightsMode === "direct") {
389
- const deviceToken = getFromStorage(storage_keys.device_token);
390
- const sessionToken = getFromStorage(storage_keys.session_token);
391
- if (deviceToken) headers["Levo-Audience-Device"] = deviceToken;
392
- if (sessionToken) headers["Levo-Audience-Session"] = sessionToken;
393
- }
394
- return headers;
395
- };
396
- const pingEndpoint = async (url, timeout) => {
397
- let timeoutId = null;
398
- try {
399
- const controller = new AbortController();
400
- timeoutId = setTimeout(() => controller.abort(), timeout);
401
- const response = await fetch(url, {
402
- method: "GET",
403
- signal: controller.signal,
404
- credentials: "omit"
409
+ }, be = (a, i, e) => {
410
+ const { debug: t } = P();
411
+ if (!a)
412
+ return null;
413
+ const n = {
414
+ name: a.workspace,
415
+ /**
416
+ * Handle track events (custom events like clicks, scrolls, etc.)
417
+ *
418
+ * Enriches the event with:
419
+ * - Session/device IDs
420
+ * - Page metadata (URL, title, etc.)
421
+ * - Tab information
422
+ * - Timestamp
423
+ *
424
+ * Then queues for batched sending.
425
+ */
426
+ track: ({ payload: c }) => {
427
+ if (!e()) return;
428
+ const r = c?.properties || {};
429
+ i(c.event, r);
430
+ },
431
+ /**
432
+ * Handle page view events.
433
+ *
434
+ * Called when navigating to a new page (in SPAs) or on initial load.
435
+ * Resets the rate limit counter and tracks a page.view event.
436
+ */
437
+ page: ({ payload: c }) => {
438
+ if (!e()) return;
439
+ $();
440
+ const r = c?.properties || {};
441
+ i("page.view", {
442
+ ...r,
443
+ resource: "page"
405
444
  });
406
- return response.ok;
407
- } catch {
408
- return false;
409
- } finally {
410
- if (timeoutId) {
411
- clearTimeout(timeoutId);
412
- }
413
- }
414
- };
415
- const detectInsightsMode = async () => {
416
- if (insightsMode === "proxy") {
417
- _insightsBaseUrl = insightsUrl;
418
- return "proxy";
419
- }
420
- if (insightsMode === "direct") {
421
- if (!directInsightsUrl) {
422
- console.warn(
423
- "[@levo-so/core] insightsMode='direct' but directInsightsUrl not set. Falling back to proxy."
424
- );
425
- _insightsBaseUrl = insightsUrl;
426
- return "proxy";
427
- }
428
- _insightsBaseUrl = directInsightsUrl;
429
- setInStorage(storage_keys.mode, "direct");
430
- return "direct";
431
- }
432
- const cachedMode = getFromStorage(storage_keys.mode);
433
- if (cachedMode === "proxy" || cachedMode === "direct") {
434
- if (cachedMode === "direct" && !directInsightsUrl) {
435
- removeFromStorage(storage_keys.mode);
436
- _insightsBaseUrl = "";
437
- } else {
438
- const pingUrl = cachedMode === "proxy" ? `${insightsUrl}/ping` : `${directInsightsUrl}/ping`;
439
- if (await pingEndpoint(pingUrl, pingTimeout)) {
440
- _insightsBaseUrl = cachedMode === "proxy" ? insightsUrl : directInsightsUrl;
441
- return cachedMode;
442
- }
443
- }
444
- }
445
- if (await pingEndpoint(`${insightsUrl}/ping`, pingTimeout)) {
446
- setInStorage(storage_keys.mode, "proxy");
447
- _insightsBaseUrl = insightsUrl;
448
- return "proxy";
449
- }
450
- if (directInsightsUrl) {
451
- setInStorage(storage_keys.mode, "direct");
452
- _insightsBaseUrl = directInsightsUrl;
453
- return "direct";
454
- }
455
- _insightsBaseUrl = insightsUrl;
456
- return "proxy";
457
- };
458
- let _session = "";
459
- let _device = "";
460
- let is_identified = false;
461
- let _eventCount = 0;
462
- let _eventQueue = [];
463
- let _flushTimer = null;
464
- let _visibilityHandler = null;
465
- let _beforeUnloadHandler = null;
466
- let _pageHideHandler = null;
467
- let _channel = null;
468
- let _pendingIdentify = false;
469
- const buildSessionSyncMessage = () => {
470
- const message = {
471
- type: "SESSION_SYNC",
472
- session: _session,
473
- device: _device
474
- };
475
- if (_insightsMode === "direct") {
476
- const storedDeviceToken = getFromStorage(storage_keys.device_token);
477
- const storedSessionToken = getFromStorage(storage_keys.session_token);
478
- if (storedDeviceToken) message.deviceToken = storedDeviceToken;
479
- if (storedSessionToken) message.sessionToken = storedSessionToken;
480
- }
481
- return message;
482
- };
483
- const initBroadcastChannel = () => {
484
- if (typeof BroadcastChannel === "undefined") {
485
- return;
486
- }
487
- try {
488
- _channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
489
- _channel.onmessage = (event) => {
490
- const { type, session, device, deviceToken, sessionToken } = event.data;
491
- if (type === "SESSION_SYNC" && !is_identified) {
492
- _session = session;
493
- _device = device;
494
- is_identified = true;
495
- _pendingIdentify = false;
496
- if (_insightsMode === "direct" && deviceToken && sessionToken) {
497
- setInStorage(storage_keys.device_token, deviceToken);
498
- setInStorage(storage_keys.session_token, sessionToken);
445
+ },
446
+ /**
447
+ * Handle identify events (session establishment).
448
+ *
449
+ * This is called when identify() is invoked. It:
450
+ * 1. Reads identity from session store (set by init())
451
+ * 2. Checks if already identified (via BroadcastChannel) - if so, skip
452
+ * 3. Calls /welcome API to get session/device IDs
453
+ * 4. On success, broadcasts session to other tabs
454
+ *
455
+ * The /welcome API uses HttpOnly cookies to maintain persistent
456
+ * device identity across sessions.
457
+ */
458
+ identify: ({ payload: c }) => {
459
+ if (!e()) return;
460
+ const { workspace: r, site_id: f, debug: u } = P(), {
461
+ mode: s,
462
+ isIdentified: o,
463
+ isIdentifying: l,
464
+ sessionToken: h,
465
+ deviceToken: y,
466
+ insightsBaseUrl: w,
467
+ identity: p
468
+ } = C(), I = z(), D = C().allTabs.length;
469
+ if (!w || !p)
470
+ return;
471
+ const L = {
472
+ ...{
473
+ private_mode: c.traits?.private_mode ?? p.privateMode ?? !1,
474
+ dark_mode: c.traits?.dark_mode ?? p.darkMode ?? !1,
475
+ timezone: c.traits?.timezone ?? p.timezone ?? "",
476
+ locale: c.traits?.locale ?? p.locale ?? "",
477
+ referrer: c.traits?.referrer ?? p.referrer ?? { type: "", referrer: {}, data: {} },
478
+ properties: {
479
+ ...p.properties
499
480
  }
500
- }
501
- if (type === "SESSION_REQUEST" && is_identified) {
502
- _channel?.postMessage(buildSessionSyncMessage());
503
- }
481
+ },
482
+ workspace_id: r ?? "",
483
+ site_id: f ?? void 0,
484
+ hostname: window.location.hostname,
485
+ pathname: window.location.pathname,
486
+ page_title: document.title,
487
+ url: window.location.href,
488
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
489
+ tab_id: I ?? "",
490
+ tab_count: D ?? 0
504
491
  };
505
- } catch {
506
- }
507
- };
508
- const requestSessionFromOtherTabs = () => {
509
- _channel?.postMessage({ type: "SESSION_REQUEST" });
510
- };
511
- const broadcastSession = () => {
512
- _channel?.postMessage(buildSessionSyncMessage());
513
- };
514
- const flushEventQueue = () => {
515
- const baseUrl = _insightsBaseUrl || insightsUrl;
516
- if (_eventQueue.length === 0 || !baseUrl || !workspace) {
517
- return;
518
- }
519
- const batch = _eventQueue.map((event) => ({
520
- ...event,
521
- session_id: event.session_id || _session,
522
- device_id: event.device_id || _device
523
- }));
524
- _eventQueue = [];
525
- const url = `${baseUrl}/v1/insights/event/bulk?workspace=${workspace}`;
526
- const headers = {
527
- "Content-Type": "application/json",
528
- ...getAuthHeaders()
529
- };
530
- if (_insightsMode === "proxy" && navigator.sendBeacon) {
531
- const success = navigator.sendBeacon(url, JSON.stringify(batch));
532
- if (success) {
492
+ if (u && console.log("[@levo-so/insights] Identification triggered:", {
493
+ isIdentified: o,
494
+ isIdentifying: l,
495
+ welcomeInput: L
496
+ }), o || l)
497
+ return;
498
+ if (T({ isIdentifying: !0 }), u) {
499
+ console.log("[@levo-so/insights] Debug mode enabled. Mocking session identification."), T({
500
+ isIdentified: !0,
501
+ isIdentifying: !1,
502
+ sessionId: "debug-session",
503
+ deviceId: "debug-device"
504
+ });
533
505
  return;
534
506
  }
535
- }
536
- fetch(url, {
537
- method: "POST",
538
- body: JSON.stringify(batch),
539
- headers,
540
- keepalive: true,
541
- credentials: _insightsMode === "proxy" ? "include" : "omit"
542
- }).catch(() => {
543
- });
544
- };
545
- const startFlushTimer = () => {
546
- if (_flushTimer) {
547
- return;
548
- }
549
- _flushTimer = setInterval(() => {
550
- flushEventQueue();
551
- }, BATCH_INTERVAL_MS);
552
- if (typeof document !== "undefined") {
553
- _visibilityHandler = () => {
554
- if (document.visibilityState === "hidden") {
555
- flushEventQueue();
556
- }
507
+ const d = {
508
+ Accept: "application/json",
509
+ "Content-Type": "application/json",
510
+ ...G({ sessionToken: h, deviceToken: y, mode: s })
557
511
  };
558
- _beforeUnloadHandler = () => {
559
- flushEventQueue();
560
- };
561
- _pageHideHandler = () => {
562
- flushEventQueue();
563
- };
564
- document.addEventListener("visibilitychange", _visibilityHandler);
565
- window.addEventListener("beforeunload", _beforeUnloadHandler);
566
- window.addEventListener("pagehide", _pageHideHandler);
567
- }
568
- };
569
- const stopFlushTimer = () => {
570
- if (_flushTimer) {
571
- clearInterval(_flushTimer);
572
- _flushTimer = null;
573
- }
574
- if (typeof document !== "undefined") {
575
- if (_visibilityHandler) {
576
- document.removeEventListener("visibilitychange", _visibilityHandler);
577
- _visibilityHandler = null;
578
- }
579
- if (_beforeUnloadHandler) {
580
- window.removeEventListener("beforeunload", _beforeUnloadHandler);
581
- _beforeUnloadHandler = null;
582
- }
583
- if (_pageHideHandler) {
584
- window.removeEventListener("pagehide", _pageHideHandler);
585
- _pageHideHandler = null;
586
- }
587
- }
588
- };
589
- const queueEvent = (collectInput) => {
590
- if (_eventCount >= MAX_EVENTS_PER_PAGE) {
591
- console.warn(`[@levo-so/core] Event limit (${MAX_EVENTS_PER_PAGE}) reached for this page`);
592
- return;
593
- }
594
- _eventCount++;
595
- _eventQueue.push(collectInput);
596
- if (_eventQueue.length >= BATCH_SIZE) {
597
- flushEventQueue();
598
- }
599
- };
600
- const getReferrer = () => {
601
- if (typeof document === "undefined") {
602
- return {};
603
- }
604
- const {
605
- type,
606
- referrer,
607
- data = {}
608
- } = getVisitorSource({
609
- referrer: document.referrer,
610
- currentUrl: window.location.href,
611
- mapper: null
612
- });
613
- const dataKeys = Object.keys(data);
614
- for (const key of dataKeys) {
615
- if (data[key] && Array.isArray(data[key]) && data[key].length > 0) {
616
- data[key] = data[key]?.[0];
617
- }
618
- }
619
- return { type, referrer, data, raw: document.referrer };
620
- };
621
- const getCurrentTabId = () => {
622
- try {
623
- if (typeof window === "undefined" || !("sessionStorage" in window)) {
624
- return "";
625
- }
626
- return window.sessionStorage.getItem(storage_keys.current_tab) || "";
627
- } catch {
628
- return "";
629
- }
630
- };
631
- const getTabCount = () => {
632
- try {
633
- if (typeof window === "undefined" || !("localStorage" in window)) {
634
- return 0;
635
- }
636
- return Number(window.localStorage.getItem(storage_keys.open_tabs)) || 0;
637
- } catch {
638
- return 0;
639
- }
640
- };
641
- const getActivityStatus = () => {
642
- const status = idle_listener?.getStatus();
643
- if (!status) {
644
- return null;
645
- }
646
- return {
647
- status: status.isIdle ? "idle" : "active",
648
- seconds: status.isIdle ? status.idle : status.active
649
- };
650
- };
651
- const identify = async (options2) => {
652
- if (!instance) {
653
- return;
654
- }
655
- const withLock = options2?.withLock ?? true;
656
- const referrer = getReferrer();
657
- const traits = await getUserProperties();
658
- await instance.identify("", {
659
- ...traits,
660
- referrer,
661
- withLock
662
- });
663
- };
664
- const request = async (url, data) => {
665
- const headers = {
666
- Accept: "application/json",
667
- "Content-Type": "application/json",
668
- ...getAuthHeaders()
669
- };
670
- try {
671
- const response = await levoFetch.url(_insightsBaseUrl || insightsUrl, true).url(url).options({
672
- keepAlive: true,
673
- credentials: _insightsMode === "proxy" ? "include" : "omit"
674
- }).headers(headers).post(data).json();
675
- return response;
676
- } catch (error) {
677
- console.warn(`Analytics request failed for ${url}:`, error);
678
- return null;
679
- }
680
- };
681
- const initiate = async (properties) => {
682
- if (NODE_ENV === "development") {
683
- return;
684
- }
685
- _eventCount = 0;
686
- _localStorageAvailable = checkLocalStorageAvailable();
687
- _insightsMode = await detectInsightsMode();
688
- initBroadcastChannel();
689
- requestSessionFromOtherTabs();
690
- startFlushTimer();
691
- await new Promise((resolve) => setTimeout(resolve, 50));
692
- const plugin = {
693
- name: workspace,
694
- /**
695
- * Handle track events (custom events like clicks, scrolls, etc.)
696
- *
697
- * Enriches the event with:
698
- * - Session/device IDs
699
- * - Page metadata (URL, title, etc.)
700
- * - Tab information
701
- * - Timestamp
702
- *
703
- * Then queues for batched sending.
704
- */
705
- track: ({ payload }) => {
706
- if (!insightsUrl || !workspace) {
707
- return;
708
- }
709
- const payloadProperties = payload?.properties || {};
710
- const collectInput = {
711
- session_id: _session,
712
- device_id: _device,
713
- event: payload.event,
714
- version: payload.version || 1,
715
- workspace_id: workspace,
716
- site_id: site || void 0,
717
- properties: {
718
- ...properties,
719
- ...payloadProperties,
720
- // Remove metadata fields from properties (they're top-level)
721
- version: void 0,
722
- resource: void 0,
723
- identifier: void 0,
724
- hostname: void 0,
725
- pathname: void 0,
726
- url: void 0,
727
- page_title: void 0,
728
- created_at: void 0
729
- },
730
- hostname: payload?.hostname || window.location.hostname,
731
- pathname: payload?.pathname || window.location.pathname,
732
- url: payload?.url || window.location.href,
733
- page_title: payload?.page_title || document.title,
734
- resource: payloadProperties?.resource || "page",
735
- identifier: payloadProperties?.identifier || properties?.id,
736
- created_at: payload?.created_at || (/* @__PURE__ */ new Date()).toISOString(),
737
- tab_id: getCurrentTabId(),
738
- tab_count: getTabCount()
739
- };
740
- queueEvent(collectInput);
741
- },
742
- /**
743
- * Handle page view events.
744
- *
745
- * Called when navigating to a new page (in SPAs) or on initial load.
746
- * Resets the rate limit counter and tracks a page.view event.
747
- */
748
- page: ({ payload }) => {
749
- if (!insightsUrl) {
750
- return;
751
- }
752
- _eventCount = 0;
753
- const payloadProperties = payload?.properties || {};
754
- track("page.view", {
755
- ...properties,
756
- ...payloadProperties,
757
- title: document.title,
758
- identifier: properties.id,
759
- resource: "page"
760
- });
761
- },
762
- /**
763
- * Handle identify events (session establishment).
764
- *
765
- * This is called when identify() is invoked. It:
766
- * 1. Checks if already identified (via BroadcastChannel) - if so, skip
767
- * 2. Calls /welcome API to get session/device IDs
768
- * 3. On success, broadcasts session to other tabs
769
- *
770
- * The /welcome API uses HttpOnly cookies to maintain persistent
771
- * device identity across sessions.
772
- */
773
- identify: ({ payload }) => {
774
- if (!insightsUrl || !workspace) {
775
- return;
776
- }
777
- if (is_identified) {
778
- return;
779
- }
780
- if (_pendingIdentify) {
781
- return;
782
- }
783
- _pendingIdentify = true;
784
- const welcomeInput = {
785
- private_mode: payload.traits.private_mode,
786
- dark_mode: payload.traits.dark_mode,
787
- timezone: payload.traits.timezone,
788
- locale: payload.traits.locale,
789
- referrer: payload.traits.referrer,
790
- properties: payload.traits.properties,
791
- workspace_id: workspace,
792
- site_id: site || void 0,
793
- hostname: window.location.hostname,
794
- pathname: window.location.pathname,
795
- page_title: document.title,
796
- url: window.location.href,
797
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
798
- tab_id: getCurrentTabId(),
799
- tab_count: getTabCount()
800
- };
801
- request(
802
- `/v1/insights/event/welcome`,
803
- welcomeInput
804
- ).then((data) => {
805
- if (is_identified) {
806
- _pendingIdentify = false;
807
- return;
808
- }
809
- is_identified = true;
810
- _pendingIdentify = false;
811
- if (data?.content?.data?.session) {
812
- _session = data?.content.data.session;
813
- }
814
- if (data?.content?.data?.device) {
815
- _device = data?.content.data.device;
512
+ a.fetch.url(w, !0).url("/v1/insights/event/welcome").options({
513
+ keepAlive: !0,
514
+ credentials: s === "proxy" ? "include" : "omit"
515
+ }).headers(d).post(L).json().then((g) => {
516
+ if (!e()) return;
517
+ const { isIdentified: b } = C();
518
+ if (!b) {
519
+ const v = { isIdentified: !0 };
520
+ if (g?.content?.data?.session && (v.sessionId = g?.content?.data?.session), g?.content?.data?.device && (v.deviceId = g?.content?.data?.device), s === "direct" && g?.content?.meta) {
521
+ const k = g.content.meta;
522
+ k.device_token && (v.deviceToken = k.device_token), k.session_token && (v.sessionToken = k.session_token);
816
523
  }
817
- if (_insightsMode === "direct" && data?.content?.meta) {
818
- const meta = data.content.meta;
819
- if (meta.device_token) {
820
- setInStorage(storage_keys.device_token, meta.device_token);
821
- }
822
- if (meta.session_token) {
823
- setInStorage(storage_keys.session_token, meta.session_token);
824
- }
825
- }
826
- broadcastSession();
827
- }).catch((e) => {
828
- _pendingIdentify = false;
829
- return getLevoError(e);
830
- });
831
- }
832
- };
833
- instance = Analytics({
834
- app: workspace || "",
835
- plugins: [plugin]
836
- });
837
- await identify();
838
- startActivityListener();
839
- return instance.page({
840
- resource: "page",
841
- identifier: properties.id
842
- });
843
- };
844
- const startActivityListener = () => {
845
- if (typeof document !== "undefined" && !idle_listener) {
846
- idle_listener = onUserActivity({
847
- timeout: _IDLE_TIMEOUT,
848
- throttle: _IDLE_THROTTLE,
849
- onIdle: (activeForSeconds) => {
850
- track("user.idle", {
851
- seconds_active: activeForSeconds
852
- });
853
- },
854
- onWakeUp: (idleForSeconds) => {
855
- track("user.active", {
856
- seconds_idle: idleForSeconds
857
- });
524
+ T(v);
858
525
  }
526
+ }).catch((g) => {
527
+ console.log("/welcome failed with error", E(g));
528
+ }).finally(() => {
529
+ T({ isIdentifying: !1 });
859
530
  });
860
531
  }
861
- return null;
862
532
  };
863
- const bounce = async (properties) => {
864
- const activity_status = getActivityStatus();
865
- if (activity_status) {
866
- if (activity_status?.status === "idle") {
867
- properties.seconds_idle = activity_status.seconds;
868
- } else {
869
- properties.seconds_active = activity_status.seconds;
533
+ return ae({
534
+ app: a.workspace || "",
535
+ debug: !!t,
536
+ plugins: [n]
537
+ });
538
+ };
539
+ let m = null, _ = !1;
540
+ const x = [], Ie = (a, i) => {
541
+ const { insightsUrl: e, debug: t = !1, site: n = null } = i;
542
+ K({
543
+ workspace: a.workspace,
544
+ debug: t,
545
+ site_id: n
546
+ }), typeof window < "u" && Promise.all([ke(), we()]).then(([o, l]) => {
547
+ const h = l?.type ? {
548
+ type: l.type,
549
+ referrer: l.referrer,
550
+ data: l.data,
551
+ raw: l.raw ?? ""
552
+ } : null;
553
+ T({
554
+ identity: {
555
+ locale: o.locale,
556
+ timezone: o.timezone,
557
+ darkMode: o.dark_mode,
558
+ privateMode: o.private_mode,
559
+ referrer: h,
560
+ properties: o.properties
870
561
  }
871
- }
872
- track("page.bounce", properties);
873
- };
874
- const track = async (event, properties) => {
875
- if (!workspace) {
562
+ });
563
+ }).catch(() => {
564
+ T({
565
+ identity: {
566
+ locale: null,
567
+ timezone: null,
568
+ darkMode: null,
569
+ privateMode: null,
570
+ referrer: null,
571
+ properties: null
572
+ }
573
+ });
574
+ });
575
+ const c = async (o, l) => {
576
+ if (typeof window > "u")
876
577
  return;
877
- }
878
- if (instance) {
879
- return instance.track(event, properties);
880
- }
881
- const collectInput = {
882
- event,
883
- version: properties?.version || 1,
578
+ const { page_id: h } = P(), { allTabs: y } = C(), w = z(), p = y.length, I = {
579
+ event: o,
580
+ version: l?.version || 1,
884
581
  properties: {
885
- ...properties,
582
+ ...l,
886
583
  version: void 0,
887
584
  resource: void 0,
888
585
  identifier: void 0,
@@ -894,48 +591,74 @@ var createLevoInsights = (client, options = {}) => {
894
591
  },
895
592
  hostname: window.location.hostname,
896
593
  pathname: window.location.pathname,
897
- url: window.location.href,
898
- page_title: document.title,
899
- resource: properties?.resource || "page",
900
- identifier: properties?.identifier || properties?.id,
901
- workspace_id: workspace,
902
- site_id: site || void 0,
903
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
904
- tab_id: getCurrentTabId(),
905
- tab_count: getTabCount()
594
+ url: l?.url ?? window.location.href,
595
+ page_title: l?.page_title ?? document.title,
596
+ resource: l?.resource ?? "page",
597
+ identifier: l?.id ?? h ?? void 0,
598
+ created_at: l?.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
599
+ tab_id: w ?? "",
600
+ tab_count: p
906
601
  };
907
- queueEvent(collectInput);
908
- };
909
- const destroy = () => {
910
- flushEventQueue();
911
- stopFlushTimer();
912
- _channel?.close();
602
+ W(I);
603
+ }, r = (o) => {
604
+ !m || !_ || m.identify("", { withLock: !0 });
605
+ }, f = (o) => {
606
+ const { page_id: l } = P();
607
+ l !== o && j("page_id", o);
913
608
  };
914
609
  return {
915
- get insightsUrl() {
916
- return insightsUrl;
917
- },
918
- /** Get the underlying analytics instance (read-only) */
919
- get instance() {
920
- return instance;
610
+ init: (o = {}) => {
611
+ if (typeof window > "u")
612
+ throw new Error("[@levo-so/insights] init() should only be called on the client side.");
613
+ const { pageId: l } = o;
614
+ if (l && f(l), !_ && !(a.NODE_ENV === "development" && !t)) {
615
+ if (!e) {
616
+ console.error(
617
+ "[@levo-so/insights] The utility could not be initialized due to missing insights endpoint."
618
+ );
619
+ return;
620
+ }
621
+ _ = !0, Q(() => {
622
+ if (!_) return;
623
+ const { insightsBaseUrl: h } = C();
624
+ h || he(i).then(({ mode: w, url: p }) => {
625
+ _ && p && T({ mode: w, insightsBaseUrl: p });
626
+ }).catch(() => {
627
+ e && T({ mode: "proxy", insightsBaseUrl: e });
628
+ });
629
+ const y = Z.subscribe((w) => {
630
+ if (!(!w || !_) && !m) {
631
+ const p = be(a, c, () => m === p);
632
+ m = p, C().insightsBaseUrl && m && (x.push(
633
+ J(),
634
+ V(m),
635
+ ee(m),
636
+ fe(m),
637
+ pe(m),
638
+ te(),
639
+ ge(m),
640
+ me(m),
641
+ ne(m),
642
+ re(m)
643
+ ), r(), m.page({ resource: "page" }));
644
+ }
645
+ });
646
+ x.push(y);
647
+ });
648
+ }
921
649
  },
922
- /** Check if session has been established (read-only) */
923
- get is_identified() {
924
- return is_identified;
650
+ destroy: () => {
651
+ _ && (N(X()), Y(), x.forEach((o) => {
652
+ o();
653
+ }), x.length = 0, m = null, _ = !1);
925
654
  },
926
- /** Storage keys used by the module */
927
- storage_keys,
928
- /** Initialize the module for a page */
929
- initiate,
930
- /** Identify/establish session */
931
- identify,
932
- /** Track a custom event */
933
- track,
934
- /** Track a bounce event */
935
- bounce,
936
- /** Cleanup the module */
937
- destroy
655
+ track: c,
656
+ get instance() {
657
+ return m;
658
+ }
938
659
  };
939
660
  };
940
-
941
- export { AnalyticsEvents, AnalyticsEventsList, createLevoInsights };
661
+ export {
662
+ xe as AnalyticsEvents,
663
+ Ie as createLevoInsights
664
+ };