@clonus/js-client 2.0.0

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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/dist/client/autocapture.d.ts +13 -0
  3. package/dist/client/autocapture.d.ts.map +1 -0
  4. package/dist/client/client.d.ts +55 -0
  5. package/dist/client/client.d.ts.map +1 -0
  6. package/dist/client/device.d.ts +11 -0
  7. package/dist/client/device.d.ts.map +1 -0
  8. package/dist/client/event.d.ts +14 -0
  9. package/dist/client/event.d.ts.map +1 -0
  10. package/dist/client/identity.d.ts +7 -0
  11. package/dist/client/identity.d.ts.map +1 -0
  12. package/dist/client/network.d.ts +13 -0
  13. package/dist/client/network.d.ts.map +1 -0
  14. package/dist/client/options.d.ts +36 -0
  15. package/dist/client/options.d.ts.map +1 -0
  16. package/dist/client/session.d.ts +14 -0
  17. package/dist/client/session.d.ts.map +1 -0
  18. package/dist/client/storage.d.ts +19 -0
  19. package/dist/client/storage.d.ts.map +1 -0
  20. package/dist/client/user.d.ts +16 -0
  21. package/dist/client/user.d.ts.map +1 -0
  22. package/dist/clonus.umd.js +3 -0
  23. package/dist/clonus.umd.js.map +1 -0
  24. package/dist/index.d.ts +28 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +3 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/index.mjs +804 -0
  29. package/dist/index.mjs.map +1 -0
  30. package/dist/utils/element.d.ts +12 -0
  31. package/dist/utils/element.d.ts.map +1 -0
  32. package/dist/utils/index.d.ts +3 -0
  33. package/dist/utils/index.d.ts.map +1 -0
  34. package/dist/utils/logging/errors.d.ts +7 -0
  35. package/dist/utils/logging/errors.d.ts.map +1 -0
  36. package/dist/utils/logging/logger.d.ts +8 -0
  37. package/dist/utils/logging/logger.d.ts.map +1 -0
  38. package/dist/utils/storage/cookie.d.ts +7 -0
  39. package/dist/utils/storage/cookie.d.ts.map +1 -0
  40. package/dist/utils/storage/local.d.ts +7 -0
  41. package/dist/utils/storage/local.d.ts.map +1 -0
  42. package/dist/utils/user_agent.d.ts +18 -0
  43. package/dist/utils/user_agent.d.ts.map +1 -0
  44. package/package.json +37 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,804 @@
1
+ const f = "https://api.clonus.io/v1";
2
+ class y {
3
+ constructor(e) {
4
+ const t = e ?? {};
5
+ this.apiUrl = f.endsWith("/") ? f : f + "/", this.autocapture = t.autocapture ?? !0, this.flushInterval = this.normalizeNumberInput(t.flush?.interval, {
6
+ default: 1e4,
7
+ min: 1e3,
8
+ max: 6e4
9
+ }), this.flushThreshold = this.normalizeNumberInput(t.flush?.threshold, {
10
+ default: 50,
11
+ min: 1,
12
+ max: 500
13
+ }), this.sessionDuration = this.normalizeNumberInput(t.session?.duration, {
14
+ default: 18e5,
15
+ min: 6e4,
16
+ max: 864e5
17
+ }), this.sessionStorageEnabled = t.session?.storage ?? !0, this.sdkVersion = t.$sdk?.version ?? "0.0.1", this.sdkLibrary = t.$sdk?.library ?? "js-client";
18
+ }
19
+ getApiUrl() {
20
+ return this.apiUrl;
21
+ }
22
+ getAutocapture() {
23
+ return this.autocapture;
24
+ }
25
+ getFlushInterval() {
26
+ return this.flushInterval;
27
+ }
28
+ getFlushThreshold() {
29
+ return this.flushThreshold;
30
+ }
31
+ getSessionDuration() {
32
+ return this.sessionDuration;
33
+ }
34
+ isSessionStorageEnabled() {
35
+ return this.sessionStorageEnabled;
36
+ }
37
+ getSDKVersion() {
38
+ return this.sdkVersion;
39
+ }
40
+ getSDKLibrary() {
41
+ return this.sdkLibrary;
42
+ }
43
+ normalizeNumberInput(e, t) {
44
+ return e == null || typeof e != "number" || isNaN(e) || !isFinite(e) ? t.default : Math.max(Math.min(e, t.max), t.min);
45
+ }
46
+ }
47
+ const k = 1e4, v = 3, b = 1e3, E = 30;
48
+ class A {
49
+ constructor(e = 30) {
50
+ this.leakyBucket = /* @__PURE__ */ new Map(), this.threshold = e;
51
+ }
52
+ shouldContinue(e) {
53
+ return this.getCount(e) < this.threshold;
54
+ }
55
+ acquire(e) {
56
+ this.leakyBucket.set(e, this.getCount(e) + 1);
57
+ }
58
+ release(e) {
59
+ const t = this.getCount(e);
60
+ t > 0 && this.leakyBucket.set(e, t - 1);
61
+ }
62
+ getCount(e) {
63
+ return this.leakyBucket.get(e) || 0;
64
+ }
65
+ }
66
+ class U {
67
+ constructor(e, t) {
68
+ if (this.retryCodes = [408, 500, 502, 503, 504, 522, 524, 599], !e || typeof e != "string")
69
+ throw new Error("API key is required");
70
+ this.apiKey = e, this.baseUrl = t, this.rateLimiter = new A(E);
71
+ }
72
+ async post(e, t) {
73
+ const s = k, i = v, n = b, o = `${this.baseUrl}${e}`;
74
+ if (!this.rateLimiter.shouldContinue(e))
75
+ throw new Error("Rate limit exceeded");
76
+ this.rateLimiter.acquire(e);
77
+ try {
78
+ return await this.fetchWithRetry(o, t, i, n, s);
79
+ } finally {
80
+ this.rateLimiter.release(e);
81
+ }
82
+ }
83
+ async fetchWithRetry(e, t, s, i, n, o = 1) {
84
+ const l = new AbortController(), u = setTimeout(() => l.abort(), n);
85
+ try {
86
+ const a = await fetch(e, {
87
+ method: "POST",
88
+ headers: {
89
+ "Content-Type": "application/json",
90
+ Authorization: `Bearer ${this.apiKey}`
91
+ },
92
+ body: JSON.stringify(t),
93
+ signal: l.signal,
94
+ keepalive: !0
95
+ });
96
+ if (!a.ok && this.shouldRetryStatus(a.status) && o < s)
97
+ return await this.waitBeforeRetry(i), this.fetchWithRetry(e, t, s, i * 2, n, o + 1);
98
+ if (!a.ok)
99
+ throw new Error(`HTTP ${a.status}: ${a.statusText}`);
100
+ return a.json();
101
+ } catch (a) {
102
+ if (o < s && this.shouldRetryError(a))
103
+ return await this.waitBeforeRetry(i), this.fetchWithRetry(e, t, s, i * 2, n, o + 1);
104
+ throw a instanceof Error && a.name === "AbortError" ? new Error(`Request timeout after ${n}ms`) : a;
105
+ } finally {
106
+ clearTimeout(u);
107
+ }
108
+ }
109
+ shouldRetryStatus(e) {
110
+ return this.retryCodes.includes(e);
111
+ }
112
+ shouldRetryError(e) {
113
+ return e instanceof Error ? e.name === "AbortError" || e.message.includes("network") : !1;
114
+ }
115
+ waitBeforeRetry(e) {
116
+ return new Promise((t) => setTimeout(t, e));
117
+ }
118
+ }
119
+ class _ {
120
+ constructor(e, t) {
121
+ this.localStorage = e, this.cookieStorage = t;
122
+ }
123
+ getPersistent(e) {
124
+ return this.localStorage.getItem(e);
125
+ }
126
+ setPersistent(e, t) {
127
+ this.localStorage.setItem(e, t);
128
+ }
129
+ removePersistent(e) {
130
+ this.localStorage.removeItem(e);
131
+ }
132
+ getCookie(e) {
133
+ return this.cookieStorage.getItem(e);
134
+ }
135
+ setCookie(e, t) {
136
+ this.cookieStorage.setItem(e, t);
137
+ }
138
+ removeCookie(e) {
139
+ this.cookieStorage.removeItem(e);
140
+ }
141
+ getJSON(e) {
142
+ const t = this.getPersistent(e);
143
+ if (!t) return null;
144
+ try {
145
+ return JSON.parse(t);
146
+ } catch {
147
+ return null;
148
+ }
149
+ }
150
+ setJSON(e, t) {
151
+ this.setPersistent(e, JSON.stringify(t));
152
+ }
153
+ }
154
+ class C {
155
+ getItem(e) {
156
+ try {
157
+ return typeof window > "u" || !window.localStorage ? null : window.localStorage.getItem(e);
158
+ } catch {
159
+ return null;
160
+ }
161
+ }
162
+ setItem(e, t) {
163
+ try {
164
+ if (typeof window > "u" || !window.localStorage)
165
+ return;
166
+ window.localStorage.setItem(e, t);
167
+ } catch {
168
+ }
169
+ }
170
+ removeItem(e) {
171
+ try {
172
+ if (typeof window > "u" || !window.localStorage)
173
+ return;
174
+ window.localStorage.removeItem(e);
175
+ } catch {
176
+ }
177
+ }
178
+ }
179
+ class L {
180
+ getItem(e) {
181
+ try {
182
+ if (typeof document > "u")
183
+ return null;
184
+ const t = e + "=", i = decodeURIComponent(document.cookie).split(";");
185
+ for (let n = 0; n < i.length; n++) {
186
+ let o = i[n];
187
+ for (; o.charAt(0) === " "; )
188
+ o = o.substring(1);
189
+ if (o.indexOf(t) === 0)
190
+ return o.substring(t.length, o.length);
191
+ }
192
+ return null;
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
197
+ setItem(e, t) {
198
+ try {
199
+ if (typeof document > "u")
200
+ return;
201
+ const s = /* @__PURE__ */ new Date();
202
+ s.setFullYear(s.getFullYear() + 1), document.cookie = `${e}=${t};expires=${s.toUTCString()};path=/`;
203
+ } catch {
204
+ }
205
+ }
206
+ removeItem(e) {
207
+ try {
208
+ if (typeof document > "u")
209
+ return;
210
+ document.cookie = `${e}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`;
211
+ } catch {
212
+ }
213
+ }
214
+ }
215
+ class g extends Error {
216
+ constructor(e) {
217
+ super(e ?? "Call and wait for initializeAsync() to finish first."), this.name = "ClonusUninitializedError", Object.setPrototypeOf(this, g.prototype);
218
+ }
219
+ }
220
+ class h extends Error {
221
+ constructor(e) {
222
+ super(e), this.name = "ClonusInvalidArgumentError", Object.setPrototypeOf(this, h.prototype);
223
+ }
224
+ }
225
+ class T {
226
+ constructor(e) {
227
+ if (!e || typeof e != "object")
228
+ throw new h("User object is required");
229
+ if (!e.email || typeof e.email != "string" || e.email.trim().length === 0)
230
+ throw new h("User email is required");
231
+ if (!e.id || typeof e.id != "string" || e.id.trim().length === 0)
232
+ throw new h("User id is required");
233
+ this.email = e.email.trim().toLowerCase(), this.id = e.id.trim(), this.properties = e.properties ?? {};
234
+ }
235
+ getEmail() {
236
+ return this.email;
237
+ }
238
+ getUserId() {
239
+ return this.id;
240
+ }
241
+ getProperties() {
242
+ return { ...this.properties };
243
+ }
244
+ toJSON() {
245
+ return {
246
+ id: this.id,
247
+ email: this.email,
248
+ properties: this.properties
249
+ };
250
+ }
251
+ }
252
+ class I {
253
+ constructor(e) {
254
+ this.sdk = e;
255
+ }
256
+ async initAsync() {
257
+ const e = this.sdk.getUser();
258
+ if (e.getEmail() || this.sdk.getBrowser())
259
+ return Promise.resolve();
260
+ try {
261
+ await this.sdk.getNetwork().post("/identify", {
262
+ user_id: e.getUserId(),
263
+ user_email: e.getEmail(),
264
+ user_properties: e.getProperties(),
265
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
266
+ });
267
+ } catch {
268
+ }
269
+ }
270
+ }
271
+ const c = [];
272
+ for (let r = 0; r < 256; ++r)
273
+ c.push((r + 256).toString(16).slice(1));
274
+ function O(r, e = 0) {
275
+ return (c[r[e + 0]] + c[r[e + 1]] + c[r[e + 2]] + c[r[e + 3]] + "-" + c[r[e + 4]] + c[r[e + 5]] + "-" + c[r[e + 6]] + c[r[e + 7]] + "-" + c[r[e + 8]] + c[r[e + 9]] + "-" + c[r[e + 10]] + c[r[e + 11]] + c[r[e + 12]] + c[r[e + 13]] + c[r[e + 14]] + c[r[e + 15]]).toLowerCase();
276
+ }
277
+ let m;
278
+ const x = new Uint8Array(16);
279
+ function D() {
280
+ if (!m) {
281
+ if (typeof crypto > "u" || !crypto.getRandomValues)
282
+ throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
283
+ m = crypto.getRandomValues.bind(crypto);
284
+ }
285
+ return m(x);
286
+ }
287
+ const R = typeof crypto < "u" && crypto.randomUUID && crypto.randomUUID.bind(crypto), p = { randomUUID: R };
288
+ function P(r, e, t) {
289
+ r = r || {};
290
+ const s = r.random ?? r.rng?.() ?? D();
291
+ if (s.length < 16)
292
+ throw new Error("Random bytes length must be >= 16");
293
+ return s[6] = s[6] & 15 | 64, s[8] = s[8] & 63 | 128, O(s);
294
+ }
295
+ function S(r, e, t) {
296
+ return p.randomUUID && !r ? p.randomUUID() : P(r);
297
+ }
298
+ class $ {
299
+ constructor(e) {
300
+ this.currentSession = null, this.storageKey = "clonus_session", this.sdk = e;
301
+ }
302
+ getOrCreateSessionId() {
303
+ return (this.getActiveSession() || this.createSession()).sessionId;
304
+ }
305
+ getActiveSession() {
306
+ const e = this.currentSession || this.loadFromStorage();
307
+ if (!e) return null;
308
+ const t = this.sdk.getOptions().getSessionDuration();
309
+ return e.expiration > Date.now() ? this.extendSession(e, t) : null;
310
+ }
311
+ createSession() {
312
+ const e = (/* @__PURE__ */ new Date()).toISOString(), t = this.sdk.getOptions().getSessionDuration(), s = {
313
+ sessionId: S(),
314
+ startTime: e,
315
+ lastActivity: e,
316
+ expiration: Date.now() + t
317
+ };
318
+ return this.currentSession = s, this.persistToStorage(s), s;
319
+ }
320
+ extendSession(e, t) {
321
+ const s = {
322
+ ...e,
323
+ lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
324
+ expiration: Date.now() + t
325
+ };
326
+ return this.currentSession = s, this.persistToStorage(s), s;
327
+ }
328
+ loadFromStorage() {
329
+ const e = this.sdk.getStorage().getPersistent(this.storageKey);
330
+ if (!e) return null;
331
+ try {
332
+ const t = JSON.parse(e);
333
+ return Date.now() < t.expiration ? t : null;
334
+ } catch {
335
+ return null;
336
+ }
337
+ }
338
+ persistToStorage(e) {
339
+ this.sdk.getOptions().isSessionStorageEnabled() && (this.sdk.getStorage().setPersistent(this.storageKey, JSON.stringify(e)), this.sdk.getStorage().setCookie(this.storageKey, e.sessionId));
340
+ }
341
+ }
342
+ class M {
343
+ constructor(e) {
344
+ this.storageKey = "clonus_device_id", this.sdk = e;
345
+ }
346
+ getOrCreateDeviceId() {
347
+ if (this.deviceId)
348
+ return this.deviceId;
349
+ const e = this.loadFromStorage();
350
+ if (e)
351
+ return this.deviceId = e, e;
352
+ const t = S();
353
+ return this.deviceId = t, this.persistToStorage(t), t;
354
+ }
355
+ loadFromStorage() {
356
+ return this.sdk.getStorage().getCookie(this.storageKey) || this.sdk.getStorage().getPersistent(this.storageKey);
357
+ }
358
+ persistToStorage(e) {
359
+ this.sdk.getStorage().setCookie(this.storageKey, e), this.sdk.getStorage().setPersistent(this.storageKey, e);
360
+ }
361
+ }
362
+ class N {
363
+ parse(e) {
364
+ const t = this.browser(e), s = this.os(e), i = this.device(e);
365
+ return {
366
+ browser_name: t.name,
367
+ browser_version: t.version,
368
+ os_name: s.name,
369
+ os_version: s.version,
370
+ device_brand: i.brand,
371
+ device_model: i.model
372
+ };
373
+ }
374
+ browser(e) {
375
+ let t = "Unknown", s = "Unknown", i;
376
+ return e.includes("Firefox/") ? (t = "Firefox", i = e.match(/Firefox\/(\d+\.\d+)/), s = i ? i[1] : "Unknown") : e.includes("Edg/") ? (t = "Edge", i = e.match(/Edg\/(\d+\.\d+)/), s = i ? i[1] : "Unknown") : e.includes("Chrome/") ? (t = "Chrome", i = e.match(/Chrome\/(\d+\.\d+)/), s = i ? i[1] : "Unknown") : e.includes("Safari/") && !e.includes("Chrome") ? (t = "Safari", i = e.match(/Version\/(\d+\.\d+)/), s = i ? i[1] : "Unknown") : (e.includes("Opera/") || e.includes("OPR/")) && (t = "Opera", i = e.match(/OPR\/(\d+\.\d+)/), s = i ? i[1] : "Unknown"), { name: t, version: s };
377
+ }
378
+ os(e) {
379
+ let t = "Unknown", s = "Unknown", i;
380
+ if (e.includes("Windows NT")) {
381
+ if (t = "Windows", i = e.match(/Windows NT (\d+\.\d+)/), i) {
382
+ const n = i[1];
383
+ n === "10.0" ? s = "10" : n === "6.3" ? s = "8.1" : n === "6.2" ? s = "8" : n === "6.1" ? s = "7" : s = n;
384
+ }
385
+ } else e.includes("Mac OS X") ? (t = "Mac OS X", i = e.match(/Mac OS X (\d+[._]\d+[._]?\d*)/), s = i ? i[1].replace(/_/g, ".") : "Unknown") : e.includes("Android") ? (t = "Android", i = e.match(/Android (\d+\.\d+)/), s = i ? i[1] : "Unknown") : e.includes("iPhone") || e.includes("iPad") ? (t = "iOS", i = e.match(/OS (\d+_\d+)/), s = i ? i[1].replace(/_/g, ".") : "Unknown") : e.includes("Linux") && (t = "Linux");
386
+ return { name: t, version: s };
387
+ }
388
+ device(e) {
389
+ let t = "Unknown", s = "Unknown", i;
390
+ if (e.includes("iPhone"))
391
+ t = "Apple", s = "iPhone";
392
+ else if (e.includes("iPad"))
393
+ t = "Apple", s = "iPad";
394
+ else if (e.includes("Macintosh") || e.includes("Mac OS X"))
395
+ t = "Apple", s = "Mac";
396
+ else if (i = e.match(/Samsung|SM-[A-Z0-9]+|GT-[A-Z0-9]+/)) {
397
+ t = "Samsung";
398
+ const o = e.match(/SM-([A-Z0-9]+)|GT-([A-Z0-9]+)/);
399
+ s = o ? o[1] || o[2] : "Galaxy";
400
+ } else e.includes("Pixel") ? (t = "Google", i = e.match(/Pixel (\d+[a-zA-Z]*)/), s = i ? `Pixel ${i[1]}` : "Pixel") : e.includes("Huawei") || e.includes("HUAWEI") ? (t = "Huawei", i = e.match(/([A-Z]{3}-[A-Z0-9]+)/), s = i ? i[1] : "Huawei") : e.includes("Xiaomi") || e.includes("Mi ") || e.includes("Redmi") ? (t = "Xiaomi", i = e.match(/Mi ([A-Z0-9 ]+)|Redmi ([A-Z0-9 ]+)/), s = i && (i[1] || i[2]) || "Mi") : e.includes("OnePlus") || e.includes("ONEPLUS") ? (t = "OnePlus", i = e.match(/OnePlus ([A-Z0-9]+)/), s = i ? i[1] : "OnePlus") : e.includes("Windows") ? (t = "PC", s = "Windows") : e.includes("Linux") && !e.includes("Android") ? (t = "PC", s = "Linux") : e.includes("Android") && (t = "Android", s = "Android Device");
401
+ let n = "Desktop";
402
+ return /(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(e) ? n = "Tablet" : /Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(
403
+ e
404
+ ) && (n = "Mobile"), { type: n, brand: t, model: s };
405
+ }
406
+ }
407
+ class F {
408
+ constructor(e) {
409
+ this.storage = e, this.queue = [], this.storageKey = "clonus_event_queue", this.MAX_QUEUE_SIZE = 1e3, this.loadFromStorage();
410
+ }
411
+ enqueue(e) {
412
+ this.queue.length >= this.MAX_QUEUE_SIZE && this.queue.shift(), this.queue.push(e), this.persistToStorage();
413
+ }
414
+ getAll() {
415
+ return [...this.queue];
416
+ }
417
+ clear() {
418
+ this.queue = [], this.persistToStorage();
419
+ }
420
+ size() {
421
+ return this.queue.length;
422
+ }
423
+ loadFromStorage() {
424
+ const e = this.storage.getJSON(this.storageKey);
425
+ this.queue = e ? e.slice(0, this.MAX_QUEUE_SIZE) : [];
426
+ }
427
+ persistToStorage() {
428
+ this.storage.setJSON(this.storageKey, this.queue);
429
+ }
430
+ }
431
+ class q {
432
+ constructor(e) {
433
+ this.storage = e, this.failedRetries = [], this.storageKey = "clonus_failed_requests", this.MAX_FAILED_REQUESTS = 100, this.MAX_AGE_MS = 7200 * 60 * 1e3, this.MAX_ATTEMPTS = 5, this.loadFromStorage();
434
+ }
435
+ add(e, t) {
436
+ const s = {
437
+ endpoint: e,
438
+ body: t,
439
+ timestamp: Date.now(),
440
+ retryCount: 0
441
+ };
442
+ this.failedRetries.length >= this.MAX_FAILED_REQUESTS && this.failedRetries.shift(), this.failedRetries.push(s), this.persistToStorage();
443
+ }
444
+ getAll() {
445
+ if (this.failedRetries.length === 0) return [];
446
+ const e = Date.now();
447
+ return this.failedRetries.filter(
448
+ (t) => e - t.timestamp <= this.MAX_AGE_MS && t.retryCount < this.MAX_ATTEMPTS
449
+ );
450
+ }
451
+ clearAll() {
452
+ this.failedRetries = [], this.persistToStorage();
453
+ }
454
+ loadFromStorage() {
455
+ const e = this.storage.getJSON(this.storageKey);
456
+ this.failedRetries = e ? e.slice(0, this.MAX_FAILED_REQUESTS) : [];
457
+ }
458
+ persistToStorage() {
459
+ this.storage.setJSON(this.storageKey, this.failedRetries);
460
+ }
461
+ }
462
+ class K {
463
+ constructor(e) {
464
+ this.sdk = e, this.queue = new F(this.sdk.getStorage()), this.retry = new q(this.sdk.getStorage()), this.ua = new N();
465
+ const t = this.sdk.getOptions().getFlushInterval();
466
+ this.flushTimer = setInterval(() => {
467
+ this.flush();
468
+ }, t);
469
+ }
470
+ enqueue(e, t, s) {
471
+ this.sdk.getLogger().swallow("enqueue", () => {
472
+ if (!this.sdk.getBrowser()) return;
473
+ const i = this.ua.parse(navigator.userAgent);
474
+ this.queue.enqueue({
475
+ user_id: this.sdk.getUser().getUserId(),
476
+ device_id: this.sdk.getDevice().getOrCreateDeviceId(),
477
+ session_id: this.sdk.getSession().getOrCreateSessionId(),
478
+ message: t,
479
+ level: e,
480
+ properties: {
481
+ $page_url: window.location.href,
482
+ $page_host: window.location.hostname,
483
+ $page_path: window.location.pathname,
484
+ $page_title: document.title,
485
+ $page_referrer: document.referrer,
486
+ $browser_name: i.browser_name,
487
+ $browser_version: i.browser_version,
488
+ $os_name: i.os_name,
489
+ $os_version: i.os_version,
490
+ $device_brand: i.device_brand,
491
+ $device_model: i.device_model,
492
+ $timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
493
+ $language: navigator.language,
494
+ ...s
495
+ },
496
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
497
+ metadata: {
498
+ sdk_version: this.sdk.getOptions().getSDKVersion(),
499
+ sdk_library: this.sdk.getOptions().getSDKLibrary()
500
+ }
501
+ });
502
+ const n = this.sdk.getOptions().getFlushThreshold();
503
+ this.queue.size() >= n && this.flush();
504
+ });
505
+ }
506
+ async flush() {
507
+ const e = this.retry.getAll();
508
+ if (e.length > 0) {
509
+ this.retry.clearAll();
510
+ for (const s of e)
511
+ try {
512
+ await this.sdk.getNetwork().post(s.endpoint, s.body);
513
+ } catch {
514
+ this.retry.add(s.endpoint, s.body);
515
+ }
516
+ }
517
+ const t = this.queue.getAll();
518
+ if (t.length !== 0) {
519
+ this.queue.clear();
520
+ try {
521
+ await this.sdk.getNetwork().post("/events", { events: t });
522
+ } catch {
523
+ this.retry.add("/events", { events: t });
524
+ }
525
+ }
526
+ }
527
+ destroy() {
528
+ this.flushTimer && (clearInterval(this.flushTimer), this.flushTimer = void 0);
529
+ }
530
+ }
531
+ const H = /* @__PURE__ */ new Set(["button", "a", "input", "select", "textarea"]), z = /* @__PURE__ */ new Set([
532
+ "button",
533
+ "link",
534
+ "menuitem",
535
+ "tab",
536
+ "checkbox",
537
+ "radio",
538
+ "switch"
539
+ ]), X = ["onclick", "data-action"], w = /* @__PURE__ */ new Set(["checkbox", "radio", "file", "select"]), B = 100;
540
+ function J(r) {
541
+ const e = r.tagName.toLowerCase(), t = r.getAttribute("role"), s = window.getComputedStyle(r).cursor;
542
+ return H.has(e) || !!t && z.has(t) || s === "pointer" || X.some((i) => r.hasAttribute(i));
543
+ }
544
+ function Z(r) {
545
+ const e = r.tagName.toLowerCase();
546
+ return w.has(e) ? !0 : e === "input" ? w.has(r.type) : !1;
547
+ }
548
+ function d(r, e) {
549
+ if (r === "pageview") {
550
+ const o = document.title?.trim(), l = window.location.pathname;
551
+ return o ? `User viewed "${o}" page` : `User viewed page at ${l}`;
552
+ }
553
+ if (!e)
554
+ return `User performed ${r}`;
555
+ const t = e.tagName.toLowerCase(), s = G(e), i = Q(e), n = s || i || Y(e);
556
+ switch (r) {
557
+ case "click":
558
+ return V(t, n, e);
559
+ case "submit":
560
+ return W(e);
561
+ case "change":
562
+ return j(t, n, e);
563
+ default:
564
+ return `User interacted with ${n || t}`;
565
+ }
566
+ }
567
+ function V(r, e, t) {
568
+ const s = t.getAttribute("role");
569
+ if (r === "a" || s === "link")
570
+ return e ? `User clicked "${e}" link` : "User clicked a link";
571
+ if (r === "button" || s === "button")
572
+ return e ? `User clicked "${e}" button` : "User clicked a button";
573
+ if (r === "input") {
574
+ const i = t.type;
575
+ if (i === "submit")
576
+ return e ? `User clicked "${e}" submit button` : "User clicked submit";
577
+ if (i === "checkbox")
578
+ return e ? `User toggled "${e}" checkbox` : "User toggled a checkbox";
579
+ if (i === "radio")
580
+ return e ? `User selected "${e}" option` : "User selected a radio option";
581
+ }
582
+ return e ? `User clicked on "${e}"` : `User clicked on ${r} element`;
583
+ }
584
+ function W(r) {
585
+ const e = r.getAttribute("name") || r.getAttribute("id"), t = r.getAttribute("action");
586
+ return e ? `User submitted "${e}" form` : t ? `User submitted form to ${new URL(t, window.location.origin).pathname}` : "User submitted a form";
587
+ }
588
+ function j(r, e, t) {
589
+ if (r === "select")
590
+ return e ? `User changed "${e}" selection` : "User changed a dropdown selection";
591
+ if (r === "input") {
592
+ const s = t.type;
593
+ if (s === "file")
594
+ return e ? `User selected file for "${e}"` : "User selected a file";
595
+ if (s === "checkbox")
596
+ return e ? `User changed "${e}" checkbox` : "User changed a checkbox";
597
+ if (s === "radio")
598
+ return e ? `User selected "${e}" option` : "User selected a radio option";
599
+ }
600
+ return e ? `User changed "${e}" field` : "User changed a form field";
601
+ }
602
+ function G(r) {
603
+ const e = r.innerText?.trim();
604
+ return e && e.length > 0 && e.length <= B ? e.split(`
605
+ `)[0].trim() : null;
606
+ }
607
+ function Q(r) {
608
+ const e = r.getAttribute("aria-label");
609
+ if (e) return e;
610
+ const t = r.getAttribute("title");
611
+ if (t) return t;
612
+ const s = r.getAttribute("id");
613
+ if (s) {
614
+ const i = document.querySelector(`label[for="${s}"]`);
615
+ if (i?.textContent) return i.textContent.trim();
616
+ }
617
+ if (r instanceof HTMLInputElement || r instanceof HTMLTextAreaElement) {
618
+ const i = r.placeholder;
619
+ if (i) return i;
620
+ }
621
+ return null;
622
+ }
623
+ function Y(r) {
624
+ const e = r.getAttribute("name");
625
+ if (e) return e;
626
+ const t = r.getAttribute("id");
627
+ if (t && !t.match(/^[a-f0-9-]{20,}$/i))
628
+ return t;
629
+ const s = r.getAttribute("data-testid") || r.getAttribute("data-test-id");
630
+ return s || null;
631
+ }
632
+ class ee {
633
+ constructor(e) {
634
+ this.cleanupCallbacks = [], this.trackedChanges = /* @__PURE__ */ new WeakSet(), this.sdk = e;
635
+ }
636
+ init() {
637
+ this.setupAutoCapture(), this.setupOnlineListener(), this.setupShutdownListeners();
638
+ }
639
+ setupAutoCapture() {
640
+ if (!this.sdk.getOptions().getAutocapture())
641
+ return;
642
+ const e = (l) => {
643
+ const u = l.target;
644
+ J(u) && this.sdk.getLogger().swallow("click", () => {
645
+ const a = d("click", u);
646
+ this.sdk.getEvent().enqueue("info", a, { $autocapture: "click" });
647
+ });
648
+ }, t = (l) => {
649
+ const u = l.target;
650
+ this.sdk.getLogger().swallow("submit", () => {
651
+ const a = d("submit", u);
652
+ this.sdk.getEvent().enqueue("info", a, { $autocapture: "submit" });
653
+ });
654
+ }, s = (l) => {
655
+ const u = l.target;
656
+ Z(u) && (this.trackedChanges.has(u) || (this.trackedChanges.add(u), this.sdk.getLogger().swallow("change", () => {
657
+ const a = d("change", u);
658
+ this.sdk.getEvent().enqueue("info", a, { $autocapture: "change" });
659
+ })));
660
+ }, i = () => {
661
+ this.sdk.getLogger().swallow("pageview", () => {
662
+ const l = d("pageview");
663
+ this.sdk.getEvent().enqueue("info", l, { $autocapture: "pageview" });
664
+ });
665
+ };
666
+ i();
667
+ const n = history.pushState, o = history.replaceState;
668
+ history.pushState = (...l) => {
669
+ n.apply(history, l), i();
670
+ }, history.replaceState = (...l) => {
671
+ o.apply(history, l), i();
672
+ }, window.addEventListener("popstate", i), document.addEventListener("click", e, !0), document.addEventListener("submit", t, !0), document.addEventListener("change", s, !0), this.cleanupCallbacks.push(() => {
673
+ history.pushState = n, history.replaceState = o, window.removeEventListener("popstate", i), document.removeEventListener("click", e, !0), document.removeEventListener("submit", t, !0), document.removeEventListener("change", s, !0);
674
+ });
675
+ }
676
+ setupOnlineListener() {
677
+ const e = () => {
678
+ this.sdk.getLogger().swallow("flush", () => {
679
+ this.sdk.getEvent().flush();
680
+ });
681
+ };
682
+ window.addEventListener("online", e), this.cleanupCallbacks.push(() => {
683
+ window.removeEventListener("online", e);
684
+ });
685
+ }
686
+ setupShutdownListeners() {
687
+ const e = () => {
688
+ this.sdk.getEvent().flush();
689
+ }, t = () => {
690
+ document.visibilityState === "hidden" && this.sdk.getEvent().flush();
691
+ };
692
+ window.addEventListener("beforeunload", e), window.addEventListener("pagehide", e), typeof document < "u" && typeof document.addEventListener == "function" && document.addEventListener("visibilitychange", t), this.cleanupCallbacks.push(() => {
693
+ window.removeEventListener("beforeunload", e), window.removeEventListener("pagehide", e), typeof document < "u" && typeof document.removeEventListener == "function" && document.removeEventListener("visibilitychange", t);
694
+ });
695
+ }
696
+ destroy() {
697
+ this.cleanupCallbacks.forEach((e) => e()), this.cleanupCallbacks = [];
698
+ }
699
+ }
700
+ class te {
701
+ constructor() {
702
+ this.seen = /* @__PURE__ */ new Set();
703
+ }
704
+ capture(e, t, s) {
705
+ try {
706
+ const i = t();
707
+ return i instanceof Promise ? i.catch((n) => this.onCaught(e, n, s)) : i;
708
+ } catch (i) {
709
+ return this.onCaught(e, i, s);
710
+ }
711
+ }
712
+ swallow(e, t) {
713
+ this.capture(e, t, () => {
714
+ });
715
+ }
716
+ logError(e, t) {
717
+ const s = t instanceof Error ? t.message : String(t), i = t instanceof Error ? t.name : "Error";
718
+ this.seen.has(i) || (this.seen.add(i), console.error(`[Clonus] ${e}:`, s));
719
+ }
720
+ onCaught(e, t, s) {
721
+ if (t instanceof g || t instanceof h)
722
+ throw t;
723
+ return this.logError(e, t), s();
724
+ }
725
+ }
726
+ class se {
727
+ constructor(e, t, s) {
728
+ if (this.initialized = !1, !e || typeof e != "string" || e.trim().length < 10)
729
+ throw new h(
730
+ "Invalid API key. Must be a non-empty string with at least 10 characters."
731
+ );
732
+ this._browser = typeof window < "u" && typeof document < "u" && typeof navigator < "u", this.logger = new te(), this.user = new T(t), this.options = new y(s), this.network = new U(e, this.options.getApiUrl()), this.storage = new _(new C(), new L()), this.identity = new I(this), this.session = new $(this), this.device = new M(this), this.event = new K(this), this.autocapture = new ee(this), this._browser && this.autocapture.init();
733
+ }
734
+ getLogger() {
735
+ return this.logger;
736
+ }
737
+ getNetwork() {
738
+ return this.network;
739
+ }
740
+ getStorage() {
741
+ return this.storage;
742
+ }
743
+ getOptions() {
744
+ return this.options;
745
+ }
746
+ getUser() {
747
+ return this.user;
748
+ }
749
+ getDevice() {
750
+ return this.device;
751
+ }
752
+ getSession() {
753
+ return this.session;
754
+ }
755
+ getEvent() {
756
+ return this.event;
757
+ }
758
+ getBrowser() {
759
+ return this._browser;
760
+ }
761
+ async initializeAsync() {
762
+ return this.logger.capture(
763
+ "initializeAsync",
764
+ async () => {
765
+ await this.identity.initAsync(), this.initialized = !0;
766
+ },
767
+ () => (this.initialized = !0, Promise.resolve())
768
+ );
769
+ }
770
+ info(e, t) {
771
+ this._log("info", e, t);
772
+ }
773
+ warn(e, t) {
774
+ this._log("warn", e, t);
775
+ }
776
+ error(e, t) {
777
+ this._log("error", e, t);
778
+ }
779
+ _log(e, t, s) {
780
+ this.logger.swallow("log", () => {
781
+ if (!this.initialized)
782
+ throw new g("Must call initializeAsync() before logging.");
783
+ if (!t || typeof t != "string")
784
+ return;
785
+ const i = t.length > 1e3 ? t.substring(0, 1e3) : t;
786
+ this.event.enqueue(e, i, s ?? {});
787
+ });
788
+ }
789
+ async shutdown() {
790
+ return this.logger.capture(
791
+ "shutdown",
792
+ async () => {
793
+ this.autocapture.destroy(), this.event.destroy(), await this.event.flush();
794
+ },
795
+ async () => {
796
+ }
797
+ );
798
+ }
799
+ }
800
+ typeof window < "u" && (window.ClonusClient = se);
801
+ export {
802
+ se as ClonusClient
803
+ };
804
+ //# sourceMappingURL=index.mjs.map