sitepong 0.2.1 → 0.2.3

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.
@@ -1,9 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var react = require('react');
4
- require('web-vitals');
3
+ var React2 = require('react');
5
4
  var reactNative = require('react-native');
6
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ var screenRecorder = require('@sitepong/screen-recorder');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React2__default = /*#__PURE__*/_interopDefault(React2);
7
11
 
8
12
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
13
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -672,6 +676,7 @@ var DEFAULT_BLOCK_SELECTORS = [
672
676
  var MAX_TEXT_LENGTH = 255;
673
677
  var AutocaptureModule = class {
674
678
  constructor(config, callback) {
679
+ this.listeners = [];
675
680
  this.clickHandler = null;
676
681
  this.submitHandler = null;
677
682
  this.active = false;
@@ -686,6 +691,27 @@ var AutocaptureModule = class {
686
691
  };
687
692
  this.callback = callback;
688
693
  }
694
+ /**
695
+ * Register a secondary listener that receives the same events as the
696
+ * primary callback (e.g. Watchtower tap capture reusing the single click
697
+ * listener + selector resolution). Returns an unsubscribe function.
698
+ */
699
+ addListener(cb) {
700
+ this.listeners.push(cb);
701
+ return () => {
702
+ const idx = this.listeners.indexOf(cb);
703
+ if (idx >= 0) this.listeners.splice(idx, 1);
704
+ };
705
+ }
706
+ emit(event) {
707
+ this.callback(event);
708
+ for (const listener of this.listeners) {
709
+ try {
710
+ listener(event);
711
+ } catch {
712
+ }
713
+ }
714
+ }
689
715
  start() {
690
716
  if (this.active || typeof document === "undefined" || typeof document.addEventListener !== "function") return;
691
717
  this.active = true;
@@ -718,7 +744,7 @@ var AutocaptureModule = class {
718
744
  properties.$event_type = "click";
719
745
  properties.$x = e.clientX;
720
746
  properties.$y = e.clientY;
721
- this.callback({
747
+ this.emit({
722
748
  type: "click",
723
749
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
724
750
  properties
@@ -740,7 +766,7 @@ var AutocaptureModule = class {
740
766
  };
741
767
  const elemProps = this.getElementProperties(form);
742
768
  Object.assign(properties, elemProps);
743
- this.callback({
769
+ this.emit({
744
770
  type: "form_submit",
745
771
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
746
772
  properties
@@ -957,6 +983,14 @@ var AnalyticsManager = class {
957
983
  );
958
984
  this.autocaptureModule.start();
959
985
  }
986
+ /**
987
+ * The live AutocaptureModule (if click/form autocapture is enabled) —
988
+ * Watchtower tap capture attaches to it so there is only ONE document-level
989
+ * click listener and one selector/element resolution path.
990
+ */
991
+ getAutocaptureModule() {
992
+ return this.autocaptureModule;
993
+ }
960
994
  track(eventName, properties) {
961
995
  if (!this.config.enabled) return;
962
996
  const event = this.createEvent("track", { name: eventName, properties });
@@ -2226,6 +2260,8 @@ var SitePongClient = class {
2226
2260
  this.databaseTracker = null;
2227
2261
  this.profiler = null;
2228
2262
  this.remoteConfigManager = null;
2263
+ this.watchtowerCapture = null;
2264
+ this.watchtowerOptions = void 0;
2229
2265
  this.envUnsubs = [];
2230
2266
  this.identifyHooks = [];
2231
2267
  this.config = {
@@ -2345,6 +2381,12 @@ var SitePongClient = class {
2345
2381
  } else if (config.replay?.enabled) {
2346
2382
  this.log("Replay requested but not available on this platform (web only).");
2347
2383
  }
2384
+ this.watchtowerOptions = config.watchtower;
2385
+ if (config.watchtower?.enabled && webManagerFactories.createWatchtowerCapture) {
2386
+ this.startWatchtowerCapture();
2387
+ } else if (config.watchtower?.enabled) {
2388
+ this.log("Watchtower capture requested but not available on this platform (web only).");
2389
+ }
2348
2390
  if (config.crons?.enabled) {
2349
2391
  this.cronManager = new CronMonitorManager({
2350
2392
  apiKey: config.apiKey,
@@ -2584,6 +2626,7 @@ var SitePongClient = class {
2584
2626
  this.log("identify hook failed:", err);
2585
2627
  }
2586
2628
  }
2629
+ this.watchtowerCapture?.setDistinctId(userId);
2587
2630
  if (!this.analyticsManager) {
2588
2631
  this.log("Analytics not enabled. Set analytics.enabled: true in init()");
2589
2632
  return;
@@ -2651,6 +2694,49 @@ var SitePongClient = class {
2651
2694
  getReplaySessionId() {
2652
2695
  return this.replayManager?.getSessionId() ?? null;
2653
2696
  }
2697
+ // --- Watchtower tap capture (web) ---
2698
+ /**
2699
+ * Start Watchtower web tap capture. Options merge over `watchtower` from
2700
+ * init(); `projectId` is required (either here or in init config). Reuses
2701
+ * the analytics AutocaptureModule's click listener when autocapture is on.
2702
+ */
2703
+ startWatchtowerCapture(options) {
2704
+ if (!webManagerFactories.createWatchtowerCapture) {
2705
+ this.log("Watchtower capture not available on this platform (web only).");
2706
+ return false;
2707
+ }
2708
+ if (!this.initialized || !this.config.apiKey) {
2709
+ this.log("Watchtower capture requires init() to be called first.");
2710
+ return false;
2711
+ }
2712
+ const opts = { ...this.watchtowerOptions, ...options };
2713
+ if (!opts.projectId) {
2714
+ this.log("Watchtower capture requires watchtower.projectId.");
2715
+ return false;
2716
+ }
2717
+ if (this.watchtowerCapture) {
2718
+ this.watchtowerCapture.stop();
2719
+ this.watchtowerCapture = null;
2720
+ }
2721
+ this.watchtowerCapture = webManagerFactories.createWatchtowerCapture({
2722
+ apiKey: this.config.apiKey,
2723
+ projectId: opts.projectId,
2724
+ endpoint: opts.endpoint || this.config.endpoint || DEFAULT_ENDPOINT3,
2725
+ appVersion: this.config.release || void 0,
2726
+ maxBatchSize: opts.maxBatchSize,
2727
+ flushInterval: opts.flushInterval,
2728
+ blockSelector: opts.blockSelector,
2729
+ maskSelector: opts.maskSelector,
2730
+ debug: this.config.debug
2731
+ });
2732
+ this.watchtowerCapture.start(this.analyticsManager?.getAutocaptureModule() ?? null);
2733
+ return true;
2734
+ }
2735
+ stopWatchtowerCapture() {
2736
+ if (!this.watchtowerCapture) return;
2737
+ this.watchtowerCapture.stop();
2738
+ this.watchtowerCapture = null;
2739
+ }
2654
2740
  // --- Performance (Tier 7) ---
2655
2741
  startTransaction(name, data) {
2656
2742
  if (!this.performanceManager) {
@@ -2802,6 +2888,7 @@ var SitePongClient = class {
2802
2888
  if (this.replayManager) {
2803
2889
  this.replayManager.setUser(null);
2804
2890
  }
2891
+ this.watchtowerCapture?.setDistinctId(null);
2805
2892
  }
2806
2893
  addBreadcrumb(breadcrumb) {
2807
2894
  this.log("Breadcrumb added:", breadcrumb);
@@ -3026,6 +3113,8 @@ sitepong.getDeviceSignals.bind(sitepong);
3026
3113
  sitepong.getFraudCheck.bind(sitepong);
3027
3114
  sitepong.startReplay.bind(sitepong);
3028
3115
  sitepong.stopReplay.bind(sitepong);
3116
+ sitepong.startWatchtowerCapture.bind(sitepong);
3117
+ sitepong.stopWatchtowerCapture.bind(sitepong);
3029
3118
  sitepong.isReplayRecording.bind(sitepong);
3030
3119
  sitepong.getReplaySessionId.bind(sitepong);
3031
3120
  sitepong.startTransaction.bind(sitepong);
@@ -3536,7 +3625,7 @@ function endLiveActivity(activityType, activityId) {
3536
3625
  activity_id: activityId
3537
3626
  });
3538
3627
  }
3539
- var SitePongRNContext = react.createContext({
3628
+ var SitePongRNContext = React2.createContext({
3540
3629
  isInitialized: false,
3541
3630
  performanceManager: null
3542
3631
  });
@@ -3555,10 +3644,10 @@ function SitePongRNProvider({
3555
3644
  pushUserId,
3556
3645
  children
3557
3646
  }) {
3558
- const initialized = react.useRef(false);
3559
- const [sdkReady, setSdkReady] = react.useState(false);
3560
- const performanceManagerRef = react.useRef(null);
3561
- react.useEffect(() => {
3647
+ const initialized = React2.useRef(false);
3648
+ const [sdkReady, setSdkReady] = React2.useState(false);
3649
+ const performanceManagerRef = React2.useRef(null);
3650
+ React2.useEffect(() => {
3562
3651
  if (initialized.current) return;
3563
3652
  const teardowns = [];
3564
3653
  const deviceInfo = collectDeviceInfo();
@@ -3627,11 +3716,11 @@ function SitePongRNProvider({
3627
3716
  return /* @__PURE__ */ jsxRuntime.jsx(SitePongRNContext.Provider, { value, children });
3628
3717
  }
3629
3718
  function useSitePongRNContext() {
3630
- return react.useContext(SitePongRNContext);
3719
+ return React2.useContext(SitePongRNContext);
3631
3720
  }
3632
3721
  function useScreenTrack(screenName) {
3633
- const { performanceManager } = react.useContext(SitePongRNContext);
3634
- react.useEffect(() => {
3722
+ const { performanceManager } = React2.useContext(SitePongRNContext);
3723
+ React2.useEffect(() => {
3635
3724
  if (!performanceManager) return;
3636
3725
  performanceManager.startScreenRender(screenName);
3637
3726
  return () => {
@@ -3640,8 +3729,8 @@ function useScreenTrack(screenName) {
3640
3729
  }, [screenName, performanceManager]);
3641
3730
  }
3642
3731
  function useAppState() {
3643
- const [appState, setAppState] = react.useState("active");
3644
- react.useEffect(() => {
3732
+ const [appState, setAppState] = React2.useState("active");
3733
+ React2.useEffect(() => {
3645
3734
  setAppState(reactNative.AppState.currentState);
3646
3735
  const subscription = reactNative.AppState.addEventListener("change", (nextState) => {
3647
3736
  setAppState(nextState);
@@ -3651,8 +3740,8 @@ function useAppState() {
3651
3740
  return appState;
3652
3741
  }
3653
3742
  function useRemoteConfig() {
3654
- const [config, setConfig] = react.useState(() => getRemoteConfig());
3655
- react.useEffect(() => {
3743
+ const [config, setConfig] = React2.useState(() => getRemoteConfig());
3744
+ React2.useEffect(() => {
3656
3745
  setConfig(getRemoteConfig());
3657
3746
  const unsubscribe = onRemoteConfigChange((newConfig) => {
3658
3747
  setConfig(newConfig);
@@ -3662,10 +3751,10 @@ function useRemoteConfig() {
3662
3751
  return config;
3663
3752
  }
3664
3753
  function useRNPerformance(screenName) {
3665
- const { performanceManager } = react.useContext(SitePongRNContext);
3666
- const [duration, setDuration] = react.useState(null);
3667
- const startTimeRef = react.useRef(Date.now());
3668
- react.useEffect(() => {
3754
+ const { performanceManager } = React2.useContext(SitePongRNContext);
3755
+ const [duration, setDuration] = React2.useState(null);
3756
+ const startTimeRef = React2.useRef(Date.now());
3757
+ React2.useEffect(() => {
3669
3758
  startTimeRef.current = Date.now();
3670
3759
  const handle = requestAnimationFrame(() => {
3671
3760
  const d = Date.now() - startTimeRef.current;
@@ -3679,6 +3768,66 @@ function useRNPerformance(screenName) {
3679
3768
  }, [screenName, performanceManager]);
3680
3769
  return { duration };
3681
3770
  }
3771
+ var started = false;
3772
+ function startWatchtower(config) {
3773
+ if (started) return;
3774
+ screenRecorder.watchtowerStart({
3775
+ apiKey: config.apiKey,
3776
+ projectId: config.projectId,
3777
+ endpoint: config.endpoint,
3778
+ sampleRate: config.sampleRate ?? 0.1,
3779
+ platform: config.platform ?? "react-native-ios"
3780
+ });
3781
+ started = true;
3782
+ if (config.distinctId) setWatchtowerUser(config.distinctId);
3783
+ }
3784
+ function setWatchtowerUser(distinctId) {
3785
+ try {
3786
+ screenRecorder.watchtowerSetUser(distinctId);
3787
+ } catch {
3788
+ }
3789
+ }
3790
+ function getWatchtowerSessionId() {
3791
+ try {
3792
+ return screenRecorder.watchtowerGetSessionId();
3793
+ } catch {
3794
+ return null;
3795
+ }
3796
+ }
3797
+ function stopWatchtower() {
3798
+ if (!started) return;
3799
+ screenRecorder.watchtowerStop();
3800
+ started = false;
3801
+ }
3802
+ function subscribeNavigationToWatchtower(navigationRef) {
3803
+ const unsubscribe = createNavigationTracker(navigationRef, {
3804
+ screenNameFilter: (name) => {
3805
+ try {
3806
+ screenRecorder.watchtowerSetScreen(name);
3807
+ } catch {
3808
+ }
3809
+ return name;
3810
+ }
3811
+ });
3812
+ try {
3813
+ const route = navigationRef.getCurrentRoute?.();
3814
+ if (route?.name) screenRecorder.watchtowerSetScreen(route.name);
3815
+ } catch {
3816
+ }
3817
+ return unsubscribe;
3818
+ }
3819
+ function WatchtowerProvider(props) {
3820
+ const { config, navigationRef, children } = props;
3821
+ React2__default.default.useEffect(() => {
3822
+ startWatchtower(config);
3823
+ const unsubscribe = subscribeNavigationToWatchtower(navigationRef);
3824
+ return () => {
3825
+ unsubscribe();
3826
+ stopWatchtower();
3827
+ };
3828
+ }, []);
3829
+ return React2__default.default.createElement(React2__default.default.Fragment, null, children);
3830
+ }
3682
3831
 
3683
3832
  // src/react-native/sqlite-tracker.ts
3684
3833
  function now() {
@@ -3846,7 +3995,7 @@ function stopWidgetSync() {
3846
3995
  syncTimer = null;
3847
3996
  }
3848
3997
  }
3849
- var started = false;
3998
+ var started2 = false;
3850
3999
  function platform() {
3851
4000
  if (reactNative.Platform.OS === "ios") return "ios";
3852
4001
  if (reactNative.Platform.OS === "android") return "android";
@@ -3934,8 +4083,8 @@ function hasSuperLinkToken(value) {
3934
4083
  }
3935
4084
  async function initSuperLinkRN(config = {}, identityOverride) {
3936
4085
  superlinkClient.configure(config);
3937
- if (started) return getMatchedDeepLink();
3938
- started = true;
4086
+ if (started2) return getMatchedDeepLink();
4087
+ started2 = true;
3939
4088
  const fingerprint = collectFingerprint();
3940
4089
  const [referrer, clipboardToken] = await Promise.all([
3941
4090
  readPlayInstallReferrer(),
@@ -4031,6 +4180,7 @@ function setRNStorage(storage) {
4031
4180
  exports.RNAutocaptureModule = RNAutocaptureModule;
4032
4181
  exports.RNPerformanceManager = RNPerformanceManager;
4033
4182
  exports.SitePongRNProvider = SitePongRNProvider;
4183
+ exports.WatchtowerProvider = WatchtowerProvider;
4034
4184
  exports.addBreadcrumb = addBreadcrumb;
4035
4185
  exports.areFlagsReady = areFlagsReady;
4036
4186
  exports.captureError = captureError;
@@ -4056,6 +4206,7 @@ exports.getMatchedDeepLink = getMatchedDeepLink;
4056
4206
  exports.getRemoteConfig = getRemoteConfig;
4057
4207
  exports.getVariant = getVariant;
4058
4208
  exports.getVariantPayload = getVariantPayload2;
4209
+ exports.getWatchtowerSessionId = getWatchtowerSessionId;
4059
4210
  exports.group = group;
4060
4211
  exports.handleUniversalLink = handleUniversalLink;
4061
4212
  exports.identify = identify2;
@@ -4081,11 +4232,15 @@ exports.setCurrentScreen = setCurrentScreen;
4081
4232
  exports.setRNStorage = setRNStorage;
4082
4233
  exports.setTags = setTags;
4083
4234
  exports.setUser = setUser;
4235
+ exports.setWatchtowerUser = setWatchtowerUser;
4084
4236
  exports.setupNetworkInterception = setupNetworkInterception;
4085
4237
  exports.setupRNErrorHandler = setupRNErrorHandler;
4086
4238
  exports.startScreenRecording = startScreenRecording;
4239
+ exports.startWatchtower = startWatchtower;
4087
4240
  exports.stopScreenRecording = stopScreenRecording;
4241
+ exports.stopWatchtower = stopWatchtower;
4088
4242
  exports.stopWidgetSync = stopWidgetSync;
4243
+ exports.subscribeNavigationToWatchtower = subscribeNavigationToWatchtower;
4089
4244
  exports.superlinkIdentify = identify3;
4090
4245
  exports.syncWidgets = syncWidgets;
4091
4246
  exports.track = track;