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.esm.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { __awaiter } from 'tslib';
2
2
  import { isValid, parse } from 'psl';
3
- import { sha256 } from 'js-sha256';
4
3
  import { v4 } from '@lukeed/uuid';
4
+ import { sha256 } from 'js-sha256';
5
5
  import { z } from 'zod';
6
6
 
7
- var version = "0.1.43";
7
+ var version = "0.1.45";
8
8
 
9
9
  const timezones = {
10
10
  'Asia/Barnaul': 'RU',
@@ -551,26 +551,117 @@ function getBrowserContext() {
551
551
  };
552
552
  }
553
553
 
554
- const isBrowser$2 = typeof window !== 'undefined';
555
- const isServer$2 = !isBrowser$2;
554
+ var LogLevel;
555
+ (function (LogLevel) {
556
+ LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
557
+ LogLevel[LogLevel["WARN"] = 1] = "WARN";
558
+ LogLevel[LogLevel["INFO"] = 2] = "INFO";
559
+ LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
560
+ })(LogLevel || (LogLevel = {}));
561
+ class AnalyticsLogger {
562
+ constructor(config) {
563
+ this.config = config;
564
+ }
565
+ debug(...args) {
566
+ if (this.config.level < LogLevel.DEBUG) return;
567
+ const icon = '◍';
568
+ const message = `${icon} ${this.config.label}`;
569
+ console.debug(`%c ${message}`, `background: #eee; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
570
+ }
571
+ /**
572
+ * Log debug information
573
+ */
574
+ info(...args) {
575
+ if (this.config.level < LogLevel.INFO) return;
576
+ const icon = '◍';
577
+ const message = `${icon} ${this.config.label}`;
578
+ console.info(`%c ${message}`, `background: #eee; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
579
+ }
580
+ /**
581
+ * Log warning information
582
+ */
583
+ warn(...args) {
584
+ if (this.config.level < LogLevel.WARN) return;
585
+ const icon = '◍';
586
+ const message = `${icon} ${this.config.label}`;
587
+ console.warn(`%c ${message}`, `background: #ffa500; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
588
+ }
589
+ /**
590
+ * Log error information
591
+ */
592
+ error(...args) {
593
+ const icon = '◍';
594
+ const message = `${icon} ${this.config.label} Error`;
595
+ console.error(`%c ${message}`, 'background: red; color: white; padding-right: 6px; border-radius: 2px;', ...args);
596
+ return {
597
+ success: false,
598
+ message: args.join(' ')
599
+ };
600
+ }
601
+ }
602
+
603
+ const isBrowser$4 = typeof window !== 'undefined';
604
+ const isServer$4 = !isBrowser$4;
605
+ /**
606
+ * Get the logger level for a specific integration
607
+ * Supports URL parameters like saasco-debug-{integration-type}=true
608
+ * If debug is true or URL param is true, sets level to DEBUG (3), otherwise WARN
609
+ */
610
+ function getIntegrationLoggerLevel(integrationName, debug) {
611
+ // Check URL parameters for debug overrides (browser only)
612
+ let urlDebug = false;
613
+ if (!isServer$4) {
614
+ try {
615
+ const urlParams = new URLSearchParams(window.location.search);
616
+ const paramName = `saasco-debug-${integrationName}`;
617
+ const paramValue = urlParams.get(paramName);
618
+ if (paramValue === 'true') {
619
+ urlDebug = true;
620
+ }
621
+ } catch (error) {
622
+ // Ignore URL parsing errors
623
+ }
624
+ }
625
+ // URL parameters override config, config overrides default (WARN)
626
+ return urlDebug || debug ? LogLevel.DEBUG : LogLevel.WARN;
627
+ }
628
+
629
+ /**
630
+ * Generate a collision-resistant UUID
631
+ * Uses lukeed's UUID v4 implementation for consistent, fast UUID generation
632
+ */
633
+ function uuid() {
634
+ return v4();
635
+ }
636
+
637
+ const isBrowser$3 = typeof window !== 'undefined';
638
+ const isServer$3 = !isBrowser$3;
556
639
  const standardFacebookEvents = ['AddPaymentInfo', 'AddToCart', 'AddToWishlist', 'CompleteRegistration', 'Contact', 'CustomizeProduct', 'Donate', 'FindLocation', 'InitiateCheckout', 'Lead', 'Purchase', 'Schedule', 'Search', 'StartTrial', 'SubmitApplication', 'Subscribe', 'ViewContent', 'PageView'];
557
640
  /**
558
641
  * Create a Facebook Pixel integration instance
559
642
  */
560
- function createFacebookPixelIntegration(config) {
643
+ function createFacebookPixelIntegration(config, debug) {
561
644
  let isPixelReady = false;
645
+ // Create integration logger
646
+ const integrationLoggerLevel = getIntegrationLoggerLevel('facebook-pixel', debug);
647
+ const logger = new AnalyticsLogger({
648
+ level: integrationLoggerLevel,
649
+ label: 'Saasco facebook-pixel'
650
+ });
562
651
  return {
563
652
  name: 'facebook-pixel',
564
653
  environments: ['client'],
565
654
  init: () => __awaiter(this, void 0, void 0, function* () {
566
- if (isServer$2) {
567
- // On server, Facebook Pixel is a no-op but doesn't throw
568
- // This allows the integration to be registered but remain inactive
655
+ if (isServer$3) {
656
+ logger.debug('Facebook Pixel init skipped on server');
569
657
  return;
570
658
  }
659
+ logger.info('Initializing Facebook Pixel', {
660
+ pixelId: config.pixelId
661
+ });
571
662
  // Check if Facebook Pixel is already loaded
572
663
  if (window.fbq || window._fbq) {
573
- console.warn('Facebook Pixel is already initialized');
664
+ logger.warn('Facebook Pixel is already initialized');
574
665
  isPixelReady = true;
575
666
  return;
576
667
  }
@@ -593,9 +684,11 @@ function createFacebookPixelIntegration(config) {
593
684
  // Add load event listener
594
685
  t.onload = () => {
595
686
  isPixelReady = true;
687
+ logger.info('Facebook Pixel script loaded successfully');
596
688
  resolve();
597
689
  };
598
690
  t.onerror = () => {
691
+ logger.error('Failed to load Facebook Pixel script');
599
692
  reject(new Error('Failed to load Facebook Pixel script'));
600
693
  };
601
694
  s = b.getElementsByTagName(e)[0];
@@ -606,33 +699,56 @@ function createFacebookPixelIntegration(config) {
606
699
  // Allow duplicate PageView events
607
700
  window.fbq.allowDuplicatePageViews = true;
608
701
  if (config.automaticConfiguration === false) {
702
+ logger.debug('Disabling automatic configuration');
609
703
  window.fbq('set', 'autoConfig', false, config.pixelId);
610
704
  }
611
705
  // Initialize pixel
706
+ logger.debug('Initializing Facebook Pixel with ID', config.pixelId);
612
707
  window.fbq('init', config.pixelId);
613
708
  // If script loads synchronously, mark as ready
614
709
  if (window.fbq && typeof window.fbq === 'function') {
615
710
  isPixelReady = true;
711
+ logger.info('Facebook Pixel initialized synchronously');
616
712
  resolve();
617
713
  }
618
714
  } catch (error) {
715
+ logger.error('Error during Facebook Pixel initialization', error);
619
716
  reject(error);
620
717
  }
621
718
  });
622
719
  }),
623
720
  track: (eventName, properties, context) => {
624
- if (!isPixelReady || !window.fbq) return;
721
+ if (!isPixelReady || !window.fbq) {
722
+ logger.warn('Facebook Pixel not ready, skipping track event', {
723
+ eventName
724
+ });
725
+ return;
726
+ }
625
727
  const fbEventName = getFacebookEventName(eventName, config.eventMapping);
626
728
  const eventsWithParams = ['Purchase', 'StartTrial', 'Subscribe'];
627
729
  const isStandardEvent = standardFacebookEvents.includes(fbEventName);
628
730
  const trackType = isStandardEvent ? 'track' : 'trackCustom';
731
+ logger.debug('Tracking Facebook Pixel event', {
732
+ originalEvent: eventName,
733
+ fbEventName,
734
+ trackType,
735
+ isStandardEvent,
736
+ properties
737
+ });
629
738
  if (eventsWithParams.includes(fbEventName) && properties) {
630
739
  const fbParams = {};
631
740
  if (properties['value'] !== undefined) fbParams['value'] = properties['value'];
632
741
  if (properties['currency'] !== undefined) fbParams['currency'] = properties['currency'];
633
742
  if (properties['predicted_ltv'] !== undefined) fbParams['predicted_ltv'] = properties['predicted_ltv'];
743
+ logger.info('Tracking Facebook Pixel event with parameters', {
744
+ event: fbEventName,
745
+ params: fbParams
746
+ });
634
747
  window.fbq(trackType, fbEventName, fbParams);
635
748
  } else {
749
+ logger.info('Tracking Facebook Pixel event', {
750
+ event: fbEventName
751
+ });
636
752
  window.fbq(trackType, fbEventName);
637
753
  }
638
754
  },
@@ -819,6 +935,150 @@ function findFirstProperty$1(properties, keys) {
819
935
  return null;
820
936
  }
821
937
 
938
+ const isBrowser$2 = typeof window !== 'undefined';
939
+ const isServer$2 = !isBrowser$2;
940
+ /**
941
+ * Standard Pinterest events
942
+ * Reference: https://www.pinterest.com/_/_/help/business/article/event-code
943
+ */
944
+ const standardPinterestEvents = ['checkout', 'addtocart', 'pagevisit', 'signup', 'watchvideo', 'lead', 'search', 'viewcategory', 'custom', 'addpaymentinfo', 'addtowishlist', 'initiatecheckout', 'subscribe', 'viewcontent'];
945
+ /**
946
+ * Create a Pinterest Tag integration instance
947
+ * Documentation: https://help.pinterest.com/en/business/article/install-the-pinterest-tag
948
+ */
949
+ function createPinterestTagIntegration(config, debug) {
950
+ let isTagReady = false;
951
+ // Create integration logger
952
+ const integrationLoggerLevel = getIntegrationLoggerLevel('pinterest-tag', debug);
953
+ const logger = new AnalyticsLogger({
954
+ level: integrationLoggerLevel,
955
+ label: 'Saasco Pinterest Tag'
956
+ });
957
+ return {
958
+ name: 'pinterest-tag',
959
+ environments: ['client'],
960
+ init: context => __awaiter(this, void 0, void 0, function* () {
961
+ if (isServer$2) {
962
+ logger.debug('Pinterest Tag init skipped on server');
963
+ return;
964
+ }
965
+ logger.info('Initializing Pinterest Tag', {
966
+ tagId: config.tagId
967
+ });
968
+ // Check if Pinterest Tag is already loaded
969
+ if (window.pintrk || window._pintrk) {
970
+ logger.warn('Pinterest Tag is already initialized');
971
+ isTagReady = true;
972
+ return;
973
+ }
974
+ // Initialize Pinterest Tag
975
+ return new Promise((resolve, reject) => {
976
+ try {
977
+ // Pinterest Tag initialization script
978
+ // Reference: https://help.pinterest.com/en/business/article/install-the-base-code
979
+ (function (e) {
980
+ if (!window.pintrk) {
981
+ window.pintrk = function () {
982
+ window.pintrk.queue.push(Array.prototype.slice.call(arguments));
983
+ };
984
+ const n = window.pintrk;
985
+ n.queue = [];
986
+ n.version = '3.0';
987
+ const t = document.createElement('script');
988
+ t.async = true;
989
+ t.src = e;
990
+ // Add load event listener
991
+ t.onload = () => {
992
+ // base script is loaded; now (re)load tag + fire page via runtime
993
+ window.pintrk('load', config.tagId);
994
+ // initialize the tag
995
+ window.pintrk('page');
996
+ // delay isTagReady to ensure the tag is loaded before sending events
997
+ setTimeout(() => {
998
+ logger.debug('Pinterest Tag initialized');
999
+ isTagReady = true;
1000
+ resolve();
1001
+ }, 500);
1002
+ };
1003
+ t.onerror = () => {
1004
+ logger.error('Failed to load Pinterest Tag script');
1005
+ reject(new Error('Failed to load Pinterest Tag script'));
1006
+ };
1007
+ const r = document.getElementsByTagName('script')[0];
1008
+ r.parentNode.insertBefore(t, r);
1009
+ }
1010
+ })('https://s.pinimg.com/ct/core.js');
1011
+ } catch (error) {
1012
+ logger.error('Failed to initialize Pinterest Tag', error);
1013
+ reject(error);
1014
+ }
1015
+ });
1016
+ }),
1017
+ track: (eventName, properties) => {
1018
+ if (!isTagReady || !window.pintrk) return;
1019
+ const pinterestEventName = getPinterestEventName(eventName, config.eventMapping);
1020
+ const eventData = buildEventData(properties);
1021
+ logger.debug('Tracking event', {
1022
+ eventName,
1023
+ pinterestEventName,
1024
+ eventData
1025
+ });
1026
+ // Track the event
1027
+ window.pintrk('track', pinterestEventName, eventData);
1028
+ },
1029
+ identify: (userId, properties, context) => {
1030
+ // Identify not supported with pinterest tag
1031
+ logger.debug('Identify not supported with pinterest tag', {
1032
+ userId,
1033
+ properties,
1034
+ context
1035
+ });
1036
+ window.pintrk('set', {
1037
+ external_id: userId
1038
+ });
1039
+ window.pintrk('set', {
1040
+ em: properties === null || properties === void 0 ? void 0 : properties['email']
1041
+ });
1042
+ return;
1043
+ }
1044
+ };
1045
+ }
1046
+ function getPinterestEventName(eventName, eventMapping) {
1047
+ // Convert default saasco Page View event by default
1048
+ if (eventName === 'Page View') return 'pagevisit';
1049
+ if (!eventMapping) return eventName.toLowerCase();
1050
+ return eventMapping[eventName] || eventName.toLowerCase();
1051
+ }
1052
+ /**
1053
+ * Build event data for Pinterest tracking
1054
+ * Reference: https://www.pinterest.com/_/_/help/business/article/event-code
1055
+ */
1056
+ function buildEventData(properties) {
1057
+ if (!properties) return {};
1058
+ const eventData = {};
1059
+ // Map common properties to Pinterest event data
1060
+ if (properties['event_id']) eventData['event_id'] = properties['event_id'];
1061
+ if (properties['value']) eventData['value'] = properties['value'];
1062
+ if (properties['currency']) eventData['currency'] = properties['currency'];
1063
+ if (properties['order_quantity']) eventData['order_quantity'] = properties['order_quantity'];
1064
+ if (properties['order_id']) eventData['order_id'] = properties['order_id'];
1065
+ if (properties['promo_code']) eventData['promo_code'] = properties['promo_code'];
1066
+ if (properties['property']) eventData['property'] = properties['property'];
1067
+ if (properties['search_query']) eventData['search_query'] = properties['search_query'];
1068
+ if (properties['video_title']) eventData['video_title'] = properties['video_title'];
1069
+ if (properties['lead_type']) eventData['lead_type'] = properties['lead_type'];
1070
+ if (properties['product_category']) eventData['product_category'] = properties['product_category'];
1071
+ // Handle line items for e-commerce tracking
1072
+ if (properties['line_items'] && Array.isArray(properties['line_items'])) {
1073
+ eventData['line_items'] = properties['line_items'];
1074
+ }
1075
+ // Generate event_id if not provided
1076
+ if (!eventData['event_id']) {
1077
+ eventData['event_id'] = uuid();
1078
+ }
1079
+ return eventData;
1080
+ }
1081
+
822
1082
  /**
823
1083
  * TikTok Pixel Integration
824
1084
  *
@@ -845,20 +1105,28 @@ const standardTikTokEvents = ['AddPaymentInfo', 'AddToCart', 'AddToWishlist', 'A
845
1105
  /**
846
1106
  * Create a TikTok Pixel integration instance
847
1107
  */
848
- function createTikTokPixelIntegration(config) {
1108
+ function createTikTokPixelIntegration(config, debug) {
849
1109
  let isPixelReady = false;
1110
+ // Create integration logger
1111
+ const integrationLoggerLevel = getIntegrationLoggerLevel('tiktok-pixel', debug);
1112
+ const logger = new AnalyticsLogger({
1113
+ level: integrationLoggerLevel,
1114
+ label: 'Saasco tiktok-pixel'
1115
+ });
850
1116
  return {
851
1117
  name: 'tiktok-pixel',
852
1118
  environments: ['client'],
853
1119
  init: () => __awaiter(this, void 0, void 0, function* () {
854
1120
  if (isServer$1) {
855
- // On server, TikTok Pixel is a no-op but doesn't throw
856
- // This allows the integration to be registered but remain inactive
1121
+ logger.debug('TikTok Pixel init skipped on server');
857
1122
  return;
858
1123
  }
1124
+ logger.info('Initializing TikTok Pixel', {
1125
+ pixelId: config.pixelId
1126
+ });
859
1127
  // Check if TikTok Pixel is already loaded
860
1128
  if (window.ttq) {
861
- console.warn('TikTok Pixel is already initialized');
1129
+ logger.warn('TikTok Pixel is already initialized');
862
1130
  isPixelReady = true;
863
1131
  return;
864
1132
  }
@@ -909,7 +1177,6 @@ function createTikTokPixelIntegration(config) {
909
1177
  ttq.load(config.pixelId, config.testMode ? {
910
1178
  test_mode: true
911
1179
  } : undefined);
912
- ttq.page();
913
1180
  // If script loads synchronously, mark as ready
914
1181
  if (window.ttq && typeof window.ttq === 'function') {
915
1182
  isPixelReady = true;
@@ -1084,69 +1351,9 @@ function mapTikTokEventParameters(eventName, properties) {
1084
1351
  return Object.keys(params).length > 0 ? params : null;
1085
1352
  }
1086
1353
 
1087
- /* eslint-disable @typescript-eslint/no-explicit-any */
1088
- class AnalyticsLogger {
1089
- constructor(config) {
1090
- this.config = config;
1091
- }
1092
- /**
1093
- * Log debug information
1094
- */
1095
- log(...args) {
1096
- if (!this.config.debug) return;
1097
- const icon = '◍';
1098
- const message = `${icon} ${this.config.label}`;
1099
- console.info(`%c ${message}`, `background: #eee; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
1100
- }
1101
- /**
1102
- * Log warning information
1103
- */
1104
- warn(...args) {
1105
- if (!this.config.debug) return;
1106
- const icon = '◍';
1107
- const message = `${icon} ${this.config.label}`;
1108
- console.warn(`%c ${message}`, `background: #ffa500; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
1109
- }
1110
- /**
1111
- * Log error information
1112
- */
1113
- error(...args) {
1114
- const icon = '◍';
1115
- const message = `${icon} ${this.config.label} Error`;
1116
- console.error(`%c ${message}`, 'background: red; color: white; padding-right: 6px; border-radius: 2px;', ...args);
1117
- return {
1118
- success: false,
1119
- message: args.join(' ')
1120
- };
1121
- }
1122
- }
1123
-
1124
- /**
1125
- * Generate a collision-resistant UUID
1126
- * Uses lukeed's UUID v4 implementation for consistent, fast UUID generation
1127
- */
1128
- function uuid() {
1129
- return v4();
1130
- }
1131
-
1132
1354
  /*
1133
1355
  Minimal analytics integration manager (v0)
1134
1356
  */
1135
- class Logger {
1136
- constructor(label = 'Analytics', debug = false) {
1137
- this.label = label;
1138
- this.debug = debug;
1139
- }
1140
- log(...args) {
1141
- if (this.debug) console.log(`[${this.label}]`, ...args);
1142
- }
1143
- warn(...args) {
1144
- console.warn(`[${this.label}]`, ...args);
1145
- }
1146
- error(...args) {
1147
- console.error(`[${this.label}]`, ...args);
1148
- }
1149
- }
1150
1357
  class IntegrationManager {
1151
1358
  constructor(config = {}) {
1152
1359
  var _a, _b, _c, _d;
@@ -1154,14 +1361,14 @@ class IntegrationManager {
1154
1361
  this.integrations = new Map();
1155
1362
  this.globalQueue = [];
1156
1363
  this.config = {
1157
- debug: (_a = config.debug) !== null && _a !== void 0 ? _a : false,
1364
+ loggerLevel: (_a = config.loggerLevel) !== null && _a !== void 0 ? _a : LogLevel.ERROR,
1158
1365
  maxQueueSize: (_b = config.maxQueueSize) !== null && _b !== void 0 ? _b : 200,
1159
1366
  maxIntegrationWaitTime: (_c = config.maxIntegrationWaitTime) !== null && _c !== void 0 ? _c : 10000,
1160
1367
  flushInterval: (_d = config.flushInterval) !== null && _d !== void 0 ? _d : 5000
1161
1368
  };
1162
1369
  this.logger = new AnalyticsLogger({
1163
1370
  label: 'Saasco Integrations Debug',
1164
- debug: this.config.debug
1371
+ level: this.config.loggerLevel
1165
1372
  });
1166
1373
  this.initTime = Date.now();
1167
1374
  this.currentEnvironment = typeof window !== 'undefined' ? 'client' : 'server';
@@ -1174,7 +1381,7 @@ class IntegrationManager {
1174
1381
  setContext(next) {
1175
1382
  this.context = Object.assign(Object.assign({}, this.context), next);
1176
1383
  // Only log if we are setting something
1177
- if (Object.keys(this.context).length > 0) this.logger.log('context', this.context);
1384
+ if (Object.keys(this.context).length > 0) this.logger.info('setContext', this.context);
1178
1385
  }
1179
1386
  /**
1180
1387
  * Register and init an integration. When init resolves, we mark it ready and
@@ -1182,17 +1389,18 @@ class IntegrationManager {
1182
1389
  */
1183
1390
  registerIntegration(integration) {
1184
1391
  return __awaiter(this, void 0, void 0, function* () {
1392
+ this.logger.debug('Register integrations', this.integrations);
1185
1393
  if (this.integrations.has(integration.name)) {
1186
- this.logger.warn(`integration already registered: ${integration.name}`);
1394
+ this.logger.warn(`Integration already registered: ${integration.name}`);
1187
1395
  return;
1188
1396
  }
1189
1397
  // Check environment compatibility
1190
1398
  const isCompatible = integration.environments.includes(this.currentEnvironment);
1191
1399
  if (!isCompatible) {
1192
- this.logger.warn(`skipping ${integration.name}: requires ${integration.environments.join(' or ')} but running in ${this.currentEnvironment}`);
1400
+ this.logger.warn(`Skipping ${integration.name}: requires ${integration.environments.join(' or ')} but running in ${this.currentEnvironment}`);
1193
1401
  return;
1194
1402
  }
1195
- this.logger.log(`registering ${integration.name})`);
1403
+ this.logger.debug(`Registering ${integration.name}`);
1196
1404
  const state = {
1197
1405
  integration,
1198
1406
  status: 'idle'
@@ -1201,17 +1409,21 @@ class IntegrationManager {
1201
1409
  if (integration.init) {
1202
1410
  try {
1203
1411
  state.status = 'loading';
1204
- yield integration.init();
1412
+ const startTime = Date.now();
1413
+ this.logger.debug(`Initializing ${integration.name}`);
1414
+ yield integration.init(this.context);
1205
1415
  state.status = 'ready';
1206
- this.logger.log(`${integration.name} integration ready`);
1416
+ const endTime = Date.now();
1417
+ const duration = endTime - startTime;
1418
+ this.logger.debug(`Integration ${integration.name} ready in ${duration}ms`);
1207
1419
  this.flush();
1208
1420
  } catch (e) {
1209
1421
  state.status = 'error';
1210
- this.logger.error(`failed to init ${integration.name}`, e);
1422
+ this.logger.error(`Failed to init ${integration.name}`, e);
1211
1423
  }
1212
1424
  } else {
1213
1425
  state.status = 'ready';
1214
- this.logger.log(`ready (no init) ${integration.name}`);
1426
+ this.logger.info(`Integration ${integration.name} ready (no init)`);
1215
1427
  this.flush();
1216
1428
  }
1217
1429
  });
@@ -1252,10 +1464,10 @@ class IntegrationManager {
1252
1464
  if (this.globalQueue.length >= this.config.maxQueueSize) {
1253
1465
  // drop oldest
1254
1466
  this.globalQueue.shift();
1255
- this.logger.warn('queue full → dropped oldest');
1467
+ this.logger.warn('Queue full → dropped oldest');
1256
1468
  }
1257
1469
  this.globalQueue.push(envelope);
1258
- this.logger.log(`queued (${this.globalQueue.length})`, envelope);
1470
+ this.logger.debug(`Queued (${this.globalQueue.length})`, envelope);
1259
1471
  return;
1260
1472
  }
1261
1473
  this.deliver(envelope);
@@ -1269,8 +1481,17 @@ class IntegrationManager {
1269
1481
  if (status !== 'ready') continue;
1270
1482
  try {
1271
1483
  if (evt.type === 'track' && integration.track && evt.name) {
1484
+ this.logger.debug(`Track (${integration.name}) ${evt.name}`, {
1485
+ properties: evt.properties,
1486
+ context: evt.context
1487
+ });
1272
1488
  yield integration.track(evt.name, evt.properties, evt.context);
1273
1489
  } else if (evt.type === 'identify' && integration.identify) {
1490
+ this.logger.debug(`Identify (${integration.name})`, {
1491
+ userId: evt.context.distinctId,
1492
+ properties: evt.properties,
1493
+ context: evt.context
1494
+ });
1274
1495
  yield integration.identify(evt.context.distinctId, evt.properties, evt.context);
1275
1496
  }
1276
1497
  } catch (e) {
@@ -1287,7 +1508,7 @@ class IntegrationManager {
1287
1508
  if (this.globalQueue.length === 0) return;
1288
1509
  // If we're not ready don't flush
1289
1510
  if (!this.isReady()) return;
1290
- this.logger.log(`flushing ${this.globalQueue.length} queued events`);
1511
+ this.logger.debug(`flushing ${this.globalQueue.length} queued events`);
1291
1512
  const toSend = this.globalQueue;
1292
1513
  this.globalQueue = [];
1293
1514
  // deliver all events
@@ -1299,8 +1520,14 @@ class IntegrationManager {
1299
1520
  return n;
1300
1521
  }
1301
1522
  isReady() {
1523
+ const timeElapsed = Date.now() - this.initTime;
1302
1524
  // Return if integrations are not ready, or if we haven't waited the max integration wait time
1303
- if (this.readyCount() < this.integrations.size && Date.now() - this.initTime < this.config.maxIntegrationWaitTime) return false;
1525
+ if (this.readyCount() < this.integrations.size && timeElapsed < this.config.maxIntegrationWaitTime) {
1526
+ return false;
1527
+ }
1528
+ if (this.readyCount() < this.integrations.size) {
1529
+ this.logger.info(`proceeding with ${this.readyCount()}/${this.integrations.size} integrations ready after ${timeElapsed}ms wait`);
1530
+ }
1304
1531
  return true;
1305
1532
  }
1306
1533
  /**
@@ -1322,7 +1549,7 @@ class IntegrationManager {
1322
1549
  return;
1323
1550
  }
1324
1551
  this.unloadHandler = () => {
1325
- this.logger.log('page unloading, flushing remaining events');
1552
+ this.logger.info('page unloading, flushing remaining events');
1326
1553
  this.flush();
1327
1554
  };
1328
1555
  // Use both beforeunload and pagehide for better coverage
@@ -1341,7 +1568,8 @@ class IntegrationManager {
1341
1568
  queueLength: this.globalQueue.length,
1342
1569
  readyCount: this.readyCount(),
1343
1570
  periodicFlushEnabled: this.config.flushInterval > 0,
1344
- flushInterval: this.config.flushInterval
1571
+ flushInterval: this.config.flushInterval,
1572
+ context: this.context
1345
1573
  };
1346
1574
  }
1347
1575
  }
@@ -1545,11 +1773,13 @@ class Saasco {
1545
1773
  this.config = config;
1546
1774
  this.lastPageViewPath = '';
1547
1775
  this.isInitialized = false;
1776
+ const loggerLevel = getLoggerLevel(this.config);
1548
1777
  // Initialize logger
1549
1778
  this.logger = new AnalyticsLogger({
1550
- debug: !!this.config.debug,
1779
+ level: loggerLevel,
1551
1780
  label: 'Saasco Debug'
1552
1781
  });
1782
+ this.logger.info('Saasco started');
1553
1783
  if (!config.projectId) {
1554
1784
  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.");
1555
1785
  return;
@@ -1564,19 +1794,19 @@ class Saasco {
1564
1794
  }, this.config.autoPageTracking);
1565
1795
  // Initialize integration manager
1566
1796
  this.integrationManager = new IntegrationManager({
1567
- debug: this.config.debug,
1797
+ loggerLevel,
1568
1798
  maxQueueSize: 1000
1569
1799
  });
1570
1800
  // Set initial context
1571
1801
  this.integrationManager.setContext({});
1572
1802
  // Initialize integrations immediately (works on both client and server)
1573
1803
  this.initIntegrations().catch(error => {
1574
- this.logger.log('Error initializing integrations:', error);
1804
+ this.logger.info('Error initializing integrations:', error);
1575
1805
  });
1576
1806
  }
1577
1807
  init() {
1578
1808
  if (this.isInitialized) {
1579
- this.logger.log('Saasco is already initialized. Please check your code to ensure that init() is not being called multiple times.');
1809
+ this.logger.info('Saasco is already initialized. Please check your code to ensure that init() is not being called multiple times.');
1580
1810
  return;
1581
1811
  }
1582
1812
  // Export Saasco to the window object for easy access and debugging
@@ -1585,43 +1815,48 @@ class Saasco {
1585
1815
  }
1586
1816
  // Migrate existing localStorage data to cookies for cross-subdomain support
1587
1817
  migrateFromLocalStorage();
1588
- this.logger.log('Saasco initialized', this.config);
1589
- if (this.config.debug) this.logger.log('Debug mode active. This will log all events to the console.');
1590
- 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.' : ''}`);
1818
+ this.logger.info('Saasco initialized', this.config);
1819
+ if (this.config.debug) this.logger.info('Debug mode active. This will log all events to the console.');
1820
+ 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.' : ''}`);
1591
1821
  this.initAutoPageTracking();
1592
1822
  this.isInitialized = true;
1593
1823
  }
1594
1824
  disableDebug() {
1595
- this.logger.log('Debug mode deactivated.');
1825
+ this.logger.info('Debug mode deactivated.');
1596
1826
  this.config.debug = false;
1597
1827
  }
1598
1828
  enableDebug() {
1599
1829
  this.config.debug = true;
1600
- this.logger.log('Debug mode activated.');
1830
+ this.logger.info('Debug mode activated.');
1601
1831
  }
1602
1832
  /**
1603
1833
  * Initialize third-party integrations
1604
1834
  */
1605
1835
  initIntegrations() {
1606
1836
  return __awaiter(this, void 0, void 0, function* () {
1837
+ this.logger.info('Initialize integrations');
1607
1838
  if (!this.config.integrations || this.config.integrations.length === 0) return;
1608
1839
  if (!this.config.enabled) {
1609
- this.logger.log('Analytics is disabled. Skipping integration initialization.');
1840
+ this.logger.info('Analytics is disabled. Skipping integration initialization.');
1610
1841
  return;
1611
1842
  }
1612
1843
  for (const integrationConfig of this.config.integrations) {
1613
1844
  try {
1614
1845
  if (integrationConfig.type === 'facebook-pixel') {
1615
- this.logger.log('Registering Facebook Pixel integration with ID:', integrationConfig.config.pixelId);
1616
- const fbIntegration = createFacebookPixelIntegration(integrationConfig.config);
1846
+ this.logger.info('Registering Facebook Pixel integration with ID:', integrationConfig.config.pixelId);
1847
+ const fbIntegration = createFacebookPixelIntegration(integrationConfig.config, integrationConfig.debug);
1617
1848
  yield this.integrationManager.registerIntegration(fbIntegration);
1849
+ } else if (integrationConfig.type === 'pinterest-tag') {
1850
+ this.logger.info('Registering Pinterest Tag integration with ID:', integrationConfig.config.tagId);
1851
+ const pinterestIntegration = createPinterestTagIntegration(integrationConfig.config, integrationConfig.debug);
1852
+ yield this.integrationManager.registerIntegration(pinterestIntegration);
1618
1853
  } 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);
1854
+ this.logger.info('Registering TikTok Pixel integration with ID:', integrationConfig.config.pixelId);
1855
+ const tiktokIntegration = createTikTokPixelIntegration(integrationConfig.config, integrationConfig.debug);
1621
1856
  yield this.integrationManager.registerIntegration(tiktokIntegration);
1622
1857
  }
1623
1858
  } catch (error) {
1624
- this.logger.log(`Failed to register ${integrationConfig.type} integration:`, error);
1859
+ this.logger.info(`Failed to register ${integrationConfig.type} integration:`, error);
1625
1860
  }
1626
1861
  }
1627
1862
  });
@@ -1675,9 +1910,9 @@ class Saasco {
1675
1910
  context: JSON.stringify(context || {})
1676
1911
  };
1677
1912
  if (data.action === 'Page View') {
1678
- this.logger.log('Page View', window.location.href, data);
1913
+ this.logger.info('Page View', window.location.href, data);
1679
1914
  } else {
1680
- this.logger.log('track', data);
1915
+ this.logger.info('track', data);
1681
1916
  }
1682
1917
  if (!this.config.enabled) {
1683
1918
  return Promise.resolve({
@@ -1730,7 +1965,7 @@ class Saasco {
1730
1965
  // This should only be called when distinctId is null and there is an existing userId.
1731
1966
  // This means the user has logged out and we should reset the session and anonymous IDs
1732
1967
  const userIdChangedToNull = distinctId === null && !!getUserId();
1733
- if (userIdChangedToNull) this.logger.log('User logged out');
1968
+ if (userIdChangedToNull) this.logger.info('User logged out');
1734
1969
  const reset = userIdChangedToNull;
1735
1970
  const sessionId = setSessionId({
1736
1971
  reset
@@ -1741,11 +1976,11 @@ class Saasco {
1741
1976
  // set the distinct Id to the userId
1742
1977
  setUserId(distinctId);
1743
1978
  // Update integration manager context and send identify (IntegrationManager will filter by environment)
1744
- this.integrationManager.setContext({
1979
+ this.integrationManager.setContext(Object.assign({
1745
1980
  distinctId,
1746
1981
  anonymousId,
1747
1982
  sessionId
1748
- });
1983
+ }, properties));
1749
1984
  // No distinctId provided so we don't track the user
1750
1985
  if (!distinctId) return Promise.resolve({
1751
1986
  success: true,
@@ -1762,7 +1997,7 @@ class Saasco {
1762
1997
  payload: properties || {},
1763
1998
  context: context || {}
1764
1999
  };
1765
- this.logger.log('identify', data);
2000
+ this.logger.info('identify', data);
1766
2001
  // Only send identify to integrations if analytics is enabled
1767
2002
  if (!this.config.enabled) {
1768
2003
  return Promise.resolve({
@@ -1838,11 +2073,11 @@ class Saasco {
1838
2073
  if (isServer) return console.warn('Saasco auto page tracking is only available in the browser');
1839
2074
  // Prevent intitializing auto page tracking more than once
1840
2075
  if (window.saascoAutoPageTrackingActive) {
1841
- this.logger.log('Auto Page Tracking already enabled');
2076
+ this.logger.info('Auto Page Tracking already enabled');
1842
2077
  return;
1843
2078
  }
1844
2079
  window.saascoAutoPageTrackingActive = true;
1845
- this.logger.log('Auto Page Tracking enabled');
2080
+ this.logger.info('Auto Page Tracking enabled');
1846
2081
  // Track initial page load
1847
2082
  this.page();
1848
2083
  // Listen for hash changes if hash tracking is enabled
@@ -1866,6 +2101,27 @@ class Saasco {
1866
2101
  return returnValue;
1867
2102
  };
1868
2103
  }
2104
+ getIntegrationsStats() {
2105
+ return this.integrationManager.getStats();
2106
+ }
2107
+ }
2108
+ function getLoggerLevel(config) {
2109
+ // Check URL parameters for debug overrides (browser only)
2110
+ let urlDebug = false;
2111
+ let urlDebugVerbose = false;
2112
+ if (!isServer) {
2113
+ try {
2114
+ const urlParams = new URLSearchParams(window.location.search);
2115
+ urlDebug = urlParams.get('saasco-debug') === 'true';
2116
+ urlDebugVerbose = urlParams.get('saasco-debug-verbose') === 'true';
2117
+ } catch (error) {
2118
+ // Ignore URL parsing errors
2119
+ }
2120
+ }
2121
+ // URL parameters override config
2122
+ const effectiveDebug = urlDebug || config.debug;
2123
+ const effectiveDebugVerbose = urlDebugVerbose || config.debugVerbose;
2124
+ return !!effectiveDebugVerbose ? LogLevel.DEBUG : !!effectiveDebug ? LogLevel.INFO : LogLevel.ERROR;
1869
2125
  }
1870
2126
 
1871
2127
  const browserContextSchema = z.object({
@@ -1893,4 +2149,4 @@ const browserContextSchema = z.object({
1893
2149
  $utmAdId: z.string().nullable()
1894
2150
  });
1895
2151
 
1896
- export { IntegrationManager, Logger, Saasco, browserContextSchema, createFacebookPixelIntegration, createTikTokPixelIntegration, getBrowserContext, standardFacebookEvents, standardTikTokEvents, timezones };
2152
+ export { IntegrationManager, Saasco, browserContextSchema, createFacebookPixelIntegration, createPinterestTagIntegration, createTikTokPixelIntegration, getBrowserContext, standardFacebookEvents, standardPinterestEvents, standardTikTokEvents, timezones };