@clianta/sdk 1.7.2 → 1.7.3

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/vue.cjs.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Clianta SDK v1.7.2
2
+ * Clianta SDK v1.7.3
3
3
  * (c) 2026 Clianta
4
4
  * Released under the MIT License.
5
5
  */
@@ -224,8 +224,9 @@ class Transport {
224
224
  /**
225
225
  * Send identify request.
226
226
  * Returns contactId from the server response so the Tracker can store it.
227
+ * Retries on 5xx with exponential backoff (same policy as sendEvents).
227
228
  */
228
- async sendIdentify(data) {
229
+ async sendIdentify(data, attempt = 1) {
229
230
  const url = `${this.config.apiEndpoint}/api/public/track/identify`;
230
231
  try {
231
232
  const response = await this.fetchWithTimeout(url, {
@@ -243,16 +244,26 @@ class Transport {
243
244
  contactId: body.contactId ?? undefined,
244
245
  };
245
246
  }
246
- if (response.status >= 500) {
247
- logger.warn(`Identify server error (${response.status})`);
248
- }
249
- else {
250
- logger.error(`Identify failed with status ${response.status}:`, body.message);
247
+ // Server error retry with exponential backoff
248
+ if (response.status >= 500 && attempt < this.config.maxRetries) {
249
+ const backoff = this.config.retryDelay * Math.pow(2, attempt - 1);
250
+ logger.warn(`Identify server error (${response.status}), retrying in ${backoff}ms...`);
251
+ await this.delay(backoff);
252
+ return this.sendIdentify(data, attempt + 1);
251
253
  }
254
+ logger.error(`Identify failed with status ${response.status}:`, body.message);
252
255
  return { success: false, status: response.status };
253
256
  }
254
257
  catch (error) {
255
- logger.error('Identify request failed:', error);
258
+ // Network error retry if still online
259
+ const isOnline = typeof navigator === 'undefined' || navigator.onLine;
260
+ if (isOnline && attempt < this.config.maxRetries) {
261
+ const backoff = this.config.retryDelay * Math.pow(2, attempt - 1);
262
+ logger.warn(`Identify network error, retrying in ${backoff}ms (${attempt}/${this.config.maxRetries})...`);
263
+ await this.delay(backoff);
264
+ return this.sendIdentify(data, attempt + 1);
265
+ }
266
+ logger.error('Identify request failed after retries:', error);
256
267
  return { success: false, error: error };
257
268
  }
258
269
  }
@@ -570,9 +581,6 @@ function resetIds(useCookies = false) {
570
581
  // Ignore
571
582
  }
572
583
  }
573
- // ============================================
574
- // URL UTILITIES
575
- // ============================================
576
584
  /**
577
585
  * Extract UTM parameters from URL
578
586
  */
@@ -729,8 +737,9 @@ class EventQueue {
729
737
  flushInterval: config.flushInterval ?? 5000,
730
738
  maxQueueSize: config.maxQueueSize ?? MAX_QUEUE_SIZE,
731
739
  storageKey: config.storageKey ?? STORAGE_KEYS.EVENT_QUEUE,
740
+ persistMode: config.persistMode ?? 'session',
732
741
  };
733
- this.persistMode = config.persistMode || 'session';
742
+ this.persistMode = this.config.persistMode;
734
743
  this.isOnline = typeof navigator === 'undefined' || navigator.onLine;
735
744
  // Restore persisted queue
736
745
  this.restoreQueue();
@@ -794,9 +803,11 @@ class EventQueue {
794
803
  // Send to backend
795
804
  const result = await this.transport.sendEvents(events);
796
805
  if (!result.success) {
797
- // Re-queue events on failure (at the front)
806
+ // Re-queue events on failure (at the front), capped at maxQueueSize
798
807
  logger.warn('Flush failed, re-queuing events');
799
- this.queue.unshift(...events);
808
+ const availableSpace = this.config.maxQueueSize - this.queue.length;
809
+ const eventsToRequeue = events.slice(0, Math.max(0, availableSpace));
810
+ this.queue.unshift(...eventsToRequeue);
800
811
  this.persistQueue(this.queue);
801
812
  }
802
813
  else {
@@ -1243,7 +1254,7 @@ class FormsPlugin extends BasePlugin {
1243
1254
  if (this.trackedForms.has(form))
1244
1255
  return;
1245
1256
  this.trackedForms.add(form);
1246
- const formId = form.id || form.name || `form-${Math.random().toString(36).substr(2, 9)}`;
1257
+ const formId = form.id || form.name || `form-${Math.random().toString(36).substring(2, 11)}`;
1247
1258
  // Track form view
1248
1259
  this.track('form_view', 'Form Viewed', {
1249
1260
  formId,
@@ -1887,27 +1898,22 @@ class PopupFormsPlugin extends BasePlugin {
1887
1898
  super.destroy();
1888
1899
  }
1889
1900
  loadShownForms() {
1890
- try {
1891
- const stored = localStorage.getItem('clianta_shown_forms');
1892
- if (stored) {
1901
+ const stored = getLocalStorage('clianta_shown_forms');
1902
+ if (stored) {
1903
+ try {
1893
1904
  const data = JSON.parse(stored);
1894
1905
  this.shownForms = new Set(data.forms || []);
1895
1906
  }
1896
- }
1897
- catch (e) {
1898
- // Ignore storage errors
1907
+ catch {
1908
+ // Ignore parse errors
1909
+ }
1899
1910
  }
1900
1911
  }
1901
1912
  saveShownForms() {
1902
- try {
1903
- localStorage.setItem('clianta_shown_forms', JSON.stringify({
1904
- forms: Array.from(this.shownForms),
1905
- timestamp: Date.now(),
1906
- }));
1907
- }
1908
- catch (e) {
1909
- // Ignore storage errors
1910
- }
1913
+ setLocalStorage('clianta_shown_forms', JSON.stringify({
1914
+ forms: Array.from(this.shownForms),
1915
+ timestamp: Date.now(),
1916
+ }));
1911
1917
  }
1912
1918
  async fetchForms() {
1913
1919
  if (!this.tracker)
@@ -1926,7 +1932,7 @@ class PopupFormsPlugin extends BasePlugin {
1926
1932
  }
1927
1933
  }
1928
1934
  catch (error) {
1929
- console.error('[Clianta] Failed to fetch forms:', error);
1935
+ logger.error('Failed to fetch popup forms:', error);
1930
1936
  }
1931
1937
  }
1932
1938
  shouldShowForm(form) {
@@ -1937,7 +1943,7 @@ class PopupFormsPlugin extends BasePlugin {
1937
1943
  }
1938
1944
  else if (form.showFrequency === 'once_per_session') {
1939
1945
  const sessionKey = `clianta_form_${form._id}_shown`;
1940
- if (sessionStorage.getItem(sessionKey))
1946
+ if (getSessionStorage(sessionKey))
1941
1947
  return false;
1942
1948
  }
1943
1949
  return true;
@@ -2009,7 +2015,7 @@ class PopupFormsPlugin extends BasePlugin {
2009
2015
  // Mark as shown
2010
2016
  this.shownForms.add(form._id);
2011
2017
  this.saveShownForms();
2012
- sessionStorage.setItem(`clianta_form_${form._id}_shown`, 'true');
2018
+ setSessionStorage(`clianta_form_${form._id}_shown`, 'true');
2013
2019
  // Track view
2014
2020
  await this.trackFormView(form._id);
2015
2021
  // Render form
@@ -2325,17 +2331,17 @@ class PopupFormsPlugin extends BasePlugin {
2325
2331
  const redirect = new URL(form.redirectUrl, window.location.origin);
2326
2332
  const isSameOrigin = redirect.origin === window.location.origin;
2327
2333
  const isSafeProtocol = redirect.protocol === 'https:' || redirect.protocol === 'http:';
2328
- if (isSameOrigin || isSafeProtocol) {
2334
+ if (isSameOrigin && isSafeProtocol) {
2329
2335
  setTimeout(() => {
2330
2336
  window.location.href = redirect.href;
2331
2337
  }, 1500);
2332
2338
  }
2333
2339
  else {
2334
- console.warn('[Clianta] Blocked unsafe redirect URL:', form.redirectUrl);
2340
+ logger.warn('Blocked unsafe redirect URL:', form.redirectUrl);
2335
2341
  }
2336
2342
  }
2337
2343
  catch {
2338
- console.warn('[Clianta] Invalid redirect URL:', form.redirectUrl);
2344
+ logger.warn('Invalid redirect URL:', form.redirectUrl);
2339
2345
  }
2340
2346
  }
2341
2347
  // Close after delay
@@ -2348,7 +2354,7 @@ class PopupFormsPlugin extends BasePlugin {
2348
2354
  }
2349
2355
  }
2350
2356
  catch (error) {
2351
- console.error('[Clianta] Form submit error:', error);
2357
+ logger.error('Form submit error:', error);
2352
2358
  if (submitBtn) {
2353
2359
  submitBtn.disabled = false;
2354
2360
  submitBtn.textContent = form.submitButtonText || 'Subscribe';
@@ -2419,7 +2425,7 @@ const STORAGE_KEY_PATTERNS = [
2419
2425
  'token', 'jwt', 'auth', 'user', 'session', 'credential', 'account',
2420
2426
  ];
2421
2427
  /** JWT/user object fields containing email */
2422
- const EMAIL_CLAIMS = ['email', 'sub', 'preferred_username', 'user_email', 'mail', 'emailAddress', 'e_mail'];
2428
+ const EMAIL_CLAIMS = ['email', 'preferred_username', 'user_email', 'mail', 'emailAddress', 'e_mail'];
2423
2429
  /** Full name fields */
2424
2430
  const NAME_CLAIMS = ['name', 'full_name', 'display_name', 'displayName'];
2425
2431
  /** First name fields */
@@ -3363,6 +3369,7 @@ class Tracker {
3363
3369
  this.queue = new EventQueue(this.transport, {
3364
3370
  batchSize: this.config.batchSize,
3365
3371
  flushInterval: this.config.flushInterval,
3372
+ persistMode: this.config.persistMode,
3366
3373
  });
3367
3374
  // Get or create visitor and session IDs based on mode
3368
3375
  this.visitorId = this.createVisitorId();
@@ -3479,10 +3486,13 @@ class Tracker {
3479
3486
  logger.warn('SDK not initialized, event dropped');
3480
3487
  return;
3481
3488
  }
3489
+ const utmParams = getUTMParams();
3482
3490
  const event = {
3483
3491
  workspaceId: this.workspaceId,
3484
3492
  visitorId: this.visitorId,
3485
3493
  sessionId: this.sessionId,
3494
+ contactId: this.contactId ?? undefined,
3495
+ groupId: this.groupId ?? undefined,
3486
3496
  eventType: eventType,
3487
3497
  eventName,
3488
3498
  url: typeof window !== 'undefined' ? window.location.href : '',
@@ -3493,18 +3503,14 @@ class Tracker {
3493
3503
  websiteDomain: typeof window !== 'undefined' ? window.location.hostname : undefined,
3494
3504
  },
3495
3505
  device: getDeviceInfo(),
3496
- ...getUTMParams(),
3506
+ utmSource: utmParams.utmSource,
3507
+ utmMedium: utmParams.utmMedium,
3508
+ utmCampaign: utmParams.utmCampaign,
3509
+ utmTerm: utmParams.utmTerm,
3510
+ utmContent: utmParams.utmContent,
3497
3511
  timestamp: new Date().toISOString(),
3498
3512
  sdkVersion: SDK_VERSION,
3499
3513
  };
3500
- // Attach contactId if known (from a prior identify() call)
3501
- if (this.contactId) {
3502
- event.contactId = this.contactId;
3503
- }
3504
- // Attach groupId if known (from a prior group() call)
3505
- if (this.groupId) {
3506
- event.groupId = this.groupId;
3507
- }
3508
3514
  // Validate event against registered schema (debug mode only)
3509
3515
  this.validateEventSchema(eventType, properties);
3510
3516
  // Check consent before tracking