saasco-sdk 0.1.26 → 0.1.29

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/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  ## 🔒 Privacy
16
16
 
17
- By default Saasco is privacy friendly, using no cookies and obscuring all activity behind Anonymous IDs. However if you want to power more complex user tracking you can identify users and then all events they do will be attributed to them in your CRM. This can assist with customer support but also for things like drip campaigns and other marketing activities.
17
+ By default Saasco is privacy friendly, obscuring all activity behind Anonymous IDs. However if you want to power more complex user tracking you can identify users and then all events they do will be attributed to them in your CRM. This can assist with customer support but also for things like drip campaigns and other marketing activities.
18
18
 
19
19
  ## Getting Started
20
20
 
package/index.cjs.js CHANGED
@@ -4,9 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var tslib = require('tslib');
6
6
  var uuid = require('@lukeed/uuid');
7
+ var psl = require('psl');
7
8
  var zod = require('zod');
8
9
 
9
- var version = "0.1.26";
10
+ var version = "0.1.29";
10
11
 
11
12
  const timezones = {
12
13
  'Asia/Barnaul': 'RU',
@@ -496,12 +497,80 @@ const PREF = 'saasco-sdk';
496
497
  const SESSION_DURATION = 1000 * 60 * 30;
497
498
  const USER_DURATION = 1000 * 60 * 60 * 24 * 365;
498
499
  const data = {};
500
+ function getRootDomain() {
501
+ if (isServer) return;
502
+ const hostname = window.location.hostname;
503
+ if (!psl.isValid(hostname)) {
504
+ // If not a valid domain, don't set domain (for localhost, IP addresses, etc.)
505
+ return undefined;
506
+ }
507
+ const parsed = psl.parse(hostname);
508
+ if ('domain' in parsed && parsed.domain) {
509
+ return parsed.domain;
510
+ }
511
+ return undefined;
512
+ }
513
+ function setCookie(name, value, ttl) {
514
+ if (isServer) return;
515
+ const expires = new Date(Date.now() + ttl).toUTCString();
516
+ const domain = getRootDomain();
517
+ const cookieString = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/${domain ? `; domain=.${domain}` : ''}`;
518
+ document.cookie = cookieString;
519
+ }
520
+ function getCookie(name) {
521
+ if (isServer) return null;
522
+ const nameEQ = name + '=';
523
+ const ca = document.cookie.split(';');
524
+ for (let i = 0; i < ca.length; i++) {
525
+ let c = ca[i];
526
+ while (c.charAt(0) === ' ') c = c.substring(1, c.length);
527
+ if (c.indexOf(nameEQ) === 0) {
528
+ return decodeURIComponent(c.substring(nameEQ.length, c.length));
529
+ }
530
+ }
531
+ return null;
532
+ }
533
+ function deleteCookie(name) {
534
+ if (isServer) return;
535
+ const domain = getRootDomain();
536
+ const cookieString = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/${domain ? `; domain=.${domain}` : ''}`;
537
+ document.cookie = cookieString;
538
+ }
539
+ function migrateFromLocalStorage() {
540
+ if (isServer) return;
541
+ const keysToMigrate = [`${PREF}-session-id`, `${PREF}-anonymous-id`, `${PREF}-user-id`, `${PREF}-session-utm-context`];
542
+ keysToMigrate.forEach(key => {
543
+ try {
544
+ const value = localStorage.getItem(key);
545
+ if (value) {
546
+ // Parse the stored data to get the original TTL
547
+ const item = JSON.parse(value);
548
+ const now = new Date().getTime();
549
+ // Check if the data is still valid
550
+ if (item.expiry > now) {
551
+ // Calculate remaining TTL
552
+ const remainingTtl = item.expiry - now;
553
+ // Store in cookie with remaining TTL
554
+ setCookie(key, value, remainingTtl);
555
+ // Remove from localStorage after successful migration
556
+ localStorage.removeItem(key);
557
+ } else {
558
+ // Data has expired, just remove it
559
+ localStorage.removeItem(key);
560
+ }
561
+ }
562
+ } catch (error) {
563
+ // If there's an error parsing the data, remove the corrupted item
564
+ localStorage.removeItem(key);
565
+ }
566
+ });
567
+ }
499
568
  function storeData(key, value, ttl) {
500
- const item = {
501
- value,
502
- expiry: new Date().getTime() + ttl
503
- };
504
569
  if (isServer) {
570
+ const item = {
571
+ value,
572
+ expiry: new Date().getTime() + ttl
573
+ };
505
574
  if (value === undefined) {
506
575
  delete data[key];
507
576
  return;
@@ -510,8 +579,15 @@ function storeData(key, value, ttl) {
510
579
  return;
511
580
  }
512
581
  const fullKey = `${PREF}-${key}`;
513
- if (value === undefined) return window.localStorage.removeItem(fullKey);
514
- localStorage.setItem(fullKey, JSON.stringify(item));
582
+ if (value === undefined) {
583
+ deleteCookie(fullKey);
584
+ return;
585
+ }
586
+ const item = {
587
+ value,
588
+ expiry: new Date().getTime() + ttl
589
+ };
590
+ setCookie(fullKey, JSON.stringify(item), ttl);
515
591
  }
516
592
  function retrieveData(key) {
517
593
  if (isServer) {
@@ -524,17 +600,22 @@ function retrieveData(key) {
524
600
  return item.value;
525
601
  }
526
602
  const fullKey = `${PREF}-${key}`;
527
- const itemStr = localStorage.getItem(fullKey);
603
+ const itemStr = getCookie(fullKey);
528
604
  if (!itemStr) {
529
605
  return null;
530
606
  }
531
- const item = JSON.parse(itemStr);
532
- const now = new Date();
533
- if (now.getTime() > item.expiry) {
534
- localStorage.removeItem(fullKey);
607
+ try {
608
+ const item = JSON.parse(itemStr);
609
+ if (Date.now() > item.expiry) {
610
+ deleteCookie(fullKey);
611
+ return null;
612
+ }
613
+ return item.value;
614
+ } catch (error) {
615
+ // If JSON parsing fails, remove the corrupted cookie
616
+ deleteCookie(fullKey);
535
617
  return null;
536
618
  }
537
- return item.value;
538
619
  }
539
620
  function getSessionId() {
540
621
  return retrieveData(`session-id`);
@@ -620,6 +701,8 @@ class Saasco {
620
701
  if (isBrowser) {
621
702
  window.saasco = this;
622
703
  }
704
+ // Migrate existing localStorage data to cookies for cross-subdomain support
705
+ migrateFromLocalStorage();
623
706
  this.log('Saasco initialized', this.config);
624
707
  if (this.config.debug) this.log('Debug mode active. This will log all events to the console.');
625
708
  if (!this.config.enabled) this.log('Analytics is disabled. No requests will be sent to the server.');
package/index.esm.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { __awaiter } from 'tslib';
2
2
  import { v4 } from '@lukeed/uuid';
3
+ import { isValid, parse } from 'psl';
3
4
  import { z } from 'zod';
4
5
 
5
- var version = "0.1.26";
6
+ var version = "0.1.29";
6
7
 
7
8
  const timezones = {
8
9
  'Asia/Barnaul': 'RU',
@@ -492,12 +493,80 @@ const PREF = 'saasco-sdk';
492
493
  const SESSION_DURATION = 1000 * 60 * 30;
493
494
  const USER_DURATION = 1000 * 60 * 60 * 24 * 365;
494
495
  const data = {};
496
+ function getRootDomain() {
497
+ if (isServer) return;
498
+ const hostname = window.location.hostname;
499
+ if (!isValid(hostname)) {
500
+ // If not a valid domain, don't set domain (for localhost, IP addresses, etc.)
501
+ return undefined;
502
+ }
503
+ const parsed = parse(hostname);
504
+ if ('domain' in parsed && parsed.domain) {
505
+ return parsed.domain;
506
+ }
507
+ return undefined;
508
+ }
509
+ function setCookie(name, value, ttl) {
510
+ if (isServer) return;
511
+ const expires = new Date(Date.now() + ttl).toUTCString();
512
+ const domain = getRootDomain();
513
+ const cookieString = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/${domain ? `; domain=.${domain}` : ''}`;
514
+ document.cookie = cookieString;
515
+ }
516
+ function getCookie(name) {
517
+ if (isServer) return null;
518
+ const nameEQ = name + '=';
519
+ const ca = document.cookie.split(';');
520
+ for (let i = 0; i < ca.length; i++) {
521
+ let c = ca[i];
522
+ while (c.charAt(0) === ' ') c = c.substring(1, c.length);
523
+ if (c.indexOf(nameEQ) === 0) {
524
+ return decodeURIComponent(c.substring(nameEQ.length, c.length));
525
+ }
526
+ }
527
+ return null;
528
+ }
529
+ function deleteCookie(name) {
530
+ if (isServer) return;
531
+ const domain = getRootDomain();
532
+ const cookieString = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/${domain ? `; domain=.${domain}` : ''}`;
533
+ document.cookie = cookieString;
534
+ }
535
+ function migrateFromLocalStorage() {
536
+ if (isServer) return;
537
+ const keysToMigrate = [`${PREF}-session-id`, `${PREF}-anonymous-id`, `${PREF}-user-id`, `${PREF}-session-utm-context`];
538
+ keysToMigrate.forEach(key => {
539
+ try {
540
+ const value = localStorage.getItem(key);
541
+ if (value) {
542
+ // Parse the stored data to get the original TTL
543
+ const item = JSON.parse(value);
544
+ const now = new Date().getTime();
545
+ // Check if the data is still valid
546
+ if (item.expiry > now) {
547
+ // Calculate remaining TTL
548
+ const remainingTtl = item.expiry - now;
549
+ // Store in cookie with remaining TTL
550
+ setCookie(key, value, remainingTtl);
551
+ // Remove from localStorage after successful migration
552
+ localStorage.removeItem(key);
553
+ } else {
554
+ // Data has expired, just remove it
555
+ localStorage.removeItem(key);
556
+ }
557
+ }
558
+ } catch (error) {
559
+ // If there's an error parsing the data, remove the corrupted item
560
+ localStorage.removeItem(key);
561
+ }
562
+ });
563
+ }
495
564
  function storeData(key, value, ttl) {
496
- const item = {
497
- value,
498
- expiry: new Date().getTime() + ttl
499
- };
500
565
  if (isServer) {
566
+ const item = {
567
+ value,
568
+ expiry: new Date().getTime() + ttl
569
+ };
501
570
  if (value === undefined) {
502
571
  delete data[key];
503
572
  return;
@@ -506,8 +575,15 @@ function storeData(key, value, ttl) {
506
575
  return;
507
576
  }
508
577
  const fullKey = `${PREF}-${key}`;
509
- if (value === undefined) return window.localStorage.removeItem(fullKey);
510
- localStorage.setItem(fullKey, JSON.stringify(item));
578
+ if (value === undefined) {
579
+ deleteCookie(fullKey);
580
+ return;
581
+ }
582
+ const item = {
583
+ value,
584
+ expiry: new Date().getTime() + ttl
585
+ };
586
+ setCookie(fullKey, JSON.stringify(item), ttl);
511
587
  }
512
588
  function retrieveData(key) {
513
589
  if (isServer) {
@@ -520,17 +596,22 @@ function retrieveData(key) {
520
596
  return item.value;
521
597
  }
522
598
  const fullKey = `${PREF}-${key}`;
523
- const itemStr = localStorage.getItem(fullKey);
599
+ const itemStr = getCookie(fullKey);
524
600
  if (!itemStr) {
525
601
  return null;
526
602
  }
527
- const item = JSON.parse(itemStr);
528
- const now = new Date();
529
- if (now.getTime() > item.expiry) {
530
- localStorage.removeItem(fullKey);
603
+ try {
604
+ const item = JSON.parse(itemStr);
605
+ if (Date.now() > item.expiry) {
606
+ deleteCookie(fullKey);
607
+ return null;
608
+ }
609
+ return item.value;
610
+ } catch (error) {
611
+ // If JSON parsing fails, remove the corrupted cookie
612
+ deleteCookie(fullKey);
531
613
  return null;
532
614
  }
533
- return item.value;
534
615
  }
535
616
  function getSessionId() {
536
617
  return retrieveData(`session-id`);
@@ -616,6 +697,8 @@ class Saasco {
616
697
  if (isBrowser) {
617
698
  window.saasco = this;
618
699
  }
700
+ // Migrate existing localStorage data to cookies for cross-subdomain support
701
+ migrateFromLocalStorage();
619
702
  this.log('Saasco initialized', this.config);
620
703
  if (this.config.debug) this.log('Debug mode active. This will log all events to the console.');
621
704
  if (!this.config.enabled) this.log('Analytics is disabled. No requests will be sent to the server.');
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "saasco-sdk",
3
- "version": "0.1.26",
3
+ "version": "0.1.29",
4
4
  "dependencies": {
5
5
  "tslib": "^2.3.0",
6
6
  "@lukeed/uuid": "^2.0.1",
7
- "zod": "^3.22.4"
7
+ "zod": "^3.22.4",
8
+ "psl": "^1.9.0"
8
9
  },
9
10
  "main": "./index.cjs.js",
10
11
  "typings": "./src/index.d.ts",