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 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.43";
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
- 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
  },
@@ -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
- // On server, TikTok Pixel is a no-op but doesn't throw
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
- console.warn('TikTok Pixel is already initialized');
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
- debug: (_a = config.debug) !== null && _a !== void 0 ? _a : false,
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
- debug: this.config.debug
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.log('context', this.context);
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(`integration already registered: ${integration.name}`);
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(`skipping ${integration.name}: requires ${integration.environments.join(' or ')} but running in ${this.currentEnvironment}`);
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.log(`registering ${integration.name})`);
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
- yield integration.init();
1416
+ const startTime = Date.now();
1417
+ this.logger.debug(`Initializing ${integration.name}`);
1418
+ yield integration.init(this.context);
1209
1419
  state.status = 'ready';
1210
- this.logger.log(`${integration.name} integration ready`);
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(`failed to init ${integration.name}`, e);
1426
+ this.logger.error(`Failed to init ${integration.name}`, e);
1215
1427
  }
1216
1428
  } else {
1217
1429
  state.status = 'ready';
1218
- this.logger.log(`ready (no init) ${integration.name}`);
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('queue full → dropped oldest');
1471
+ this.logger.warn('Queue full → dropped oldest');
1260
1472
  }
1261
1473
  this.globalQueue.push(envelope);
1262
- this.logger.log(`queued (${this.globalQueue.length})`, envelope);
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.log(`flushing ${this.globalQueue.length} queued events`);
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 && Date.now() - this.initTime < this.config.maxIntegrationWaitTime) return false;
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.log('page unloading, flushing remaining events');
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
- debug: !!this.config.debug,
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
- debug: this.config.debug,
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.log('Error initializing integrations:', error);
1808
+ this.logger.info('Error initializing integrations:', error);
1579
1809
  });
1580
1810
  }
1581
1811
  init() {
1582
1812
  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.');
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.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 and no integrations will be initialized. ${this.config.debug ? 'Debug mode is active and will still log information.' : ''}`);
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.log('Debug mode deactivated.');
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.log('Debug mode activated.');
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.log('Analytics is disabled. Skipping integration initialization.');
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.log('Registering Facebook Pixel integration with ID:', integrationConfig.config.pixelId);
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.log('Registering TikTok Pixel integration with ID:', integrationConfig.config.pixelId);
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.log(`Failed to register ${integrationConfig.type} integration:`, error);
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.log('Page View', window.location.href, data);
1917
+ this.logger.info('Page View', window.location.href, data);
1683
1918
  } else {
1684
- this.logger.log('track', data);
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.log('User logged out');
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.log('identify', data);
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.log('Auto Page Tracking already enabled');
2080
+ this.logger.info('Auto Page Tracking already enabled');
1846
2081
  return;
1847
2082
  }
1848
2083
  window.saascoAutoPageTrackingActive = true;
1849
- this.logger.log('Auto Page Tracking enabled');
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;