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