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