sitepong 0.2.0 → 0.2.2

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/dist/index.mjs CHANGED
@@ -1,5 +1,3 @@
1
- import 'web-vitals';
2
-
3
1
  // src/flags/anonymous-id.ts
4
2
  var STORAGE_KEY = "sitepong_anonymous_id";
5
3
  function generateUUID() {
@@ -518,6 +516,60 @@ function clearSession() {
518
516
  memorySessionTs = null;
519
517
  }
520
518
 
519
+ // src/analytics/utm.ts
520
+ var STORAGE_KEY2 = "sp_utm";
521
+ var UTM_KEYS = [
522
+ ["source", "utm_source"],
523
+ ["medium", "utm_medium"],
524
+ ["campaign", "utm_campaign"],
525
+ ["term", "utm_term"],
526
+ ["content", "utm_content"]
527
+ ];
528
+ function parseFromLocation() {
529
+ if (typeof window === "undefined" || !window.location || !window.location.search) return null;
530
+ let params;
531
+ try {
532
+ params = new URLSearchParams(window.location.search);
533
+ } catch {
534
+ return null;
535
+ }
536
+ const result = {};
537
+ let found = false;
538
+ for (const [key, queryKey] of UTM_KEYS) {
539
+ const value = params.get(queryKey);
540
+ if (value) {
541
+ result[key] = value;
542
+ found = true;
543
+ }
544
+ }
545
+ return found ? result : null;
546
+ }
547
+ function readStored() {
548
+ if (typeof sessionStorage === "undefined") return null;
549
+ try {
550
+ const raw = sessionStorage.getItem(STORAGE_KEY2);
551
+ if (!raw) return null;
552
+ const parsed = JSON.parse(raw);
553
+ return parsed && typeof parsed === "object" ? parsed : null;
554
+ } catch {
555
+ return null;
556
+ }
557
+ }
558
+ function writeStored(utm) {
559
+ if (typeof sessionStorage === "undefined") return;
560
+ try {
561
+ sessionStorage.setItem(STORAGE_KEY2, JSON.stringify(utm));
562
+ } catch {
563
+ }
564
+ }
565
+ function getSessionUtm() {
566
+ const stored = readStored();
567
+ if (stored) return stored;
568
+ const fresh = parseFromLocation();
569
+ if (fresh) writeStored(fresh);
570
+ return fresh;
571
+ }
572
+
521
573
  // src/analytics/autocapture.ts
522
574
  var DEFAULT_BLOCK_SELECTORS = [
523
575
  "[data-sp-no-capture]",
@@ -526,6 +578,7 @@ var DEFAULT_BLOCK_SELECTORS = [
526
578
  var MAX_TEXT_LENGTH = 255;
527
579
  var AutocaptureModule = class {
528
580
  constructor(config, callback) {
581
+ this.listeners = [];
529
582
  this.clickHandler = null;
530
583
  this.submitHandler = null;
531
584
  this.active = false;
@@ -540,6 +593,27 @@ var AutocaptureModule = class {
540
593
  };
541
594
  this.callback = callback;
542
595
  }
596
+ /**
597
+ * Register a secondary listener that receives the same events as the
598
+ * primary callback (e.g. Watchtower tap capture reusing the single click
599
+ * listener + selector resolution). Returns an unsubscribe function.
600
+ */
601
+ addListener(cb) {
602
+ this.listeners.push(cb);
603
+ return () => {
604
+ const idx = this.listeners.indexOf(cb);
605
+ if (idx >= 0) this.listeners.splice(idx, 1);
606
+ };
607
+ }
608
+ emit(event) {
609
+ this.callback(event);
610
+ for (const listener of this.listeners) {
611
+ try {
612
+ listener(event);
613
+ } catch {
614
+ }
615
+ }
616
+ }
543
617
  start() {
544
618
  if (this.active || typeof document === "undefined" || typeof document.addEventListener !== "function") return;
545
619
  this.active = true;
@@ -572,7 +646,7 @@ var AutocaptureModule = class {
572
646
  properties.$event_type = "click";
573
647
  properties.$x = e.clientX;
574
648
  properties.$y = e.clientY;
575
- this.callback({
649
+ this.emit({
576
650
  type: "click",
577
651
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
578
652
  properties
@@ -594,7 +668,7 @@ var AutocaptureModule = class {
594
668
  };
595
669
  const elemProps = this.getElementProperties(form);
596
670
  Object.assign(properties, elemProps);
597
- this.callback({
671
+ this.emit({
598
672
  type: "form_submit",
599
673
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
600
674
  properties
@@ -811,6 +885,14 @@ var AnalyticsManager = class {
811
885
  );
812
886
  this.autocaptureModule.start();
813
887
  }
888
+ /**
889
+ * The live AutocaptureModule (if click/form autocapture is enabled) —
890
+ * Watchtower tap capture attaches to it so there is only ONE document-level
891
+ * click listener and one selector/element resolution path.
892
+ */
893
+ getAutocaptureModule() {
894
+ return this.autocaptureModule;
895
+ }
814
896
  track(eventName, properties) {
815
897
  if (!this.config.enabled) return;
816
898
  const event = this.createEvent("track", { name: eventName, properties });
@@ -924,7 +1006,9 @@ var AnalyticsManager = class {
924
1006
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
925
1007
  url: typeof window !== "undefined" && window.location ? window.location.href : void 0,
926
1008
  referrer: typeof document !== "undefined" && typeof document.referrer === "string" ? document.referrer : void 0,
927
- userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0
1009
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
1010
+ utm: getSessionUtm() || void 0,
1011
+ appVersion: this.config.appVersion || void 0
928
1012
  };
929
1013
  }
930
1014
  enqueue(event) {
@@ -1622,7 +1706,7 @@ var DEFAULT_REMOTE_CONFIG = {
1622
1706
  };
1623
1707
 
1624
1708
  // src/remote-config/manager.ts
1625
- var STORAGE_KEY2 = "sitepong_remote_config";
1709
+ var STORAGE_KEY3 = "sitepong_remote_config";
1626
1710
  var STORAGE_TS_KEY = "sitepong_remote_config_ts";
1627
1711
  var RemoteConfigManager = class {
1628
1712
  constructor(options) {
@@ -1665,7 +1749,7 @@ var RemoteConfigManager = class {
1665
1749
  const storage = this.options.storage;
1666
1750
  if (!storage) return;
1667
1751
  try {
1668
- const cached = await storage.getItem(STORAGE_KEY2);
1752
+ const cached = await storage.getItem(STORAGE_KEY3);
1669
1753
  const cachedTs = await storage.getItem(STORAGE_TS_KEY);
1670
1754
  if (cached && cachedTs) {
1671
1755
  const age = Date.now() - parseInt(cachedTs, 10);
@@ -1726,7 +1810,7 @@ var RemoteConfigManager = class {
1726
1810
  const storage = this.options.storage;
1727
1811
  if (!storage) return;
1728
1812
  try {
1729
- await storage.setItem(STORAGE_KEY2, JSON.stringify(this.config));
1813
+ await storage.setItem(STORAGE_KEY3, JSON.stringify(this.config));
1730
1814
  await storage.setItem(STORAGE_TS_KEY, String(Date.now()));
1731
1815
  } catch {
1732
1816
  this.log("Failed to cache remote config");
@@ -1982,7 +2066,7 @@ function stripTrailingSlash(url) {
1982
2066
  var superlinkClient = new SuperLinkClient();
1983
2067
 
1984
2068
  // src/superlink/parse.ts
1985
- var UTM_KEYS = [
2069
+ var UTM_KEYS2 = [
1986
2070
  ["source", "utm_source"],
1987
2071
  ["medium", "utm_medium"],
1988
2072
  ["campaign", "utm_campaign"],
@@ -1998,7 +2082,7 @@ function parseUniversalLink(url) {
1998
2082
  }
1999
2083
  const params = parsed.searchParams;
2000
2084
  const utm = {};
2001
- for (const [key, queryKey] of UTM_KEYS) {
2085
+ for (const [key, queryKey] of UTM_KEYS2) {
2002
2086
  const value = params.get(queryKey);
2003
2087
  if (value) utm[key] = value;
2004
2088
  }
@@ -2065,13 +2149,13 @@ function getMatchedDeepLink() {
2065
2149
  }
2066
2150
 
2067
2151
  // src/superlink/web.ts
2068
- var STORAGE_KEY3 = "sp_superlink";
2152
+ var STORAGE_KEY4 = "sp_superlink";
2069
2153
  var captured = null;
2070
2154
  var didCapture = false;
2071
- function readStored() {
2155
+ function readStored2() {
2072
2156
  if (typeof sessionStorage === "undefined") return null;
2073
2157
  try {
2074
- const raw = sessionStorage.getItem(STORAGE_KEY3);
2158
+ const raw = sessionStorage.getItem(STORAGE_KEY4);
2075
2159
  if (!raw) return null;
2076
2160
  const parsed = JSON.parse(raw);
2077
2161
  return parsed && typeof parsed === "object" ? parsed : null;
@@ -2079,10 +2163,10 @@ function readStored() {
2079
2163
  return null;
2080
2164
  }
2081
2165
  }
2082
- function writeStored(link) {
2166
+ function writeStored2(link) {
2083
2167
  if (typeof sessionStorage === "undefined") return;
2084
2168
  try {
2085
- sessionStorage.setItem(STORAGE_KEY3, JSON.stringify(link));
2169
+ sessionStorage.setItem(STORAGE_KEY4, JSON.stringify(link));
2086
2170
  } catch {
2087
2171
  }
2088
2172
  }
@@ -2130,7 +2214,7 @@ function extractIdentityMetadata(url) {
2130
2214
  function captureWebDeepLink() {
2131
2215
  if (didCapture) return captured;
2132
2216
  didCapture = true;
2133
- const stored = readStored();
2217
+ const stored = readStored2();
2134
2218
  if (stored) {
2135
2219
  captured = stored;
2136
2220
  return captured;
@@ -2139,7 +2223,7 @@ function captureWebDeepLink() {
2139
2223
  const link = parseUniversalLink(window.location.href);
2140
2224
  if (link && hasPayload(link)) {
2141
2225
  captured = link;
2142
- writeStored(link);
2226
+ writeStored2(link);
2143
2227
  void superlinkClient.reportEvent({
2144
2228
  type: "opened",
2145
2229
  click_id: link.click_id,
@@ -2320,6 +2404,8 @@ var SitePongClient = class {
2320
2404
  this.databaseTracker = null;
2321
2405
  this.profiler = null;
2322
2406
  this.remoteConfigManager = null;
2407
+ this.watchtowerCapture = null;
2408
+ this.watchtowerOptions = void 0;
2323
2409
  this.envUnsubs = [];
2324
2410
  this.identifyHooks = [];
2325
2411
  this.config = {
@@ -2439,6 +2525,12 @@ var SitePongClient = class {
2439
2525
  } else if (config.replay?.enabled) {
2440
2526
  this.log("Replay requested but not available on this platform (web only).");
2441
2527
  }
2528
+ this.watchtowerOptions = config.watchtower;
2529
+ if (config.watchtower?.enabled && webManagerFactories.createWatchtowerCapture) {
2530
+ this.startWatchtowerCapture();
2531
+ } else if (config.watchtower?.enabled) {
2532
+ this.log("Watchtower capture requested but not available on this platform (web only).");
2533
+ }
2442
2534
  if (config.crons?.enabled) {
2443
2535
  this.cronManager = new CronMonitorManager({
2444
2536
  apiKey: config.apiKey,
@@ -2678,6 +2770,7 @@ var SitePongClient = class {
2678
2770
  this.log("identify hook failed:", err);
2679
2771
  }
2680
2772
  }
2773
+ this.watchtowerCapture?.setDistinctId(userId);
2681
2774
  if (!this.analyticsManager) {
2682
2775
  this.log("Analytics not enabled. Set analytics.enabled: true in init()");
2683
2776
  return;
@@ -2745,6 +2838,49 @@ var SitePongClient = class {
2745
2838
  getReplaySessionId() {
2746
2839
  return this.replayManager?.getSessionId() ?? null;
2747
2840
  }
2841
+ // --- Watchtower tap capture (web) ---
2842
+ /**
2843
+ * Start Watchtower web tap capture. Options merge over `watchtower` from
2844
+ * init(); `projectId` is required (either here or in init config). Reuses
2845
+ * the analytics AutocaptureModule's click listener when autocapture is on.
2846
+ */
2847
+ startWatchtowerCapture(options) {
2848
+ if (!webManagerFactories.createWatchtowerCapture) {
2849
+ this.log("Watchtower capture not available on this platform (web only).");
2850
+ return false;
2851
+ }
2852
+ if (!this.initialized || !this.config.apiKey) {
2853
+ this.log("Watchtower capture requires init() to be called first.");
2854
+ return false;
2855
+ }
2856
+ const opts = { ...this.watchtowerOptions, ...options };
2857
+ if (!opts.projectId) {
2858
+ this.log("Watchtower capture requires watchtower.projectId.");
2859
+ return false;
2860
+ }
2861
+ if (this.watchtowerCapture) {
2862
+ this.watchtowerCapture.stop();
2863
+ this.watchtowerCapture = null;
2864
+ }
2865
+ this.watchtowerCapture = webManagerFactories.createWatchtowerCapture({
2866
+ apiKey: this.config.apiKey,
2867
+ projectId: opts.projectId,
2868
+ endpoint: opts.endpoint || this.config.endpoint || DEFAULT_ENDPOINT3,
2869
+ appVersion: this.config.release || void 0,
2870
+ maxBatchSize: opts.maxBatchSize,
2871
+ flushInterval: opts.flushInterval,
2872
+ blockSelector: opts.blockSelector,
2873
+ maskSelector: opts.maskSelector,
2874
+ debug: this.config.debug
2875
+ });
2876
+ this.watchtowerCapture.start(this.analyticsManager?.getAutocaptureModule() ?? null);
2877
+ return true;
2878
+ }
2879
+ stopWatchtowerCapture() {
2880
+ if (!this.watchtowerCapture) return;
2881
+ this.watchtowerCapture.stop();
2882
+ this.watchtowerCapture = null;
2883
+ }
2748
2884
  // --- Performance (Tier 7) ---
2749
2885
  startTransaction(name, data) {
2750
2886
  if (!this.performanceManager) {
@@ -2896,6 +3032,7 @@ var SitePongClient = class {
2896
3032
  if (this.replayManager) {
2897
3033
  this.replayManager.setUser(null);
2898
3034
  }
3035
+ this.watchtowerCapture?.setDistinctId(null);
2899
3036
  }
2900
3037
  addBreadcrumb(breadcrumb) {
2901
3038
  this.log("Breadcrumb added:", breadcrumb);
@@ -3120,6 +3257,8 @@ var getDeviceSignals = sitepong.getDeviceSignals.bind(sitepong);
3120
3257
  var getFraudCheck = sitepong.getFraudCheck.bind(sitepong);
3121
3258
  var startReplay = sitepong.startReplay.bind(sitepong);
3122
3259
  var stopReplay = sitepong.stopReplay.bind(sitepong);
3260
+ var startWatchtowerCapture = sitepong.startWatchtowerCapture.bind(sitepong);
3261
+ var stopWatchtowerCapture = sitepong.stopWatchtowerCapture.bind(sitepong);
3123
3262
  var isReplayRecording = sitepong.isReplayRecording.bind(sitepong);
3124
3263
  var getReplaySessionId = sitepong.getReplaySessionId.bind(sitepong);
3125
3264
  var startTransaction = sitepong.startTransaction.bind(sitepong);
@@ -3153,6 +3292,6 @@ var onRemoteConfigChange = sitepong.onRemoteConfigChange.bind(sitepong);
3153
3292
  var registerIdentifyHook = sitepong.registerIdentifyHook.bind(sitepong);
3154
3293
  var src_default = sitepong;
3155
3294
 
3156
- export { DEFAULT_SUPERLINK_ENDPOINT, SuperLinkClient, TracePropagator, addBreadcrumb, areFlagsReady, captureError, captureMessage, captureWebDeepLink, clearAnonymousId, clearUser, sitepong as client, completeFromScan, createTraceContext, cronCheckin, cronStart, cronWrap, dbTrack, dbTrackSync, src_default as default, endSpan, endTransaction, extractIdentityMetadata, extractTrace, flush, flushMetrics, flushProfiles, generateSpanId, generateTraceId, getAllFlags, getAnonymousId, getDbNPlusOnePatterns, getDbQueryCount, getDeepLink, getDeviceSignals, getFlag, getFraudCheck, getLatestProfile, getMatchedDeepLink, getProfiles, getRemoteConfig, getReplaySessionId, getVariant, getVariantPayload2 as getVariantPayload, getVisitorId, getWebVitals, group, identify2 as identify, init, initRN, initSuperLink, isInitialized, isRemoteConfigFeatureEnabled, isReplayRecording, metricDistribution, metricGauge, metricHistogram, metricIncrement, metricStartTimer, metricTime, onDeferredDeepLink, onRemoteConfigChange, parseUniversalLink, profile, propagateTrace, refreshFlags, registerIdentifyHook, registerWebManagerFactories, resetAnalytics, resetDbQueryCount, setAnonymousId, setContext, setCurrentScreen, setEnvironment, setRNGetCurrentScreen, setTags, setUser, startProfileSpan, startReplay, startSpan, startTransaction, stopReplay, superlinkClient, identify as superlinkIdentify, track, trackPageView, waitForFlags, writeClipboardToken };
3295
+ export { DEFAULT_SUPERLINK_ENDPOINT, SuperLinkClient, TracePropagator, addBreadcrumb, areFlagsReady, captureError, captureMessage, captureWebDeepLink, clearAnonymousId, clearUser, sitepong as client, completeFromScan, createTraceContext, cronCheckin, cronStart, cronWrap, dbTrack, dbTrackSync, src_default as default, endSpan, endTransaction, extractIdentityMetadata, extractTrace, flush, flushMetrics, flushProfiles, generateSpanId, generateTraceId, getAllFlags, getAnonymousId, getDbNPlusOnePatterns, getDbQueryCount, getDeepLink, getDeviceSignals, getFlag, getFraudCheck, getLatestProfile, getMatchedDeepLink, getProfiles, getRemoteConfig, getReplaySessionId, getVariant, getVariantPayload2 as getVariantPayload, getVisitorId, getWebVitals, group, identify2 as identify, init, initRN, initSuperLink, isInitialized, isRemoteConfigFeatureEnabled, isReplayRecording, metricDistribution, metricGauge, metricHistogram, metricIncrement, metricStartTimer, metricTime, onDeferredDeepLink, onRemoteConfigChange, parseUniversalLink, profile, propagateTrace, refreshFlags, registerIdentifyHook, registerWebManagerFactories, resetAnalytics, resetDbQueryCount, setAnonymousId, setContext, setCurrentScreen, setEnvironment, setRNGetCurrentScreen, setTags, setUser, startProfileSpan, startReplay, startSpan, startTransaction, startWatchtowerCapture, stopReplay, stopWatchtowerCapture, superlinkClient, identify as superlinkIdentify, track, trackPageView, waitForFlags, writeClipboardToken };
3157
3296
  //# sourceMappingURL=index.mjs.map
3158
3297
  //# sourceMappingURL=index.mjs.map