saasco-sdk 0.1.42 → 0.1.44

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 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.42";
11
+ var version = "0.1.44";
12
12
 
13
13
  const timezones = {
14
14
  'Asia/Barnaul': 'RU',
@@ -555,26 +555,117 @@ function getBrowserContext() {
555
555
  };
556
556
  }
557
557
 
558
- const isBrowser$2 = typeof window !== 'undefined';
559
- const isServer$2 = !isBrowser$2;
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$2) {
571
- // On server, Facebook Pixel is a no-op but doesn't throw
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
- console.warn('Facebook Pixel is already initialized');
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) return;
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
  },
@@ -802,7 +918,7 @@ function coerceMetaConversionsAmValues(properties) {
802
918
  coerceFrom,
803
919
  transform
804
920
  } of coerce) {
805
- const foundValue = findFirstProperty$1(properties, coerceFrom);
921
+ const foundValue = findFirstProperty$2(properties, coerceFrom);
806
922
  if (foundValue !== null && transform) {
807
923
  const transformedValue = transform(foundValue);
808
924
  if (transformedValue) {
@@ -814,6 +930,183 @@ function coerceMetaConversionsAmValues(properties) {
814
930
  }
815
931
  return result;
816
932
  }
933
+ function findFirstProperty$2(properties, keys) {
934
+ for (const key of keys) {
935
+ if (properties[key] !== undefined) {
936
+ return properties[key];
937
+ }
938
+ }
939
+ return null;
940
+ }
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, context) => {
1022
+ if (!isTagReady || !window.pintrk) return;
1023
+ const pinterestEventName = getPinterestEventName(eventName, config.eventMapping);
1024
+ const eventData = buildEventData(properties);
1025
+ // Get enhanced matching data from properties
1026
+ const enhancedMatchData = getPinterestEnhancedMatchData(context || {});
1027
+ logger.debug('Tracking event', {
1028
+ eventName,
1029
+ pinterestEventName,
1030
+ eventData,
1031
+ enhancedMatchData
1032
+ });
1033
+ // Combine event data with enhanced matching data
1034
+ const finalEventData = Object.assign(Object.assign({}, eventData), enhancedMatchData);
1035
+ // Track the event
1036
+ window.pintrk('track', pinterestEventName, finalEventData);
1037
+ },
1038
+ identify: (userId, properties, context) => {
1039
+ // Identify not supported with pinterest tag
1040
+ logger.debug('Identify not supported with pinterest tag', {
1041
+ userId,
1042
+ properties,
1043
+ context
1044
+ });
1045
+ window.pintrk('set', {
1046
+ external_id: userId
1047
+ });
1048
+ window.pintrk('set', {
1049
+ em: properties === null || properties === void 0 ? void 0 : properties['email']
1050
+ });
1051
+ return;
1052
+ }
1053
+ };
1054
+ }
1055
+ function getPinterestEventName(eventName, eventMapping) {
1056
+ // Convert default saasco Page View event by default
1057
+ if (eventName === 'Page View') return 'pagevisit';
1058
+ if (!eventMapping) return eventName.toLowerCase();
1059
+ return eventMapping[eventName] || eventName.toLowerCase();
1060
+ }
1061
+ /**
1062
+ * Build event data for Pinterest tracking
1063
+ * Reference: https://www.pinterest.com/_/_/help/business/article/event-code
1064
+ */
1065
+ function buildEventData(properties) {
1066
+ if (!properties) return {};
1067
+ const eventData = {};
1068
+ // Map common properties to Pinterest event data
1069
+ if (properties['event_id']) eventData['event_id'] = properties['event_id'];
1070
+ if (properties['value']) eventData['value'] = properties['value'];
1071
+ if (properties['currency']) eventData['currency'] = properties['currency'];
1072
+ if (properties['order_quantity']) eventData['order_quantity'] = properties['order_quantity'];
1073
+ if (properties['order_id']) eventData['order_id'] = properties['order_id'];
1074
+ if (properties['promo_code']) eventData['promo_code'] = properties['promo_code'];
1075
+ if (properties['property']) eventData['property'] = properties['property'];
1076
+ if (properties['search_query']) eventData['search_query'] = properties['search_query'];
1077
+ if (properties['video_title']) eventData['video_title'] = properties['video_title'];
1078
+ if (properties['lead_type']) eventData['lead_type'] = properties['lead_type'];
1079
+ if (properties['product_category']) eventData['product_category'] = properties['product_category'];
1080
+ // Handle line items for e-commerce tracking
1081
+ if (properties['line_items'] && Array.isArray(properties['line_items'])) {
1082
+ eventData['line_items'] = properties['line_items'];
1083
+ }
1084
+ // Generate event_id if not provided
1085
+ if (!eventData['event_id']) {
1086
+ eventData['event_id'] = uuid();
1087
+ }
1088
+ return eventData;
1089
+ }
1090
+ /**
1091
+ * Get Pinterest Enhanced Matching values from properties
1092
+ * Reference: https://help.pinterest.com/en/business/article/enhanced-match
1093
+ */
1094
+ function getPinterestEnhancedMatchData(properties) {
1095
+ const enhancedMatch = {};
1096
+ // Email
1097
+ const email = findFirstProperty$1(properties, ['email', 'user_email', 'userEmail', 'email_address', 'emailAddress', 'e_mail', 'E_Mail', 'mail', 'Mail', 'contact_email', 'contactEmail', 'primary_email', 'primaryEmail']);
1098
+ if (email && typeof email === 'string') {
1099
+ // Pinterest expects hashed email for enhanced matching
1100
+ const normalizedEmail = email.trim().toLowerCase();
1101
+ enhancedMatch.em = normalizedEmail;
1102
+ }
1103
+ // External ID (User ID)
1104
+ const externalId = findFirstProperty$1(properties, ['external_id', 'externalId', 'user_id', 'userId', 'id', 'distinctId', 'distinct_id', 'customer_id', 'customerId', 'member_id', 'memberId']);
1105
+ if (externalId) {
1106
+ enhancedMatch.external_id = String(externalId);
1107
+ }
1108
+ return enhancedMatch;
1109
+ }
817
1110
  function findFirstProperty$1(properties, keys) {
818
1111
  for (const key of keys) {
819
1112
  if (properties[key] !== undefined) {
@@ -849,20 +1142,28 @@ const standardTikTokEvents = ['AddPaymentInfo', 'AddToCart', 'AddToWishlist', 'A
849
1142
  /**
850
1143
  * Create a TikTok Pixel integration instance
851
1144
  */
852
- function createTikTokPixelIntegration(config) {
1145
+ function createTikTokPixelIntegration(config, debug) {
853
1146
  let isPixelReady = false;
1147
+ // Create integration logger
1148
+ const integrationLoggerLevel = getIntegrationLoggerLevel('tiktok-pixel', debug);
1149
+ const logger = new AnalyticsLogger({
1150
+ level: integrationLoggerLevel,
1151
+ label: 'Saasco tiktok-pixel'
1152
+ });
854
1153
  return {
855
1154
  name: 'tiktok-pixel',
856
1155
  environments: ['client'],
857
1156
  init: () => tslib.__awaiter(this, void 0, void 0, function* () {
858
1157
  if (isServer$1) {
859
- // On server, TikTok Pixel is a no-op but doesn't throw
860
- // This allows the integration to be registered but remain inactive
1158
+ logger.debug('TikTok Pixel init skipped on server');
861
1159
  return;
862
1160
  }
1161
+ logger.info('Initializing TikTok Pixel', {
1162
+ pixelId: config.pixelId
1163
+ });
863
1164
  // Check if TikTok Pixel is already loaded
864
1165
  if (window.ttq) {
865
- console.warn('TikTok Pixel is already initialized');
1166
+ logger.warn('TikTok Pixel is already initialized');
866
1167
  isPixelReady = true;
867
1168
  return;
868
1169
  }
@@ -913,7 +1214,6 @@ function createTikTokPixelIntegration(config) {
913
1214
  ttq.load(config.pixelId, config.testMode ? {
914
1215
  test_mode: true
915
1216
  } : undefined);
916
- ttq.page();
917
1217
  // If script loads synchronously, mark as ready
918
1218
  if (window.ttq && typeof window.ttq === 'function') {
919
1219
  isPixelReady = true;
@@ -1088,69 +1388,9 @@ function mapTikTokEventParameters(eventName, properties) {
1088
1388
  return Object.keys(params).length > 0 ? params : null;
1089
1389
  }
1090
1390
 
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
1391
  /*
1137
1392
  Minimal analytics integration manager (v0)
1138
1393
  */
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
1394
  class IntegrationManager {
1155
1395
  constructor(config = {}) {
1156
1396
  var _a, _b, _c, _d;
@@ -1158,14 +1398,14 @@ class IntegrationManager {
1158
1398
  this.integrations = new Map();
1159
1399
  this.globalQueue = [];
1160
1400
  this.config = {
1161
- debug: (_a = config.debug) !== null && _a !== void 0 ? _a : false,
1401
+ loggerLevel: (_a = config.loggerLevel) !== null && _a !== void 0 ? _a : LogLevel.ERROR,
1162
1402
  maxQueueSize: (_b = config.maxQueueSize) !== null && _b !== void 0 ? _b : 200,
1163
1403
  maxIntegrationWaitTime: (_c = config.maxIntegrationWaitTime) !== null && _c !== void 0 ? _c : 10000,
1164
1404
  flushInterval: (_d = config.flushInterval) !== null && _d !== void 0 ? _d : 5000
1165
1405
  };
1166
1406
  this.logger = new AnalyticsLogger({
1167
1407
  label: 'Saasco Integrations Debug',
1168
- debug: this.config.debug
1408
+ level: this.config.loggerLevel
1169
1409
  });
1170
1410
  this.initTime = Date.now();
1171
1411
  this.currentEnvironment = typeof window !== 'undefined' ? 'client' : 'server';
@@ -1178,7 +1418,7 @@ class IntegrationManager {
1178
1418
  setContext(next) {
1179
1419
  this.context = Object.assign(Object.assign({}, this.context), next);
1180
1420
  // Only log if we are setting something
1181
- if (Object.keys(this.context).length > 0) this.logger.log('context', this.context);
1421
+ if (Object.keys(this.context).length > 0) this.logger.info('setContext', this.context);
1182
1422
  }
1183
1423
  /**
1184
1424
  * Register and init an integration. When init resolves, we mark it ready and
@@ -1186,17 +1426,18 @@ class IntegrationManager {
1186
1426
  */
1187
1427
  registerIntegration(integration) {
1188
1428
  return tslib.__awaiter(this, void 0, void 0, function* () {
1429
+ this.logger.debug('Register integrations', this.integrations);
1189
1430
  if (this.integrations.has(integration.name)) {
1190
- this.logger.warn(`integration already registered: ${integration.name}`);
1431
+ this.logger.warn(`Integration already registered: ${integration.name}`);
1191
1432
  return;
1192
1433
  }
1193
1434
  // Check environment compatibility
1194
1435
  const isCompatible = integration.environments.includes(this.currentEnvironment);
1195
1436
  if (!isCompatible) {
1196
- this.logger.warn(`skipping ${integration.name}: requires ${integration.environments.join(' or ')} but running in ${this.currentEnvironment}`);
1437
+ this.logger.warn(`Skipping ${integration.name}: requires ${integration.environments.join(' or ')} but running in ${this.currentEnvironment}`);
1197
1438
  return;
1198
1439
  }
1199
- this.logger.log(`registering ${integration.name})`);
1440
+ this.logger.debug(`Registering ${integration.name}`);
1200
1441
  const state = {
1201
1442
  integration,
1202
1443
  status: 'idle'
@@ -1205,17 +1446,21 @@ class IntegrationManager {
1205
1446
  if (integration.init) {
1206
1447
  try {
1207
1448
  state.status = 'loading';
1208
- yield integration.init();
1449
+ const startTime = Date.now();
1450
+ this.logger.debug(`Initializing ${integration.name}`);
1451
+ yield integration.init(this.context);
1209
1452
  state.status = 'ready';
1210
- this.logger.log(`${integration.name} integration ready`);
1453
+ const endTime = Date.now();
1454
+ const duration = endTime - startTime;
1455
+ this.logger.debug(`Integration ${integration.name} ready in ${duration}ms`);
1211
1456
  this.flush();
1212
1457
  } catch (e) {
1213
1458
  state.status = 'error';
1214
- this.logger.error(`failed to init ${integration.name}`, e);
1459
+ this.logger.error(`Failed to init ${integration.name}`, e);
1215
1460
  }
1216
1461
  } else {
1217
1462
  state.status = 'ready';
1218
- this.logger.log(`ready (no init) ${integration.name}`);
1463
+ this.logger.info(`Integration ${integration.name} ready (no init)`);
1219
1464
  this.flush();
1220
1465
  }
1221
1466
  });
@@ -1256,10 +1501,10 @@ class IntegrationManager {
1256
1501
  if (this.globalQueue.length >= this.config.maxQueueSize) {
1257
1502
  // drop oldest
1258
1503
  this.globalQueue.shift();
1259
- this.logger.warn('queue full → dropped oldest');
1504
+ this.logger.warn('Queue full → dropped oldest');
1260
1505
  }
1261
1506
  this.globalQueue.push(envelope);
1262
- this.logger.log(`queued (${this.globalQueue.length})`, envelope);
1507
+ this.logger.debug(`Queued (${this.globalQueue.length})`, envelope);
1263
1508
  return;
1264
1509
  }
1265
1510
  this.deliver(envelope);
@@ -1273,8 +1518,17 @@ class IntegrationManager {
1273
1518
  if (status !== 'ready') continue;
1274
1519
  try {
1275
1520
  if (evt.type === 'track' && integration.track && evt.name) {
1521
+ this.logger.debug(`Track (${integration.name}) ${evt.name}`, {
1522
+ properties: evt.properties,
1523
+ context: evt.context
1524
+ });
1276
1525
  yield integration.track(evt.name, evt.properties, evt.context);
1277
1526
  } else if (evt.type === 'identify' && integration.identify) {
1527
+ this.logger.debug(`Identify (${integration.name})`, {
1528
+ userId: evt.context.distinctId,
1529
+ properties: evt.properties,
1530
+ context: evt.context
1531
+ });
1278
1532
  yield integration.identify(evt.context.distinctId, evt.properties, evt.context);
1279
1533
  }
1280
1534
  } catch (e) {
@@ -1291,7 +1545,7 @@ class IntegrationManager {
1291
1545
  if (this.globalQueue.length === 0) return;
1292
1546
  // If we're not ready don't flush
1293
1547
  if (!this.isReady()) return;
1294
- this.logger.log(`flushing ${this.globalQueue.length} queued events`);
1548
+ this.logger.debug(`flushing ${this.globalQueue.length} queued events`);
1295
1549
  const toSend = this.globalQueue;
1296
1550
  this.globalQueue = [];
1297
1551
  // deliver all events
@@ -1303,8 +1557,14 @@ class IntegrationManager {
1303
1557
  return n;
1304
1558
  }
1305
1559
  isReady() {
1560
+ const timeElapsed = Date.now() - this.initTime;
1306
1561
  // Return if integrations are not ready, or if we haven't waited the max integration wait time
1307
- if (this.readyCount() < this.integrations.size && Date.now() - this.initTime < this.config.maxIntegrationWaitTime) return false;
1562
+ if (this.readyCount() < this.integrations.size && timeElapsed < this.config.maxIntegrationWaitTime) {
1563
+ return false;
1564
+ }
1565
+ if (this.readyCount() < this.integrations.size) {
1566
+ this.logger.info(`proceeding with ${this.readyCount()}/${this.integrations.size} integrations ready after ${timeElapsed}ms wait`);
1567
+ }
1308
1568
  return true;
1309
1569
  }
1310
1570
  /**
@@ -1326,7 +1586,7 @@ class IntegrationManager {
1326
1586
  return;
1327
1587
  }
1328
1588
  this.unloadHandler = () => {
1329
- this.logger.log('page unloading, flushing remaining events');
1589
+ this.logger.info('page unloading, flushing remaining events');
1330
1590
  this.flush();
1331
1591
  };
1332
1592
  // Use both beforeunload and pagehide for better coverage
@@ -1345,7 +1605,8 @@ class IntegrationManager {
1345
1605
  queueLength: this.globalQueue.length,
1346
1606
  readyCount: this.readyCount(),
1347
1607
  periodicFlushEnabled: this.config.flushInterval > 0,
1348
- flushInterval: this.config.flushInterval
1608
+ flushInterval: this.config.flushInterval,
1609
+ context: this.context
1349
1610
  };
1350
1611
  }
1351
1612
  }
@@ -1549,11 +1810,13 @@ class Saasco {
1549
1810
  this.config = config;
1550
1811
  this.lastPageViewPath = '';
1551
1812
  this.isInitialized = false;
1813
+ const loggerLevel = getLoggerLevel(this.config);
1552
1814
  // Initialize logger
1553
1815
  this.logger = new AnalyticsLogger({
1554
- debug: !!this.config.debug,
1816
+ level: loggerLevel,
1555
1817
  label: 'Saasco Debug'
1556
1818
  });
1819
+ this.logger.info('Saasco started');
1557
1820
  if (!config.projectId) {
1558
1821
  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
1822
  return;
@@ -1568,19 +1831,19 @@ class Saasco {
1568
1831
  }, this.config.autoPageTracking);
1569
1832
  // Initialize integration manager
1570
1833
  this.integrationManager = new IntegrationManager({
1571
- debug: this.config.debug,
1834
+ loggerLevel,
1572
1835
  maxQueueSize: 1000
1573
1836
  });
1574
1837
  // Set initial context
1575
1838
  this.integrationManager.setContext({});
1576
1839
  // Initialize integrations immediately (works on both client and server)
1577
1840
  this.initIntegrations().catch(error => {
1578
- this.logger.log('Error initializing integrations:', error);
1841
+ this.logger.info('Error initializing integrations:', error);
1579
1842
  });
1580
1843
  }
1581
1844
  init() {
1582
1845
  if (this.isInitialized) {
1583
- this.logger.log('Saasco is already initialized. Please check your code to ensure that init() is not being called multiple times.');
1846
+ this.logger.info('Saasco is already initialized. Please check your code to ensure that init() is not being called multiple times.');
1584
1847
  return;
1585
1848
  }
1586
1849
  // Export Saasco to the window object for easy access and debugging
@@ -1589,39 +1852,48 @@ class Saasco {
1589
1852
  }
1590
1853
  // Migrate existing localStorage data to cookies for cross-subdomain support
1591
1854
  migrateFromLocalStorage();
1592
- this.logger.log('Saasco initialized', this.config);
1593
- if (this.config.debug) this.logger.log('Debug mode active. This will log all events to the console.');
1594
- if (!this.config.enabled) this.logger.log('Analytics is disabled. No requests will be sent to the server.');
1855
+ this.logger.info('Saasco initialized', this.config);
1856
+ if (this.config.debug) this.logger.info('Debug mode active. This will log all events to the console.');
1857
+ 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
1858
  this.initAutoPageTracking();
1596
1859
  this.isInitialized = true;
1597
1860
  }
1598
1861
  disableDebug() {
1599
- this.logger.log('Debug mode deactivated.');
1862
+ this.logger.info('Debug mode deactivated.');
1600
1863
  this.config.debug = false;
1601
1864
  }
1602
1865
  enableDebug() {
1603
1866
  this.config.debug = true;
1604
- this.logger.log('Debug mode activated.');
1867
+ this.logger.info('Debug mode activated.');
1605
1868
  }
1606
1869
  /**
1607
1870
  * Initialize third-party integrations
1608
1871
  */
1609
1872
  initIntegrations() {
1610
1873
  return tslib.__awaiter(this, void 0, void 0, function* () {
1874
+ this.logger.info('Initialize integrations');
1611
1875
  if (!this.config.integrations || this.config.integrations.length === 0) return;
1876
+ if (!this.config.enabled) {
1877
+ this.logger.info('Analytics is disabled. Skipping integration initialization.');
1878
+ return;
1879
+ }
1612
1880
  for (const integrationConfig of this.config.integrations) {
1613
1881
  try {
1614
1882
  if (integrationConfig.type === 'facebook-pixel') {
1615
- this.logger.log('Registering Facebook Pixel integration with ID:', integrationConfig.config.pixelId);
1616
- const fbIntegration = createFacebookPixelIntegration(integrationConfig.config);
1883
+ this.logger.info('Registering Facebook Pixel integration with ID:', integrationConfig.config.pixelId);
1884
+ const fbIntegration = createFacebookPixelIntegration(integrationConfig.config, integrationConfig.debug);
1617
1885
  yield this.integrationManager.registerIntegration(fbIntegration);
1886
+ } else if (integrationConfig.type === 'pinterest-tag') {
1887
+ this.logger.info('Registering Pinterest Tag integration with ID:', integrationConfig.config.tagId);
1888
+ const pinterestIntegration = createPinterestTagIntegration(integrationConfig.config, integrationConfig.debug);
1889
+ yield this.integrationManager.registerIntegration(pinterestIntegration);
1618
1890
  } else if (integrationConfig.type === 'tiktok-pixel') {
1619
- this.logger.log('Registering TikTok Pixel integration with ID:', integrationConfig.config.pixelId);
1620
- const tiktokIntegration = createTikTokPixelIntegration(integrationConfig.config);
1891
+ this.logger.info('Registering TikTok Pixel integration with ID:', integrationConfig.config.pixelId);
1892
+ const tiktokIntegration = createTikTokPixelIntegration(integrationConfig.config, integrationConfig.debug);
1621
1893
  yield this.integrationManager.registerIntegration(tiktokIntegration);
1622
1894
  }
1623
1895
  } catch (error) {
1624
- this.logger.log(`Failed to register ${integrationConfig.type} integration:`, error);
1896
+ this.logger.info(`Failed to register ${integrationConfig.type} integration:`, error);
1625
1897
  }
1626
1898
  }
1627
1899
  });
@@ -1674,7 +1946,18 @@ class Saasco {
1674
1946
  source: isBrowser ? 'client' : 'server',
1675
1947
  context: JSON.stringify(context || {})
1676
1948
  };
1677
- // Send event to integrations
1949
+ if (data.action === 'Page View') {
1950
+ this.logger.info('Page View', window.location.href, data);
1951
+ } else {
1952
+ this.logger.info('track', data);
1953
+ }
1954
+ if (!this.config.enabled) {
1955
+ return Promise.resolve({
1956
+ success: true,
1957
+ message: 'Analytics is disabled'
1958
+ });
1959
+ }
1960
+ // Only send track to integrations if analytics is enabled
1678
1961
  this.integrationManager.track(action, properties);
1679
1962
  // Send event to API
1680
1963
  return this.doRequest('events', data);
@@ -1719,7 +2002,7 @@ class Saasco {
1719
2002
  // This should only be called when distinctId is null and there is an existing userId.
1720
2003
  // This means the user has logged out and we should reset the session and anonymous IDs
1721
2004
  const userIdChangedToNull = distinctId === null && !!getUserId();
1722
- if (userIdChangedToNull) this.logger.log('User logged out');
2005
+ if (userIdChangedToNull) this.logger.info('User logged out');
1723
2006
  const reset = userIdChangedToNull;
1724
2007
  const sessionId = setSessionId({
1725
2008
  reset
@@ -1730,12 +2013,11 @@ class Saasco {
1730
2013
  // set the distinct Id to the userId
1731
2014
  setUserId(distinctId);
1732
2015
  // Update integration manager context and send identify (IntegrationManager will filter by environment)
1733
- this.integrationManager.setContext({
2016
+ this.integrationManager.setContext(Object.assign({
1734
2017
  distinctId,
1735
2018
  anonymousId,
1736
2019
  sessionId
1737
- });
1738
- this.integrationManager.identify(distinctId, properties);
2020
+ }, properties));
1739
2021
  // No distinctId provided so we don't track the user
1740
2022
  if (!distinctId) return Promise.resolve({
1741
2023
  success: true,
@@ -1752,6 +2034,15 @@ class Saasco {
1752
2034
  payload: properties || {},
1753
2035
  context: context || {}
1754
2036
  };
2037
+ this.logger.info('identify', data);
2038
+ // Only send identify to integrations if analytics is enabled
2039
+ if (!this.config.enabled) {
2040
+ return Promise.resolve({
2041
+ success: true,
2042
+ message: 'Analytics is disabled'
2043
+ });
2044
+ }
2045
+ this.integrationManager.identify(distinctId, properties);
1755
2046
  return this.doRequest('identify', data);
1756
2047
  }
1757
2048
  /**
@@ -1776,11 +2067,6 @@ class Saasco {
1776
2067
  return tslib.__awaiter(this, void 0, void 0, function* () {
1777
2068
  const base = this.config.proxy || 'https://www.saasco.com/api/';
1778
2069
  const url = `${base}${path}`;
1779
- if (data.action === 'Page View') {
1780
- this.logger.log('Page View', window.location.href, data);
1781
- } else {
1782
- this.logger.log(data.action || path, data);
1783
- }
1784
2070
  // If analytics is disabled, don't send the request
1785
2071
  if (this.config.enabled === false) return {
1786
2072
  success: false,
@@ -1824,11 +2110,11 @@ class Saasco {
1824
2110
  if (isServer) return console.warn('Saasco auto page tracking is only available in the browser');
1825
2111
  // Prevent intitializing auto page tracking more than once
1826
2112
  if (window.saascoAutoPageTrackingActive) {
1827
- this.logger.log('Auto Page Tracking already enabled');
2113
+ this.logger.info('Auto Page Tracking already enabled');
1828
2114
  return;
1829
2115
  }
1830
2116
  window.saascoAutoPageTrackingActive = true;
1831
- this.logger.log('Auto Page Tracking enabled');
2117
+ this.logger.info('Auto Page Tracking enabled');
1832
2118
  // Track initial page load
1833
2119
  this.page();
1834
2120
  // Listen for hash changes if hash tracking is enabled
@@ -1852,6 +2138,27 @@ class Saasco {
1852
2138
  return returnValue;
1853
2139
  };
1854
2140
  }
2141
+ getIntegrationsStats() {
2142
+ return this.integrationManager.getStats();
2143
+ }
2144
+ }
2145
+ function getLoggerLevel(config) {
2146
+ // Check URL parameters for debug overrides (browser only)
2147
+ let urlDebug = false;
2148
+ let urlDebugVerbose = false;
2149
+ if (!isServer) {
2150
+ try {
2151
+ const urlParams = new URLSearchParams(window.location.search);
2152
+ urlDebug = urlParams.get('saasco-debug') === 'true';
2153
+ urlDebugVerbose = urlParams.get('saasco-debug-verbose') === 'true';
2154
+ } catch (error) {
2155
+ // Ignore URL parsing errors
2156
+ }
2157
+ }
2158
+ // URL parameters override config
2159
+ const effectiveDebug = urlDebug || config.debug;
2160
+ const effectiveDebugVerbose = urlDebugVerbose || config.debugVerbose;
2161
+ return !!effectiveDebugVerbose ? LogLevel.DEBUG : !!effectiveDebug ? LogLevel.INFO : LogLevel.ERROR;
1855
2162
  }
1856
2163
 
1857
2164
  const browserContextSchema = zod.z.object({
@@ -1880,12 +2187,13 @@ const browserContextSchema = zod.z.object({
1880
2187
  });
1881
2188
 
1882
2189
  exports.IntegrationManager = IntegrationManager;
1883
- exports.Logger = Logger;
1884
2190
  exports.Saasco = Saasco;
1885
2191
  exports.browserContextSchema = browserContextSchema;
1886
2192
  exports.createFacebookPixelIntegration = createFacebookPixelIntegration;
2193
+ exports.createPinterestTagIntegration = createPinterestTagIntegration;
1887
2194
  exports.createTikTokPixelIntegration = createTikTokPixelIntegration;
1888
2195
  exports.getBrowserContext = getBrowserContext;
1889
2196
  exports.standardFacebookEvents = standardFacebookEvents;
2197
+ exports.standardPinterestEvents = standardPinterestEvents;
1890
2198
  exports.standardTikTokEvents = standardTikTokEvents;
1891
2199
  exports.timezones = timezones;