saasco-sdk 0.1.43 → 0.1.45
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/index.cjs.js +372 -115
- package/index.esm.js +371 -115
- package/package.json +1 -1
- package/src/lib/analytics.d.ts +17 -1
- package/src/lib/integrations/facebook-pixel.d.ts +1 -1
- package/src/lib/integrations/index.d.ts +1 -0
- package/src/lib/integrations/integration-manager.d.ts +5 -11
- package/src/lib/integrations/pinterest-tag.d.ts +37 -0
- package/src/lib/integrations/tiktok-pixel.d.ts +1 -1
- package/src/lib/utils/getIntegrationLoggerLevel.d.ts +8 -0
- package/src/lib/utils/index.d.ts +1 -0
- package/src/lib/utils/logger.d.ts +9 -2
package/index.cjs.js
CHANGED
|
@@ -4,11 +4,11 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var tslib = require('tslib');
|
|
6
6
|
var psl = require('psl');
|
|
7
|
-
var jsSha256 = require('js-sha256');
|
|
8
7
|
var uuid$1 = require('@lukeed/uuid');
|
|
8
|
+
var jsSha256 = require('js-sha256');
|
|
9
9
|
var zod = require('zod');
|
|
10
10
|
|
|
11
|
-
var version = "0.1.
|
|
11
|
+
var version = "0.1.45";
|
|
12
12
|
|
|
13
13
|
const timezones = {
|
|
14
14
|
'Asia/Barnaul': 'RU',
|
|
@@ -555,26 +555,117 @@ function getBrowserContext() {
|
|
|
555
555
|
};
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
-
|
|
559
|
-
|
|
558
|
+
var LogLevel;
|
|
559
|
+
(function (LogLevel) {
|
|
560
|
+
LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
|
|
561
|
+
LogLevel[LogLevel["WARN"] = 1] = "WARN";
|
|
562
|
+
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
563
|
+
LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
|
|
564
|
+
})(LogLevel || (LogLevel = {}));
|
|
565
|
+
class AnalyticsLogger {
|
|
566
|
+
constructor(config) {
|
|
567
|
+
this.config = config;
|
|
568
|
+
}
|
|
569
|
+
debug(...args) {
|
|
570
|
+
if (this.config.level < LogLevel.DEBUG) return;
|
|
571
|
+
const icon = '◍';
|
|
572
|
+
const message = `${icon} ${this.config.label}`;
|
|
573
|
+
console.debug(`%c ${message}`, `background: #eee; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Log debug information
|
|
577
|
+
*/
|
|
578
|
+
info(...args) {
|
|
579
|
+
if (this.config.level < LogLevel.INFO) return;
|
|
580
|
+
const icon = '◍';
|
|
581
|
+
const message = `${icon} ${this.config.label}`;
|
|
582
|
+
console.info(`%c ${message}`, `background: #eee; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Log warning information
|
|
586
|
+
*/
|
|
587
|
+
warn(...args) {
|
|
588
|
+
if (this.config.level < LogLevel.WARN) return;
|
|
589
|
+
const icon = '◍';
|
|
590
|
+
const message = `${icon} ${this.config.label}`;
|
|
591
|
+
console.warn(`%c ${message}`, `background: #ffa500; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Log error information
|
|
595
|
+
*/
|
|
596
|
+
error(...args) {
|
|
597
|
+
const icon = '◍';
|
|
598
|
+
const message = `${icon} ${this.config.label} Error`;
|
|
599
|
+
console.error(`%c ${message}`, 'background: red; color: white; padding-right: 6px; border-radius: 2px;', ...args);
|
|
600
|
+
return {
|
|
601
|
+
success: false,
|
|
602
|
+
message: args.join(' ')
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const isBrowser$4 = typeof window !== 'undefined';
|
|
608
|
+
const isServer$4 = !isBrowser$4;
|
|
609
|
+
/**
|
|
610
|
+
* Get the logger level for a specific integration
|
|
611
|
+
* Supports URL parameters like saasco-debug-{integration-type}=true
|
|
612
|
+
* If debug is true or URL param is true, sets level to DEBUG (3), otherwise WARN
|
|
613
|
+
*/
|
|
614
|
+
function getIntegrationLoggerLevel(integrationName, debug) {
|
|
615
|
+
// Check URL parameters for debug overrides (browser only)
|
|
616
|
+
let urlDebug = false;
|
|
617
|
+
if (!isServer$4) {
|
|
618
|
+
try {
|
|
619
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
620
|
+
const paramName = `saasco-debug-${integrationName}`;
|
|
621
|
+
const paramValue = urlParams.get(paramName);
|
|
622
|
+
if (paramValue === 'true') {
|
|
623
|
+
urlDebug = true;
|
|
624
|
+
}
|
|
625
|
+
} catch (error) {
|
|
626
|
+
// Ignore URL parsing errors
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
// URL parameters override config, config overrides default (WARN)
|
|
630
|
+
return urlDebug || debug ? LogLevel.DEBUG : LogLevel.WARN;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Generate a collision-resistant UUID
|
|
635
|
+
* Uses lukeed's UUID v4 implementation for consistent, fast UUID generation
|
|
636
|
+
*/
|
|
637
|
+
function uuid() {
|
|
638
|
+
return uuid$1.v4();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const isBrowser$3 = typeof window !== 'undefined';
|
|
642
|
+
const isServer$3 = !isBrowser$3;
|
|
560
643
|
const standardFacebookEvents = ['AddPaymentInfo', 'AddToCart', 'AddToWishlist', 'CompleteRegistration', 'Contact', 'CustomizeProduct', 'Donate', 'FindLocation', 'InitiateCheckout', 'Lead', 'Purchase', 'Schedule', 'Search', 'StartTrial', 'SubmitApplication', 'Subscribe', 'ViewContent', 'PageView'];
|
|
561
644
|
/**
|
|
562
645
|
* Create a Facebook Pixel integration instance
|
|
563
646
|
*/
|
|
564
|
-
function createFacebookPixelIntegration(config) {
|
|
647
|
+
function createFacebookPixelIntegration(config, debug) {
|
|
565
648
|
let isPixelReady = false;
|
|
649
|
+
// Create integration logger
|
|
650
|
+
const integrationLoggerLevel = getIntegrationLoggerLevel('facebook-pixel', debug);
|
|
651
|
+
const logger = new AnalyticsLogger({
|
|
652
|
+
level: integrationLoggerLevel,
|
|
653
|
+
label: 'Saasco facebook-pixel'
|
|
654
|
+
});
|
|
566
655
|
return {
|
|
567
656
|
name: 'facebook-pixel',
|
|
568
657
|
environments: ['client'],
|
|
569
658
|
init: () => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
570
|
-
if (isServer$
|
|
571
|
-
|
|
572
|
-
// This allows the integration to be registered but remain inactive
|
|
659
|
+
if (isServer$3) {
|
|
660
|
+
logger.debug('Facebook Pixel init skipped on server');
|
|
573
661
|
return;
|
|
574
662
|
}
|
|
663
|
+
logger.info('Initializing Facebook Pixel', {
|
|
664
|
+
pixelId: config.pixelId
|
|
665
|
+
});
|
|
575
666
|
// Check if Facebook Pixel is already loaded
|
|
576
667
|
if (window.fbq || window._fbq) {
|
|
577
|
-
|
|
668
|
+
logger.warn('Facebook Pixel is already initialized');
|
|
578
669
|
isPixelReady = true;
|
|
579
670
|
return;
|
|
580
671
|
}
|
|
@@ -597,9 +688,11 @@ function createFacebookPixelIntegration(config) {
|
|
|
597
688
|
// Add load event listener
|
|
598
689
|
t.onload = () => {
|
|
599
690
|
isPixelReady = true;
|
|
691
|
+
logger.info('Facebook Pixel script loaded successfully');
|
|
600
692
|
resolve();
|
|
601
693
|
};
|
|
602
694
|
t.onerror = () => {
|
|
695
|
+
logger.error('Failed to load Facebook Pixel script');
|
|
603
696
|
reject(new Error('Failed to load Facebook Pixel script'));
|
|
604
697
|
};
|
|
605
698
|
s = b.getElementsByTagName(e)[0];
|
|
@@ -610,33 +703,56 @@ function createFacebookPixelIntegration(config) {
|
|
|
610
703
|
// Allow duplicate PageView events
|
|
611
704
|
window.fbq.allowDuplicatePageViews = true;
|
|
612
705
|
if (config.automaticConfiguration === false) {
|
|
706
|
+
logger.debug('Disabling automatic configuration');
|
|
613
707
|
window.fbq('set', 'autoConfig', false, config.pixelId);
|
|
614
708
|
}
|
|
615
709
|
// Initialize pixel
|
|
710
|
+
logger.debug('Initializing Facebook Pixel with ID', config.pixelId);
|
|
616
711
|
window.fbq('init', config.pixelId);
|
|
617
712
|
// If script loads synchronously, mark as ready
|
|
618
713
|
if (window.fbq && typeof window.fbq === 'function') {
|
|
619
714
|
isPixelReady = true;
|
|
715
|
+
logger.info('Facebook Pixel initialized synchronously');
|
|
620
716
|
resolve();
|
|
621
717
|
}
|
|
622
718
|
} catch (error) {
|
|
719
|
+
logger.error('Error during Facebook Pixel initialization', error);
|
|
623
720
|
reject(error);
|
|
624
721
|
}
|
|
625
722
|
});
|
|
626
723
|
}),
|
|
627
724
|
track: (eventName, properties, context) => {
|
|
628
|
-
if (!isPixelReady || !window.fbq)
|
|
725
|
+
if (!isPixelReady || !window.fbq) {
|
|
726
|
+
logger.warn('Facebook Pixel not ready, skipping track event', {
|
|
727
|
+
eventName
|
|
728
|
+
});
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
629
731
|
const fbEventName = getFacebookEventName(eventName, config.eventMapping);
|
|
630
732
|
const eventsWithParams = ['Purchase', 'StartTrial', 'Subscribe'];
|
|
631
733
|
const isStandardEvent = standardFacebookEvents.includes(fbEventName);
|
|
632
734
|
const trackType = isStandardEvent ? 'track' : 'trackCustom';
|
|
735
|
+
logger.debug('Tracking Facebook Pixel event', {
|
|
736
|
+
originalEvent: eventName,
|
|
737
|
+
fbEventName,
|
|
738
|
+
trackType,
|
|
739
|
+
isStandardEvent,
|
|
740
|
+
properties
|
|
741
|
+
});
|
|
633
742
|
if (eventsWithParams.includes(fbEventName) && properties) {
|
|
634
743
|
const fbParams = {};
|
|
635
744
|
if (properties['value'] !== undefined) fbParams['value'] = properties['value'];
|
|
636
745
|
if (properties['currency'] !== undefined) fbParams['currency'] = properties['currency'];
|
|
637
746
|
if (properties['predicted_ltv'] !== undefined) fbParams['predicted_ltv'] = properties['predicted_ltv'];
|
|
747
|
+
logger.info('Tracking Facebook Pixel event with parameters', {
|
|
748
|
+
event: fbEventName,
|
|
749
|
+
params: fbParams
|
|
750
|
+
});
|
|
638
751
|
window.fbq(trackType, fbEventName, fbParams);
|
|
639
752
|
} else {
|
|
753
|
+
logger.info('Tracking Facebook Pixel event', {
|
|
754
|
+
event: fbEventName
|
|
755
|
+
});
|
|
640
756
|
window.fbq(trackType, fbEventName);
|
|
641
757
|
}
|
|
642
758
|
},
|
|
@@ -823,6 +939,150 @@ function findFirstProperty$1(properties, keys) {
|
|
|
823
939
|
return null;
|
|
824
940
|
}
|
|
825
941
|
|
|
942
|
+
const isBrowser$2 = typeof window !== 'undefined';
|
|
943
|
+
const isServer$2 = !isBrowser$2;
|
|
944
|
+
/**
|
|
945
|
+
* Standard Pinterest events
|
|
946
|
+
* Reference: https://www.pinterest.com/_/_/help/business/article/event-code
|
|
947
|
+
*/
|
|
948
|
+
const standardPinterestEvents = ['checkout', 'addtocart', 'pagevisit', 'signup', 'watchvideo', 'lead', 'search', 'viewcategory', 'custom', 'addpaymentinfo', 'addtowishlist', 'initiatecheckout', 'subscribe', 'viewcontent'];
|
|
949
|
+
/**
|
|
950
|
+
* Create a Pinterest Tag integration instance
|
|
951
|
+
* Documentation: https://help.pinterest.com/en/business/article/install-the-pinterest-tag
|
|
952
|
+
*/
|
|
953
|
+
function createPinterestTagIntegration(config, debug) {
|
|
954
|
+
let isTagReady = false;
|
|
955
|
+
// Create integration logger
|
|
956
|
+
const integrationLoggerLevel = getIntegrationLoggerLevel('pinterest-tag', debug);
|
|
957
|
+
const logger = new AnalyticsLogger({
|
|
958
|
+
level: integrationLoggerLevel,
|
|
959
|
+
label: 'Saasco Pinterest Tag'
|
|
960
|
+
});
|
|
961
|
+
return {
|
|
962
|
+
name: 'pinterest-tag',
|
|
963
|
+
environments: ['client'],
|
|
964
|
+
init: context => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
965
|
+
if (isServer$2) {
|
|
966
|
+
logger.debug('Pinterest Tag init skipped on server');
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
logger.info('Initializing Pinterest Tag', {
|
|
970
|
+
tagId: config.tagId
|
|
971
|
+
});
|
|
972
|
+
// Check if Pinterest Tag is already loaded
|
|
973
|
+
if (window.pintrk || window._pintrk) {
|
|
974
|
+
logger.warn('Pinterest Tag is already initialized');
|
|
975
|
+
isTagReady = true;
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
// Initialize Pinterest Tag
|
|
979
|
+
return new Promise((resolve, reject) => {
|
|
980
|
+
try {
|
|
981
|
+
// Pinterest Tag initialization script
|
|
982
|
+
// Reference: https://help.pinterest.com/en/business/article/install-the-base-code
|
|
983
|
+
(function (e) {
|
|
984
|
+
if (!window.pintrk) {
|
|
985
|
+
window.pintrk = function () {
|
|
986
|
+
window.pintrk.queue.push(Array.prototype.slice.call(arguments));
|
|
987
|
+
};
|
|
988
|
+
const n = window.pintrk;
|
|
989
|
+
n.queue = [];
|
|
990
|
+
n.version = '3.0';
|
|
991
|
+
const t = document.createElement('script');
|
|
992
|
+
t.async = true;
|
|
993
|
+
t.src = e;
|
|
994
|
+
// Add load event listener
|
|
995
|
+
t.onload = () => {
|
|
996
|
+
// base script is loaded; now (re)load tag + fire page via runtime
|
|
997
|
+
window.pintrk('load', config.tagId);
|
|
998
|
+
// initialize the tag
|
|
999
|
+
window.pintrk('page');
|
|
1000
|
+
// delay isTagReady to ensure the tag is loaded before sending events
|
|
1001
|
+
setTimeout(() => {
|
|
1002
|
+
logger.debug('Pinterest Tag initialized');
|
|
1003
|
+
isTagReady = true;
|
|
1004
|
+
resolve();
|
|
1005
|
+
}, 500);
|
|
1006
|
+
};
|
|
1007
|
+
t.onerror = () => {
|
|
1008
|
+
logger.error('Failed to load Pinterest Tag script');
|
|
1009
|
+
reject(new Error('Failed to load Pinterest Tag script'));
|
|
1010
|
+
};
|
|
1011
|
+
const r = document.getElementsByTagName('script')[0];
|
|
1012
|
+
r.parentNode.insertBefore(t, r);
|
|
1013
|
+
}
|
|
1014
|
+
})('https://s.pinimg.com/ct/core.js');
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
logger.error('Failed to initialize Pinterest Tag', error);
|
|
1017
|
+
reject(error);
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
}),
|
|
1021
|
+
track: (eventName, properties) => {
|
|
1022
|
+
if (!isTagReady || !window.pintrk) return;
|
|
1023
|
+
const pinterestEventName = getPinterestEventName(eventName, config.eventMapping);
|
|
1024
|
+
const eventData = buildEventData(properties);
|
|
1025
|
+
logger.debug('Tracking event', {
|
|
1026
|
+
eventName,
|
|
1027
|
+
pinterestEventName,
|
|
1028
|
+
eventData
|
|
1029
|
+
});
|
|
1030
|
+
// Track the event
|
|
1031
|
+
window.pintrk('track', pinterestEventName, eventData);
|
|
1032
|
+
},
|
|
1033
|
+
identify: (userId, properties, context) => {
|
|
1034
|
+
// Identify not supported with pinterest tag
|
|
1035
|
+
logger.debug('Identify not supported with pinterest tag', {
|
|
1036
|
+
userId,
|
|
1037
|
+
properties,
|
|
1038
|
+
context
|
|
1039
|
+
});
|
|
1040
|
+
window.pintrk('set', {
|
|
1041
|
+
external_id: userId
|
|
1042
|
+
});
|
|
1043
|
+
window.pintrk('set', {
|
|
1044
|
+
em: properties === null || properties === void 0 ? void 0 : properties['email']
|
|
1045
|
+
});
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
function getPinterestEventName(eventName, eventMapping) {
|
|
1051
|
+
// Convert default saasco Page View event by default
|
|
1052
|
+
if (eventName === 'Page View') return 'pagevisit';
|
|
1053
|
+
if (!eventMapping) return eventName.toLowerCase();
|
|
1054
|
+
return eventMapping[eventName] || eventName.toLowerCase();
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Build event data for Pinterest tracking
|
|
1058
|
+
* Reference: https://www.pinterest.com/_/_/help/business/article/event-code
|
|
1059
|
+
*/
|
|
1060
|
+
function buildEventData(properties) {
|
|
1061
|
+
if (!properties) return {};
|
|
1062
|
+
const eventData = {};
|
|
1063
|
+
// Map common properties to Pinterest event data
|
|
1064
|
+
if (properties['event_id']) eventData['event_id'] = properties['event_id'];
|
|
1065
|
+
if (properties['value']) eventData['value'] = properties['value'];
|
|
1066
|
+
if (properties['currency']) eventData['currency'] = properties['currency'];
|
|
1067
|
+
if (properties['order_quantity']) eventData['order_quantity'] = properties['order_quantity'];
|
|
1068
|
+
if (properties['order_id']) eventData['order_id'] = properties['order_id'];
|
|
1069
|
+
if (properties['promo_code']) eventData['promo_code'] = properties['promo_code'];
|
|
1070
|
+
if (properties['property']) eventData['property'] = properties['property'];
|
|
1071
|
+
if (properties['search_query']) eventData['search_query'] = properties['search_query'];
|
|
1072
|
+
if (properties['video_title']) eventData['video_title'] = properties['video_title'];
|
|
1073
|
+
if (properties['lead_type']) eventData['lead_type'] = properties['lead_type'];
|
|
1074
|
+
if (properties['product_category']) eventData['product_category'] = properties['product_category'];
|
|
1075
|
+
// Handle line items for e-commerce tracking
|
|
1076
|
+
if (properties['line_items'] && Array.isArray(properties['line_items'])) {
|
|
1077
|
+
eventData['line_items'] = properties['line_items'];
|
|
1078
|
+
}
|
|
1079
|
+
// Generate event_id if not provided
|
|
1080
|
+
if (!eventData['event_id']) {
|
|
1081
|
+
eventData['event_id'] = uuid();
|
|
1082
|
+
}
|
|
1083
|
+
return eventData;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
826
1086
|
/**
|
|
827
1087
|
* TikTok Pixel Integration
|
|
828
1088
|
*
|
|
@@ -849,20 +1109,28 @@ const standardTikTokEvents = ['AddPaymentInfo', 'AddToCart', 'AddToWishlist', 'A
|
|
|
849
1109
|
/**
|
|
850
1110
|
* Create a TikTok Pixel integration instance
|
|
851
1111
|
*/
|
|
852
|
-
function createTikTokPixelIntegration(config) {
|
|
1112
|
+
function createTikTokPixelIntegration(config, debug) {
|
|
853
1113
|
let isPixelReady = false;
|
|
1114
|
+
// Create integration logger
|
|
1115
|
+
const integrationLoggerLevel = getIntegrationLoggerLevel('tiktok-pixel', debug);
|
|
1116
|
+
const logger = new AnalyticsLogger({
|
|
1117
|
+
level: integrationLoggerLevel,
|
|
1118
|
+
label: 'Saasco tiktok-pixel'
|
|
1119
|
+
});
|
|
854
1120
|
return {
|
|
855
1121
|
name: 'tiktok-pixel',
|
|
856
1122
|
environments: ['client'],
|
|
857
1123
|
init: () => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
858
1124
|
if (isServer$1) {
|
|
859
|
-
|
|
860
|
-
// This allows the integration to be registered but remain inactive
|
|
1125
|
+
logger.debug('TikTok Pixel init skipped on server');
|
|
861
1126
|
return;
|
|
862
1127
|
}
|
|
1128
|
+
logger.info('Initializing TikTok Pixel', {
|
|
1129
|
+
pixelId: config.pixelId
|
|
1130
|
+
});
|
|
863
1131
|
// Check if TikTok Pixel is already loaded
|
|
864
1132
|
if (window.ttq) {
|
|
865
|
-
|
|
1133
|
+
logger.warn('TikTok Pixel is already initialized');
|
|
866
1134
|
isPixelReady = true;
|
|
867
1135
|
return;
|
|
868
1136
|
}
|
|
@@ -913,7 +1181,6 @@ function createTikTokPixelIntegration(config) {
|
|
|
913
1181
|
ttq.load(config.pixelId, config.testMode ? {
|
|
914
1182
|
test_mode: true
|
|
915
1183
|
} : undefined);
|
|
916
|
-
ttq.page();
|
|
917
1184
|
// If script loads synchronously, mark as ready
|
|
918
1185
|
if (window.ttq && typeof window.ttq === 'function') {
|
|
919
1186
|
isPixelReady = true;
|
|
@@ -1088,69 +1355,9 @@ function mapTikTokEventParameters(eventName, properties) {
|
|
|
1088
1355
|
return Object.keys(params).length > 0 ? params : null;
|
|
1089
1356
|
}
|
|
1090
1357
|
|
|
1091
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1092
|
-
class AnalyticsLogger {
|
|
1093
|
-
constructor(config) {
|
|
1094
|
-
this.config = config;
|
|
1095
|
-
}
|
|
1096
|
-
/**
|
|
1097
|
-
* Log debug information
|
|
1098
|
-
*/
|
|
1099
|
-
log(...args) {
|
|
1100
|
-
if (!this.config.debug) return;
|
|
1101
|
-
const icon = '◍';
|
|
1102
|
-
const message = `${icon} ${this.config.label}`;
|
|
1103
|
-
console.info(`%c ${message}`, `background: #eee; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
|
|
1104
|
-
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Log warning information
|
|
1107
|
-
*/
|
|
1108
|
-
warn(...args) {
|
|
1109
|
-
if (!this.config.debug) return;
|
|
1110
|
-
const icon = '◍';
|
|
1111
|
-
const message = `${icon} ${this.config.label}`;
|
|
1112
|
-
console.warn(`%c ${message}`, `background: #ffa500; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
|
|
1113
|
-
}
|
|
1114
|
-
/**
|
|
1115
|
-
* Log error information
|
|
1116
|
-
*/
|
|
1117
|
-
error(...args) {
|
|
1118
|
-
const icon = '◍';
|
|
1119
|
-
const message = `${icon} ${this.config.label} Error`;
|
|
1120
|
-
console.error(`%c ${message}`, 'background: red; color: white; padding-right: 6px; border-radius: 2px;', ...args);
|
|
1121
|
-
return {
|
|
1122
|
-
success: false,
|
|
1123
|
-
message: args.join(' ')
|
|
1124
|
-
};
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
/**
|
|
1129
|
-
* Generate a collision-resistant UUID
|
|
1130
|
-
* Uses lukeed's UUID v4 implementation for consistent, fast UUID generation
|
|
1131
|
-
*/
|
|
1132
|
-
function uuid() {
|
|
1133
|
-
return uuid$1.v4();
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
1358
|
/*
|
|
1137
1359
|
Minimal analytics integration manager (v0)
|
|
1138
1360
|
*/
|
|
1139
|
-
class Logger {
|
|
1140
|
-
constructor(label = 'Analytics', debug = false) {
|
|
1141
|
-
this.label = label;
|
|
1142
|
-
this.debug = debug;
|
|
1143
|
-
}
|
|
1144
|
-
log(...args) {
|
|
1145
|
-
if (this.debug) console.log(`[${this.label}]`, ...args);
|
|
1146
|
-
}
|
|
1147
|
-
warn(...args) {
|
|
1148
|
-
console.warn(`[${this.label}]`, ...args);
|
|
1149
|
-
}
|
|
1150
|
-
error(...args) {
|
|
1151
|
-
console.error(`[${this.label}]`, ...args);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
1361
|
class IntegrationManager {
|
|
1155
1362
|
constructor(config = {}) {
|
|
1156
1363
|
var _a, _b, _c, _d;
|
|
@@ -1158,14 +1365,14 @@ class IntegrationManager {
|
|
|
1158
1365
|
this.integrations = new Map();
|
|
1159
1366
|
this.globalQueue = [];
|
|
1160
1367
|
this.config = {
|
|
1161
|
-
|
|
1368
|
+
loggerLevel: (_a = config.loggerLevel) !== null && _a !== void 0 ? _a : LogLevel.ERROR,
|
|
1162
1369
|
maxQueueSize: (_b = config.maxQueueSize) !== null && _b !== void 0 ? _b : 200,
|
|
1163
1370
|
maxIntegrationWaitTime: (_c = config.maxIntegrationWaitTime) !== null && _c !== void 0 ? _c : 10000,
|
|
1164
1371
|
flushInterval: (_d = config.flushInterval) !== null && _d !== void 0 ? _d : 5000
|
|
1165
1372
|
};
|
|
1166
1373
|
this.logger = new AnalyticsLogger({
|
|
1167
1374
|
label: 'Saasco Integrations Debug',
|
|
1168
|
-
|
|
1375
|
+
level: this.config.loggerLevel
|
|
1169
1376
|
});
|
|
1170
1377
|
this.initTime = Date.now();
|
|
1171
1378
|
this.currentEnvironment = typeof window !== 'undefined' ? 'client' : 'server';
|
|
@@ -1178,7 +1385,7 @@ class IntegrationManager {
|
|
|
1178
1385
|
setContext(next) {
|
|
1179
1386
|
this.context = Object.assign(Object.assign({}, this.context), next);
|
|
1180
1387
|
// Only log if we are setting something
|
|
1181
|
-
if (Object.keys(this.context).length > 0) this.logger.
|
|
1388
|
+
if (Object.keys(this.context).length > 0) this.logger.info('setContext', this.context);
|
|
1182
1389
|
}
|
|
1183
1390
|
/**
|
|
1184
1391
|
* Register and init an integration. When init resolves, we mark it ready and
|
|
@@ -1186,17 +1393,18 @@ class IntegrationManager {
|
|
|
1186
1393
|
*/
|
|
1187
1394
|
registerIntegration(integration) {
|
|
1188
1395
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1396
|
+
this.logger.debug('Register integrations', this.integrations);
|
|
1189
1397
|
if (this.integrations.has(integration.name)) {
|
|
1190
|
-
this.logger.warn(`
|
|
1398
|
+
this.logger.warn(`Integration already registered: ${integration.name}`);
|
|
1191
1399
|
return;
|
|
1192
1400
|
}
|
|
1193
1401
|
// Check environment compatibility
|
|
1194
1402
|
const isCompatible = integration.environments.includes(this.currentEnvironment);
|
|
1195
1403
|
if (!isCompatible) {
|
|
1196
|
-
this.logger.warn(`
|
|
1404
|
+
this.logger.warn(`Skipping ${integration.name}: requires ${integration.environments.join(' or ')} but running in ${this.currentEnvironment}`);
|
|
1197
1405
|
return;
|
|
1198
1406
|
}
|
|
1199
|
-
this.logger.
|
|
1407
|
+
this.logger.debug(`Registering ${integration.name}`);
|
|
1200
1408
|
const state = {
|
|
1201
1409
|
integration,
|
|
1202
1410
|
status: 'idle'
|
|
@@ -1205,17 +1413,21 @@ class IntegrationManager {
|
|
|
1205
1413
|
if (integration.init) {
|
|
1206
1414
|
try {
|
|
1207
1415
|
state.status = 'loading';
|
|
1208
|
-
|
|
1416
|
+
const startTime = Date.now();
|
|
1417
|
+
this.logger.debug(`Initializing ${integration.name}`);
|
|
1418
|
+
yield integration.init(this.context);
|
|
1209
1419
|
state.status = 'ready';
|
|
1210
|
-
|
|
1420
|
+
const endTime = Date.now();
|
|
1421
|
+
const duration = endTime - startTime;
|
|
1422
|
+
this.logger.debug(`Integration ${integration.name} ready in ${duration}ms`);
|
|
1211
1423
|
this.flush();
|
|
1212
1424
|
} catch (e) {
|
|
1213
1425
|
state.status = 'error';
|
|
1214
|
-
this.logger.error(`
|
|
1426
|
+
this.logger.error(`Failed to init ${integration.name}`, e);
|
|
1215
1427
|
}
|
|
1216
1428
|
} else {
|
|
1217
1429
|
state.status = 'ready';
|
|
1218
|
-
this.logger.
|
|
1430
|
+
this.logger.info(`Integration ${integration.name} ready (no init)`);
|
|
1219
1431
|
this.flush();
|
|
1220
1432
|
}
|
|
1221
1433
|
});
|
|
@@ -1256,10 +1468,10 @@ class IntegrationManager {
|
|
|
1256
1468
|
if (this.globalQueue.length >= this.config.maxQueueSize) {
|
|
1257
1469
|
// drop oldest
|
|
1258
1470
|
this.globalQueue.shift();
|
|
1259
|
-
this.logger.warn('
|
|
1471
|
+
this.logger.warn('Queue full → dropped oldest');
|
|
1260
1472
|
}
|
|
1261
1473
|
this.globalQueue.push(envelope);
|
|
1262
|
-
this.logger.
|
|
1474
|
+
this.logger.debug(`Queued (${this.globalQueue.length})`, envelope);
|
|
1263
1475
|
return;
|
|
1264
1476
|
}
|
|
1265
1477
|
this.deliver(envelope);
|
|
@@ -1273,8 +1485,17 @@ class IntegrationManager {
|
|
|
1273
1485
|
if (status !== 'ready') continue;
|
|
1274
1486
|
try {
|
|
1275
1487
|
if (evt.type === 'track' && integration.track && evt.name) {
|
|
1488
|
+
this.logger.debug(`Track (${integration.name}) ${evt.name}`, {
|
|
1489
|
+
properties: evt.properties,
|
|
1490
|
+
context: evt.context
|
|
1491
|
+
});
|
|
1276
1492
|
yield integration.track(evt.name, evt.properties, evt.context);
|
|
1277
1493
|
} else if (evt.type === 'identify' && integration.identify) {
|
|
1494
|
+
this.logger.debug(`Identify (${integration.name})`, {
|
|
1495
|
+
userId: evt.context.distinctId,
|
|
1496
|
+
properties: evt.properties,
|
|
1497
|
+
context: evt.context
|
|
1498
|
+
});
|
|
1278
1499
|
yield integration.identify(evt.context.distinctId, evt.properties, evt.context);
|
|
1279
1500
|
}
|
|
1280
1501
|
} catch (e) {
|
|
@@ -1291,7 +1512,7 @@ class IntegrationManager {
|
|
|
1291
1512
|
if (this.globalQueue.length === 0) return;
|
|
1292
1513
|
// If we're not ready don't flush
|
|
1293
1514
|
if (!this.isReady()) return;
|
|
1294
|
-
this.logger.
|
|
1515
|
+
this.logger.debug(`flushing ${this.globalQueue.length} queued events`);
|
|
1295
1516
|
const toSend = this.globalQueue;
|
|
1296
1517
|
this.globalQueue = [];
|
|
1297
1518
|
// deliver all events
|
|
@@ -1303,8 +1524,14 @@ class IntegrationManager {
|
|
|
1303
1524
|
return n;
|
|
1304
1525
|
}
|
|
1305
1526
|
isReady() {
|
|
1527
|
+
const timeElapsed = Date.now() - this.initTime;
|
|
1306
1528
|
// Return if integrations are not ready, or if we haven't waited the max integration wait time
|
|
1307
|
-
if (this.readyCount() < this.integrations.size &&
|
|
1529
|
+
if (this.readyCount() < this.integrations.size && timeElapsed < this.config.maxIntegrationWaitTime) {
|
|
1530
|
+
return false;
|
|
1531
|
+
}
|
|
1532
|
+
if (this.readyCount() < this.integrations.size) {
|
|
1533
|
+
this.logger.info(`proceeding with ${this.readyCount()}/${this.integrations.size} integrations ready after ${timeElapsed}ms wait`);
|
|
1534
|
+
}
|
|
1308
1535
|
return true;
|
|
1309
1536
|
}
|
|
1310
1537
|
/**
|
|
@@ -1326,7 +1553,7 @@ class IntegrationManager {
|
|
|
1326
1553
|
return;
|
|
1327
1554
|
}
|
|
1328
1555
|
this.unloadHandler = () => {
|
|
1329
|
-
this.logger.
|
|
1556
|
+
this.logger.info('page unloading, flushing remaining events');
|
|
1330
1557
|
this.flush();
|
|
1331
1558
|
};
|
|
1332
1559
|
// Use both beforeunload and pagehide for better coverage
|
|
@@ -1345,7 +1572,8 @@ class IntegrationManager {
|
|
|
1345
1572
|
queueLength: this.globalQueue.length,
|
|
1346
1573
|
readyCount: this.readyCount(),
|
|
1347
1574
|
periodicFlushEnabled: this.config.flushInterval > 0,
|
|
1348
|
-
flushInterval: this.config.flushInterval
|
|
1575
|
+
flushInterval: this.config.flushInterval,
|
|
1576
|
+
context: this.context
|
|
1349
1577
|
};
|
|
1350
1578
|
}
|
|
1351
1579
|
}
|
|
@@ -1549,11 +1777,13 @@ class Saasco {
|
|
|
1549
1777
|
this.config = config;
|
|
1550
1778
|
this.lastPageViewPath = '';
|
|
1551
1779
|
this.isInitialized = false;
|
|
1780
|
+
const loggerLevel = getLoggerLevel(this.config);
|
|
1552
1781
|
// Initialize logger
|
|
1553
1782
|
this.logger = new AnalyticsLogger({
|
|
1554
|
-
|
|
1783
|
+
level: loggerLevel,
|
|
1555
1784
|
label: 'Saasco Debug'
|
|
1556
1785
|
});
|
|
1786
|
+
this.logger.info('Saasco started');
|
|
1557
1787
|
if (!config.projectId) {
|
|
1558
1788
|
this.logger.error("Project ID is required but has not been provided. If you are using an env variable make sure it's set correctly.");
|
|
1559
1789
|
return;
|
|
@@ -1568,19 +1798,19 @@ class Saasco {
|
|
|
1568
1798
|
}, this.config.autoPageTracking);
|
|
1569
1799
|
// Initialize integration manager
|
|
1570
1800
|
this.integrationManager = new IntegrationManager({
|
|
1571
|
-
|
|
1801
|
+
loggerLevel,
|
|
1572
1802
|
maxQueueSize: 1000
|
|
1573
1803
|
});
|
|
1574
1804
|
// Set initial context
|
|
1575
1805
|
this.integrationManager.setContext({});
|
|
1576
1806
|
// Initialize integrations immediately (works on both client and server)
|
|
1577
1807
|
this.initIntegrations().catch(error => {
|
|
1578
|
-
this.logger.
|
|
1808
|
+
this.logger.info('Error initializing integrations:', error);
|
|
1579
1809
|
});
|
|
1580
1810
|
}
|
|
1581
1811
|
init() {
|
|
1582
1812
|
if (this.isInitialized) {
|
|
1583
|
-
this.logger.
|
|
1813
|
+
this.logger.info('Saasco is already initialized. Please check your code to ensure that init() is not being called multiple times.');
|
|
1584
1814
|
return;
|
|
1585
1815
|
}
|
|
1586
1816
|
// Export Saasco to the window object for easy access and debugging
|
|
@@ -1589,43 +1819,48 @@ class Saasco {
|
|
|
1589
1819
|
}
|
|
1590
1820
|
// Migrate existing localStorage data to cookies for cross-subdomain support
|
|
1591
1821
|
migrateFromLocalStorage();
|
|
1592
|
-
this.logger.
|
|
1593
|
-
if (this.config.debug) this.logger.
|
|
1594
|
-
if (!this.config.enabled) this.logger.
|
|
1822
|
+
this.logger.info('Saasco initialized', this.config);
|
|
1823
|
+
if (this.config.debug) this.logger.info('Debug mode active. This will log all events to the console.');
|
|
1824
|
+
if (!this.config.enabled) this.logger.info(`Analytics is disabled. No requests will be sent to the server and no integrations will be initialized. ${this.config.debug ? 'Debug mode is active and will still log information.' : ''}`);
|
|
1595
1825
|
this.initAutoPageTracking();
|
|
1596
1826
|
this.isInitialized = true;
|
|
1597
1827
|
}
|
|
1598
1828
|
disableDebug() {
|
|
1599
|
-
this.logger.
|
|
1829
|
+
this.logger.info('Debug mode deactivated.');
|
|
1600
1830
|
this.config.debug = false;
|
|
1601
1831
|
}
|
|
1602
1832
|
enableDebug() {
|
|
1603
1833
|
this.config.debug = true;
|
|
1604
|
-
this.logger.
|
|
1834
|
+
this.logger.info('Debug mode activated.');
|
|
1605
1835
|
}
|
|
1606
1836
|
/**
|
|
1607
1837
|
* Initialize third-party integrations
|
|
1608
1838
|
*/
|
|
1609
1839
|
initIntegrations() {
|
|
1610
1840
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1841
|
+
this.logger.info('Initialize integrations');
|
|
1611
1842
|
if (!this.config.integrations || this.config.integrations.length === 0) return;
|
|
1612
1843
|
if (!this.config.enabled) {
|
|
1613
|
-
this.logger.
|
|
1844
|
+
this.logger.info('Analytics is disabled. Skipping integration initialization.');
|
|
1614
1845
|
return;
|
|
1615
1846
|
}
|
|
1616
1847
|
for (const integrationConfig of this.config.integrations) {
|
|
1617
1848
|
try {
|
|
1618
1849
|
if (integrationConfig.type === 'facebook-pixel') {
|
|
1619
|
-
this.logger.
|
|
1620
|
-
const fbIntegration = createFacebookPixelIntegration(integrationConfig.config);
|
|
1850
|
+
this.logger.info('Registering Facebook Pixel integration with ID:', integrationConfig.config.pixelId);
|
|
1851
|
+
const fbIntegration = createFacebookPixelIntegration(integrationConfig.config, integrationConfig.debug);
|
|
1621
1852
|
yield this.integrationManager.registerIntegration(fbIntegration);
|
|
1853
|
+
} else if (integrationConfig.type === 'pinterest-tag') {
|
|
1854
|
+
this.logger.info('Registering Pinterest Tag integration with ID:', integrationConfig.config.tagId);
|
|
1855
|
+
const pinterestIntegration = createPinterestTagIntegration(integrationConfig.config, integrationConfig.debug);
|
|
1856
|
+
yield this.integrationManager.registerIntegration(pinterestIntegration);
|
|
1622
1857
|
} else if (integrationConfig.type === 'tiktok-pixel') {
|
|
1623
|
-
this.logger.
|
|
1624
|
-
const tiktokIntegration = createTikTokPixelIntegration(integrationConfig.config);
|
|
1858
|
+
this.logger.info('Registering TikTok Pixel integration with ID:', integrationConfig.config.pixelId);
|
|
1859
|
+
const tiktokIntegration = createTikTokPixelIntegration(integrationConfig.config, integrationConfig.debug);
|
|
1625
1860
|
yield this.integrationManager.registerIntegration(tiktokIntegration);
|
|
1626
1861
|
}
|
|
1627
1862
|
} catch (error) {
|
|
1628
|
-
this.logger.
|
|
1863
|
+
this.logger.info(`Failed to register ${integrationConfig.type} integration:`, error);
|
|
1629
1864
|
}
|
|
1630
1865
|
}
|
|
1631
1866
|
});
|
|
@@ -1679,9 +1914,9 @@ class Saasco {
|
|
|
1679
1914
|
context: JSON.stringify(context || {})
|
|
1680
1915
|
};
|
|
1681
1916
|
if (data.action === 'Page View') {
|
|
1682
|
-
this.logger.
|
|
1917
|
+
this.logger.info('Page View', window.location.href, data);
|
|
1683
1918
|
} else {
|
|
1684
|
-
this.logger.
|
|
1919
|
+
this.logger.info('track', data);
|
|
1685
1920
|
}
|
|
1686
1921
|
if (!this.config.enabled) {
|
|
1687
1922
|
return Promise.resolve({
|
|
@@ -1734,7 +1969,7 @@ class Saasco {
|
|
|
1734
1969
|
// This should only be called when distinctId is null and there is an existing userId.
|
|
1735
1970
|
// This means the user has logged out and we should reset the session and anonymous IDs
|
|
1736
1971
|
const userIdChangedToNull = distinctId === null && !!getUserId();
|
|
1737
|
-
if (userIdChangedToNull) this.logger.
|
|
1972
|
+
if (userIdChangedToNull) this.logger.info('User logged out');
|
|
1738
1973
|
const reset = userIdChangedToNull;
|
|
1739
1974
|
const sessionId = setSessionId({
|
|
1740
1975
|
reset
|
|
@@ -1745,11 +1980,11 @@ class Saasco {
|
|
|
1745
1980
|
// set the distinct Id to the userId
|
|
1746
1981
|
setUserId(distinctId);
|
|
1747
1982
|
// Update integration manager context and send identify (IntegrationManager will filter by environment)
|
|
1748
|
-
this.integrationManager.setContext({
|
|
1983
|
+
this.integrationManager.setContext(Object.assign({
|
|
1749
1984
|
distinctId,
|
|
1750
1985
|
anonymousId,
|
|
1751
1986
|
sessionId
|
|
1752
|
-
});
|
|
1987
|
+
}, properties));
|
|
1753
1988
|
// No distinctId provided so we don't track the user
|
|
1754
1989
|
if (!distinctId) return Promise.resolve({
|
|
1755
1990
|
success: true,
|
|
@@ -1766,7 +2001,7 @@ class Saasco {
|
|
|
1766
2001
|
payload: properties || {},
|
|
1767
2002
|
context: context || {}
|
|
1768
2003
|
};
|
|
1769
|
-
this.logger.
|
|
2004
|
+
this.logger.info('identify', data);
|
|
1770
2005
|
// Only send identify to integrations if analytics is enabled
|
|
1771
2006
|
if (!this.config.enabled) {
|
|
1772
2007
|
return Promise.resolve({
|
|
@@ -1842,11 +2077,11 @@ class Saasco {
|
|
|
1842
2077
|
if (isServer) return console.warn('Saasco auto page tracking is only available in the browser');
|
|
1843
2078
|
// Prevent intitializing auto page tracking more than once
|
|
1844
2079
|
if (window.saascoAutoPageTrackingActive) {
|
|
1845
|
-
this.logger.
|
|
2080
|
+
this.logger.info('Auto Page Tracking already enabled');
|
|
1846
2081
|
return;
|
|
1847
2082
|
}
|
|
1848
2083
|
window.saascoAutoPageTrackingActive = true;
|
|
1849
|
-
this.logger.
|
|
2084
|
+
this.logger.info('Auto Page Tracking enabled');
|
|
1850
2085
|
// Track initial page load
|
|
1851
2086
|
this.page();
|
|
1852
2087
|
// Listen for hash changes if hash tracking is enabled
|
|
@@ -1870,6 +2105,27 @@ class Saasco {
|
|
|
1870
2105
|
return returnValue;
|
|
1871
2106
|
};
|
|
1872
2107
|
}
|
|
2108
|
+
getIntegrationsStats() {
|
|
2109
|
+
return this.integrationManager.getStats();
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
function getLoggerLevel(config) {
|
|
2113
|
+
// Check URL parameters for debug overrides (browser only)
|
|
2114
|
+
let urlDebug = false;
|
|
2115
|
+
let urlDebugVerbose = false;
|
|
2116
|
+
if (!isServer) {
|
|
2117
|
+
try {
|
|
2118
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
2119
|
+
urlDebug = urlParams.get('saasco-debug') === 'true';
|
|
2120
|
+
urlDebugVerbose = urlParams.get('saasco-debug-verbose') === 'true';
|
|
2121
|
+
} catch (error) {
|
|
2122
|
+
// Ignore URL parsing errors
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
// URL parameters override config
|
|
2126
|
+
const effectiveDebug = urlDebug || config.debug;
|
|
2127
|
+
const effectiveDebugVerbose = urlDebugVerbose || config.debugVerbose;
|
|
2128
|
+
return !!effectiveDebugVerbose ? LogLevel.DEBUG : !!effectiveDebug ? LogLevel.INFO : LogLevel.ERROR;
|
|
1873
2129
|
}
|
|
1874
2130
|
|
|
1875
2131
|
const browserContextSchema = zod.z.object({
|
|
@@ -1898,12 +2154,13 @@ const browserContextSchema = zod.z.object({
|
|
|
1898
2154
|
});
|
|
1899
2155
|
|
|
1900
2156
|
exports.IntegrationManager = IntegrationManager;
|
|
1901
|
-
exports.Logger = Logger;
|
|
1902
2157
|
exports.Saasco = Saasco;
|
|
1903
2158
|
exports.browserContextSchema = browserContextSchema;
|
|
1904
2159
|
exports.createFacebookPixelIntegration = createFacebookPixelIntegration;
|
|
2160
|
+
exports.createPinterestTagIntegration = createPinterestTagIntegration;
|
|
1905
2161
|
exports.createTikTokPixelIntegration = createTikTokPixelIntegration;
|
|
1906
2162
|
exports.getBrowserContext = getBrowserContext;
|
|
1907
2163
|
exports.standardFacebookEvents = standardFacebookEvents;
|
|
2164
|
+
exports.standardPinterestEvents = standardPinterestEvents;
|
|
1908
2165
|
exports.standardTikTokEvents = standardTikTokEvents;
|
|
1909
2166
|
exports.timezones = timezones;
|