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 +1 -1
- package/index.cjs.js +96 -13
- package/index.esm.js +96 -13
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
## 🔒 Privacy
|
|
16
16
|
|
|
17
|
-
By default Saasco is privacy friendly,
|
|
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.
|
|
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)
|
|
514
|
-
|
|
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 =
|
|
603
|
+
const itemStr = getCookie(fullKey);
|
|
528
604
|
if (!itemStr) {
|
|
529
605
|
return null;
|
|
530
606
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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.
|
|
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)
|
|
510
|
-
|
|
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 =
|
|
599
|
+
const itemStr = getCookie(fullKey);
|
|
524
600
|
if (!itemStr) {
|
|
525
601
|
return null;
|
|
526
602
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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.
|
|
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",
|