sitepong 0.2.2 → 0.2.4

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.
Files changed (57) hide show
  1. package/android/build.gradle +39 -0
  2. package/android/src/main/java/com/sitepong/deviceid/DeviceIdHelper.kt +45 -0
  3. package/android/src/main/java/com/sitepong/deviceid/DeviceIdModule.kt +45 -0
  4. package/android/src/main/java/com/sitepong/screenrecorder/ChunkUploader.kt +78 -0
  5. package/android/src/main/java/com/sitepong/screenrecorder/H264Encoder.kt +163 -0
  6. package/android/src/main/java/com/sitepong/screenrecorder/ScreenCapture.kt +103 -0
  7. package/android/src/main/java/com/sitepong/screenrecorder/ScreenRecorderModule.kt +80 -0
  8. package/android/src/main/java/com/sitepong/screenrecorder/SensitiveViewManager.kt +19 -0
  9. package/app.plugin.js +426 -0
  10. package/dist/cdn/sitepong.min.js +5 -5
  11. package/dist/cdn/sitepong.min.js.map +1 -1
  12. package/dist/entries/rn.d.ts +188 -16
  13. package/dist/entries/rn.js +188 -35
  14. package/dist/entries/rn.js.map +1 -1
  15. package/dist/entries/web.js +2 -1
  16. package/dist/entries/web.js.map +1 -1
  17. package/dist/entries/web.mjs +2 -1
  18. package/dist/entries/web.mjs.map +1 -1
  19. package/dist/index.d.mts +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +2 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +2 -1
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/react/index.js +2 -1
  26. package/dist/react/index.js.map +1 -1
  27. package/dist/react/index.mjs +2 -1
  28. package/dist/react/index.mjs.map +1 -1
  29. package/expo-module.config.json +16 -0
  30. package/ios/DeviceId/DeviceIdModule.swift +55 -0
  31. package/ios/DeviceId/KeychainHelper.swift +68 -0
  32. package/ios/LiveActivity/ConfigStore.swift +43 -0
  33. package/ios/LiveActivity/DSLNode.swift +188 -0
  34. package/ios/LiveActivity/SitePongActivityAttributes.swift +112 -0
  35. package/ios/LiveActivity/SitePongLiveActivityModule.swift +250 -0
  36. package/ios/LiveActivity/widget/ColorHex.swift +30 -0
  37. package/ios/LiveActivity/widget/DSLAnimations.swift +128 -0
  38. package/ios/LiveActivity/widget/DSLRenderer.swift +538 -0
  39. package/ios/LiveActivity/widget/SitePongLiveActivityWidget.swift +393 -0
  40. package/ios/LiveActivity/widget/SitePongWidgetBundle.swift +84 -0
  41. package/ios/ScreenRecorder/ChunkUploader.swift +52 -0
  42. package/ios/ScreenRecorder/H264Encoder.swift +229 -0
  43. package/ios/ScreenRecorder/ScreenCapture.swift +149 -0
  44. package/ios/ScreenRecorder/ScreenRecorderModule.swift +179 -0
  45. package/ios/ScreenRecorder/SensitiveViewManager.swift +63 -0
  46. package/ios/Sitepong.podspec +57 -0
  47. package/ios/WatchtowerCore/Screenshotter.swift +163 -0
  48. package/ios/WatchtowerCore/SwiftUIModifiers.swift +78 -0
  49. package/ios/WatchtowerCore/Swizzling.swift +59 -0
  50. package/ios/WatchtowerCore/TapEvent.swift +61 -0
  51. package/ios/WatchtowerCore/Transport.swift +136 -0
  52. package/ios/WatchtowerCore/UIViewExtensions.swift +31 -0
  53. package/ios/WatchtowerCore/Watchtower.swift +80 -0
  54. package/ios/WatchtowerCore/WatchtowerEngine.swift +565 -0
  55. package/ios/WatchtowerCore/WatchtowerHash.swift +120 -0
  56. package/ios/WatchtowerCore/WatchtowerRegistry.swift +87 -0
  57. package/package.json +19 -8
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { ReactNode, ReactElement } from 'react';
2
2
 
3
3
  /**
4
4
  * Platform-agnostic storage interface.
@@ -500,6 +500,7 @@ declare class RNPerformanceManager {
500
500
  * 2. **Manual**: Call startScreenRecording / stopScreenRecording / flushScreenRecording
501
501
  * yourself. Keeps a 60s rolling buffer by default.
502
502
  */
503
+
503
504
  interface ScreenRecordingConfig {
504
505
  /** Enable screen recording (default: false) */
505
506
  enabled?: boolean;
@@ -541,6 +542,21 @@ declare const flushScreenRecordingOnError: typeof flushScreenRecording;
541
542
  * Check if screen recording is currently active.
542
543
  */
543
544
  declare function isScreenRecording(): boolean;
545
+ /**
546
+ * Register a native view tag as sensitive — it is masked (black rectangle) in
547
+ * recorded frames and Watchtower screenshots. No-ops if native is unavailable.
548
+ */
549
+ declare function addSensitiveView(viewTag: number): void;
550
+ /** Unregister a previously-registered sensitive view tag. */
551
+ declare function removeSensitiveView(viewTag: number): void;
552
+ /**
553
+ * React component that marks its children as sensitive: content wrapped in
554
+ * <SensitiveView> appears as a black rectangle in recordings and screenshots.
555
+ * Requires `react` and `react-native` at runtime (RN-only).
556
+ */
557
+ declare function SensitiveView(props: {
558
+ children: ReactNode;
559
+ }): ReactElement;
544
560
 
545
561
  /**
546
562
  * SitePong React Native Provider
@@ -632,9 +648,30 @@ interface RNDeviceInfo {
632
648
  }
633
649
  declare function collectDeviceInfo(): RNDeviceInfo;
634
650
  /** @deprecated No-op, returns null. Kept for API compatibility. */
651
+ /**
652
+ * Full native device signals from the bundled `SitePongDeviceId` module —
653
+ * persistent device id (Keychain on iOS / ANDROID_ID on Android), model,
654
+ * OS version, emulator flag, and vendor/android ids. Returns null when the
655
+ * native module is unavailable (web/Node, or a bare app not yet rebuilt).
656
+ */
657
+ interface NativeDeviceSignals {
658
+ deviceId: string;
659
+ platform: 'ios' | 'android';
660
+ osVersion: string;
661
+ model: string;
662
+ manufacturer: string;
663
+ isEmulator: boolean;
664
+ screenScale: number;
665
+ identifierForVendor?: string;
666
+ androidId?: string;
667
+ }
668
+ /**
669
+ * Persistent device ID that survives app reinstalls (Keychain on iOS,
670
+ * ANDROID_ID-derived on Android). Returns null when native is unavailable.
671
+ */
635
672
  declare function fetchPersistentDeviceId(): Promise<string | null>;
636
- /** @deprecated No-op, returns null. Kept for API compatibility. */
637
- declare function fetchNativeDeviceSignals(): Promise<null>;
673
+ /** Full native device signals for fingerprinting/analytics, or null. */
674
+ declare function fetchNativeDeviceSignals(): Promise<NativeDeviceSignals | null>;
638
675
 
639
676
  /**
640
677
  * React Native Error Handler
@@ -657,9 +694,10 @@ declare function setupRNErrorHandler(options?: RNErrorHandlerOptions): () => voi
657
694
  *
658
695
  * This file is intentionally MINIMAL. The hot path — capturing taps, taking
659
696
  * screenshots, perceptual hashing, redaction, and upload — all happens in the
660
- * shared native engine `WatchtowerCore` (spec §3), reached through the thin
661
- * Expo bridge in `@sitepong/screen-recorder`. capture-core's JS dHash is NOT on
662
- * the RN runtime path; the engine hashes natively.
697
+ * shared native engine (spec §3), which is compiled into THIS package's native
698
+ * module (`SitePongScreenRecorder`) and reached through `./native-modules`.
699
+ * capture-core's JS dHash is NOT on the RN runtime path; the engine hashes
700
+ * natively.
663
701
  *
664
702
  * JS's only jobs here:
665
703
  * (a) start the native engine with apiKey/projectId/endpoint + platform
@@ -822,13 +860,14 @@ declare function createTrackedDatabase<T extends SQLiteDatabase>(db: T, options?
822
860
  * Registers Expo push tokens and APNs Live Activity tokens with the
823
861
  * SitePong ingest server for remote push notification delivery.
824
862
  *
825
- * For Live Activities, prefer the turnkey `@sitepong/expo-live-activity`
826
- * package — it ships a built-in SwiftUI widget template (SF Symbols,
827
- * custom progress bar with sliding icon, visibility-driven slots) and
828
- * forwards push tokens to the registration functions in this file
829
- * automatically. Use the lower-level `registerLiveActivityToken` and
830
- * `registerPushToStartToken` exports below only if you author your own
831
- * iOS Widget Extension and `ActivityAttributes` struct in Swift.
863
+ * For Live Activities, prefer the turnkey API from this same `sitepong`
864
+ * package (`startLiveActivity` in ./live-activity) — it ships a built-in
865
+ * SwiftUI widget template (SF Symbols, custom progress bar with sliding icon,
866
+ * visibility-driven slots) and forwards push tokens to the registration
867
+ * functions in this file automatically. Use the lower-level
868
+ * `registerLiveActivityToken` and `registerPushToStartToken` exports below
869
+ * only if you author your own iOS Widget Extension and `ActivityAttributes`
870
+ * struct in Swift.
832
871
  *
833
872
  * ## User attribution (two-step)
834
873
  *
@@ -876,10 +915,143 @@ declare function registerLiveActivityToken(activityType: string, activityId: str
876
915
  * Register a push-to-start token for remotely launching Live Activities (iOS 17.2+).
877
916
  */
878
917
  declare function registerPushToStartToken(activityType: string, pushToStartToken: string, options: PushEnvironmentOptions): void;
918
+
879
919
  /**
880
- * Mark a Live Activity as ended (deactivates its token).
920
+ * iOS Live Activities drives the SitePong built-in ActivityKit widget template
921
+ * from JavaScript. Ships inside the single `sitepong` package: the native
922
+ * `SitePongLiveActivity` module compiles into the Sitepong pod, and the widget
923
+ * extension is added by app.plugin.js when you opt in with
924
+ * `["sitepong", { "liveActivities": true }]`.
925
+ *
926
+ * Customers never write Swift — call startLiveActivity() with a typed
927
+ * ContentState and the SwiftUI template renders SF Symbols, custom progress
928
+ * bars, and styled text on the lock screen and Dynamic Island. Push tokens for
929
+ * remote updates are forwarded automatically to the SDK's push registration so
930
+ * that backend updates (POST /api/push/live-activity) reach the right device.
931
+ *
932
+ * Requires iOS 16.2+ at runtime. On web/Node or a device without ActivityKit,
933
+ * the native module is unavailable and these calls reject/no-op safely.
881
934
  */
882
- declare function endLiveActivity(activityType: string, activityId: string): void;
935
+ type SymbolWeight = 'ultraLight' | 'thin' | 'light' | 'regular' | 'medium' | 'semibold' | 'bold' | 'heavy' | 'black';
936
+ type TextWeight = SymbolWeight;
937
+ interface IconSpec {
938
+ symbol: string;
939
+ color?: string;
940
+ weight?: SymbolWeight;
941
+ size?: number;
942
+ }
943
+ interface TextStyle {
944
+ size?: number;
945
+ weight?: TextWeight;
946
+ color?: string;
947
+ italic?: boolean;
948
+ monospacedDigit?: boolean;
949
+ }
950
+ type ProgressSpec = {
951
+ style: 'bar';
952
+ value: number;
953
+ trackColor?: string;
954
+ fillColor?: string;
955
+ label?: string;
956
+ labelStyle?: TextStyle;
957
+ } | {
958
+ style: 'bar-with-icon';
959
+ value: number;
960
+ icon: string;
961
+ iconColor?: string;
962
+ iconWeight?: SymbolWeight;
963
+ trackColor?: string;
964
+ fillColor?: string;
965
+ label?: string;
966
+ labelStyle?: TextStyle;
967
+ } | {
968
+ style: 'circular';
969
+ value: number;
970
+ icon?: string;
971
+ iconColor?: string;
972
+ trackColor?: string;
973
+ fillColor?: string;
974
+ } | {
975
+ style: 'timer';
976
+ endDate?: number;
977
+ startDate?: number;
978
+ trackColor?: string;
979
+ fillColor?: string;
980
+ };
981
+ interface LiveActivityContentState {
982
+ title?: string;
983
+ subtitle?: string;
984
+ titleStyle?: TextStyle;
985
+ subtitleStyle?: TextStyle;
986
+ leadingIcon?: IconSpec;
987
+ trailingIcon?: IconSpec;
988
+ progress?: ProgressSpec;
989
+ backgroundColor?: string;
990
+ dynamicIsland?: {
991
+ compactLeading?: IconSpec | {
992
+ text: string;
993
+ style?: TextStyle;
994
+ };
995
+ compactTrailing?: IconSpec | {
996
+ text: string;
997
+ style?: TextStyle;
998
+ };
999
+ minimal?: IconSpec;
1000
+ };
1001
+ }
1002
+ interface LiveActivityAttributes {
1003
+ id?: string;
1004
+ metadata?: Record<string, string>;
1005
+ }
1006
+ interface StartLiveActivityOptions {
1007
+ activityType: string;
1008
+ attributes?: LiveActivityAttributes;
1009
+ contentState: LiveActivityContentState;
1010
+ environment?: 'sandbox' | 'production';
1011
+ alert?: {
1012
+ title?: string;
1013
+ body?: string;
1014
+ };
1015
+ }
1016
+ interface StartedLiveActivity {
1017
+ activityId: string;
1018
+ pushToken: string;
1019
+ }
1020
+ /**
1021
+ * Start a Live Activity. Returns the ActivityKit id and APNs push token, which
1022
+ * are also registered with the SDK so backend sends reach the right device.
1023
+ */
1024
+ declare function startLiveActivity(options: StartLiveActivityOptions): Promise<StartedLiveActivity>;
1025
+ /** Update a running Live Activity locally. */
1026
+ declare function updateLiveActivity(activityId: string, contentState: LiveActivityContentState, options?: {
1027
+ alert?: {
1028
+ title?: string;
1029
+ body?: string;
1030
+ };
1031
+ }): Promise<void>;
1032
+ /**
1033
+ * End a running Live Activity and deactivate its push token. Optionally pass a
1034
+ * final content state and a `dismissalDate` (epoch ms) for when the system
1035
+ * removes it from the lock screen.
1036
+ */
1037
+ declare function endLiveActivity(activityId: string, options?: {
1038
+ finalContentState?: LiveActivityContentState;
1039
+ dismissalDate?: number;
1040
+ }): Promise<void>;
1041
+ /** Are Live Activities supported and enabled by the user on this device? */
1042
+ declare function areLiveActivitiesEnabled(): Promise<boolean>;
1043
+ /**
1044
+ * Subscribe to push-token rotations. `startLiveActivity` already re-registers
1045
+ * tokens automatically; this is exposed for apps that drive ActivityKit
1046
+ * themselves. Returns a subscription with `.remove()`.
1047
+ */
1048
+ declare function onPushTokenUpdate(listener: (event: {
1049
+ activityId: string;
1050
+ activityType: string;
1051
+ pushToken: string;
1052
+ }) => void): {
1053
+ remove: () => void;
1054
+ };
883
1055
 
884
1056
  interface WidgetSyncOptions {
885
1057
  refreshInterval?: number;
@@ -992,4 +1164,4 @@ declare function reportConversion(props?: Record<string, unknown>): Promise<bool
992
1164
  */
993
1165
  declare function setRNStorage(storage: StorageAdapter | null): void;
994
1166
 
995
- export { type AsyncStorageLike, type DeferredDeepLinkHandler, type ErrorContext, type GroupTraits, type NavigationRef, type NavigationTrackerOptions, type NetworkInterceptionOptions, RNAutocaptureModule, type RNAutocaptureOptions, type RNDeviceInfo, type RNErrorHandlerOptions, RNPerformanceManager, type RemoteConfig, type ScreenRecordingConfig, type SitePongConfig, type SitePongInitConfig, SitePongRNProvider, type SitePongRNProviderProps, type SuperLinkConfig, type SuperLinkDeepLink, type SuperLinkEventType, type SuperLinkFingerprint, type SuperLinkIdentityMetadata, type SuperLinkMatchType, type SuperLinkPlatform, type TrackProperties, type TrackedDatabaseOptions, type UserTraits, type WatchtowerConfig, WatchtowerProvider, type WatchtowerProviderProps, type WidgetSyncOptions, addBreadcrumb, areFlagsReady, captureError, captureMessage, clearAnonymousId, clearUser, collectDeviceInfo, completeFromScan, createAsyncStorageAdapter, createNavigationTracker, createSuperLinkListener, createTrackedDatabase, endLiveActivity, fetchNativeDeviceSignals, fetchPersistentDeviceId, flush, flushScreenRecording, flushScreenRecordingOnError, getAllFlags, getAnonymousId, getFlag, getMatchedDeepLink, getRemoteConfig, getVariant, getVariantPayload, getWatchtowerSessionId, group, handleUniversalLink, identify$1 as identify, initRN, initSuperLinkRN, isInitialized, isRemoteConfigFeatureEnabled, isScreenRecording, markColdStart, onDeferredDeepLink, onRemoteConfigChange, parseUniversalLink, refreshFlags, registerDeviceToken, registerLiveActivityToken, registerPushToStartToken, registerPushToken, reportConversion, resetAnalytics, setAnonymousId, setContext, setCurrentScreen, setRNStorage, setTags, setUser, setWatchtowerUser, setupNetworkInterception, setupRNErrorHandler, startScreenRecording, startWatchtower, stopScreenRecording, stopWatchtower, stopWidgetSync, subscribeNavigationToWatchtower, identify as superlinkIdentify, syncWidgets, track, triggerWidgetRefresh, updateWidgetData, useAppState, useRNPerformance, useRemoteConfig, useScreenTrack, useSitePongRNContext, waitForFlags };
1167
+ export { type AsyncStorageLike, type DeferredDeepLinkHandler, type ErrorContext, type GroupTraits, type IconSpec, type LiveActivityAttributes, type LiveActivityContentState, type NavigationRef, type NavigationTrackerOptions, type NetworkInterceptionOptions, type ProgressSpec, RNAutocaptureModule, type RNAutocaptureOptions, type RNDeviceInfo, type RNErrorHandlerOptions, RNPerformanceManager, type RemoteConfig, type ScreenRecordingConfig, SensitiveView, type SitePongConfig, type SitePongInitConfig, SitePongRNProvider, type SitePongRNProviderProps, type StartLiveActivityOptions, type StartedLiveActivity, type SuperLinkConfig, type SuperLinkDeepLink, type SuperLinkEventType, type SuperLinkFingerprint, type SuperLinkIdentityMetadata, type SuperLinkMatchType, type SuperLinkPlatform, type SymbolWeight, type TextStyle, type TextWeight, type TrackProperties, type TrackedDatabaseOptions, type UserTraits, type WatchtowerConfig, WatchtowerProvider, type WatchtowerProviderProps, type WidgetSyncOptions, addBreadcrumb, addSensitiveView, areFlagsReady, areLiveActivitiesEnabled, captureError, captureMessage, clearAnonymousId, clearUser, collectDeviceInfo, completeFromScan, createAsyncStorageAdapter, createNavigationTracker, createSuperLinkListener, createTrackedDatabase, endLiveActivity, fetchNativeDeviceSignals, fetchPersistentDeviceId, flush, flushScreenRecording, flushScreenRecordingOnError, getAllFlags, getAnonymousId, getFlag, getMatchedDeepLink, getRemoteConfig, getVariant, getVariantPayload, getWatchtowerSessionId, group, handleUniversalLink, identify$1 as identify, initRN, initSuperLinkRN, isInitialized, isRemoteConfigFeatureEnabled, isScreenRecording, markColdStart, onDeferredDeepLink, onPushTokenUpdate, onRemoteConfigChange, parseUniversalLink, refreshFlags, registerDeviceToken, registerLiveActivityToken, registerPushToStartToken, registerPushToken, removeSensitiveView, reportConversion, resetAnalytics, setAnonymousId, setContext, setCurrentScreen, setRNStorage, setTags, setUser, setWatchtowerUser, setupNetworkInterception, setupRNErrorHandler, startLiveActivity, startScreenRecording, startWatchtower, stopScreenRecording, stopWatchtower, stopWidgetSync, subscribeNavigationToWatchtower, identify as superlinkIdentify, syncWidgets, track, triggerWidgetRefresh, updateLiveActivity, updateWidgetData, useAppState, useRNPerformance, useRemoteConfig, useScreenTrack, useSitePongRNContext, waitForFlags };
@@ -2,8 +2,8 @@
2
2
 
3
3
  var React2 = require('react');
4
4
  var reactNative = require('react-native');
5
+ var expoModulesCore = require('expo-modules-core');
5
6
  var jsxRuntime = require('react/jsx-runtime');
6
- var screenRecorder = require('@sitepong/screen-recorder');
7
7
 
8
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
 
@@ -1918,7 +1918,8 @@ var RemoteConfigManager = class {
1918
1918
  if (this.refreshTimer) {
1919
1919
  clearTimeout(this.refreshTimer);
1920
1920
  }
1921
- const ttlMs = this.config.ttl * 1e3;
1921
+ const ttlSec = typeof this.config.ttl === "number" && this.config.ttl > 0 ? this.config.ttl : 300;
1922
+ const ttlMs = ttlSec * 1e3;
1922
1923
  this.refreshTimer = setTimeout(() => {
1923
1924
  this.fetchAndApply().catch((err) => {
1924
1925
  this.log("Refresh failed:", err);
@@ -3155,6 +3156,39 @@ function createAsyncStorageAdapter(asyncStorage) {
3155
3156
  removeItem: (key) => asyncStorage.removeItem(key)
3156
3157
  };
3157
3158
  }
3159
+ var moduleCache = /* @__PURE__ */ new Map();
3160
+ function getNativeModule(name) {
3161
+ if (moduleCache.has(name)) {
3162
+ return moduleCache.get(name);
3163
+ }
3164
+ let mod = null;
3165
+ try {
3166
+ mod = expoModulesCore.requireNativeModule(name);
3167
+ } catch {
3168
+ mod = null;
3169
+ }
3170
+ moduleCache.set(name, mod);
3171
+ return mod;
3172
+ }
3173
+ function getNativeEventEmitter(nativeModule) {
3174
+ if (!nativeModule) return null;
3175
+ try {
3176
+ return new expoModulesCore.EventEmitter(nativeModule);
3177
+ } catch {
3178
+ return null;
3179
+ }
3180
+ }
3181
+ function getScreenRecorderModule() {
3182
+ return getNativeModule("SitePongScreenRecorder");
3183
+ }
3184
+ function getDeviceIdModule() {
3185
+ return getNativeModule("SitePongDeviceId");
3186
+ }
3187
+ function getLiveActivityModule() {
3188
+ return getNativeModule("SitePongLiveActivity");
3189
+ }
3190
+
3191
+ // src/react-native/device.ts
3158
3192
  function collectDeviceInfo() {
3159
3193
  const screen = reactNative.Dimensions.get("screen");
3160
3194
  return {
@@ -3166,23 +3200,25 @@ function collectDeviceInfo() {
3166
3200
  };
3167
3201
  }
3168
3202
  async function fetchPersistentDeviceId() {
3169
- return null;
3203
+ const mod = getDeviceIdModule();
3204
+ if (!mod?.getDeviceId) return null;
3205
+ try {
3206
+ return await mod.getDeviceId();
3207
+ } catch {
3208
+ return null;
3209
+ }
3170
3210
  }
3171
3211
  async function fetchNativeDeviceSignals() {
3172
- return null;
3173
- }
3174
-
3175
- // src/react-native/screen-recorder.ts
3176
- var nativeModule = null;
3177
- function getNativeModule() {
3178
- if (!nativeModule) {
3179
- try {
3180
- nativeModule = __require("@sitepong/screen-recorder");
3181
- } catch {
3182
- nativeModule = null;
3183
- }
3212
+ const mod = getDeviceIdModule();
3213
+ if (!mod?.getDeviceSignals) return null;
3214
+ try {
3215
+ return await mod.getDeviceSignals();
3216
+ } catch {
3217
+ return null;
3184
3218
  }
3185
- return nativeModule;
3219
+ }
3220
+ function getNativeModule2() {
3221
+ return getScreenRecorderModule();
3186
3222
  }
3187
3223
  var ERROR_BUFFER_DURATION = 1e4;
3188
3224
  var MANUAL_BUFFER_DURATION = 6e4;
@@ -3202,7 +3238,7 @@ function startScreenRecording(apiKey, endpoint, sessionId, config = {}) {
3202
3238
  });
3203
3239
  }
3204
3240
  function _startRecording(apiKey, endpoint, sessionId, config) {
3205
- const mod = getNativeModule();
3241
+ const mod = getNativeModule2();
3206
3242
  if (!mod) return false;
3207
3243
  if (!config.enabled) return false;
3208
3244
  const sampleRate = config.sampleRate ?? 1;
@@ -3225,7 +3261,7 @@ function _startRecording(apiKey, endpoint, sessionId, config) {
3225
3261
  return true;
3226
3262
  }
3227
3263
  function stopScreenRecording() {
3228
- const mod = getNativeModule();
3264
+ const mod = getNativeModule2();
3229
3265
  if (!mod || !recordingActive) return;
3230
3266
  if (maxDurationTimer) {
3231
3267
  clearTimeout(maxDurationTimer);
@@ -3236,7 +3272,7 @@ function stopScreenRecording() {
3236
3272
  recordingActive = false;
3237
3273
  }
3238
3274
  async function flushScreenRecording() {
3239
- const mod = getNativeModule();
3275
+ const mod = getNativeModule2();
3240
3276
  if (!mod || !recordingActive) return;
3241
3277
  try {
3242
3278
  await mod.flushBuffer();
@@ -3248,6 +3284,34 @@ var flushScreenRecordingOnError = flushScreenRecording;
3248
3284
  function isScreenRecording() {
3249
3285
  return recordingActive;
3250
3286
  }
3287
+ function addSensitiveView(viewTag) {
3288
+ getNativeModule2()?.addSensitiveView(viewTag);
3289
+ }
3290
+ function removeSensitiveView(viewTag) {
3291
+ getNativeModule2()?.removeSensitiveView(viewTag);
3292
+ }
3293
+ function SensitiveView(props) {
3294
+ const tagRef = React2.useRef(null);
3295
+ React2.useEffect(() => {
3296
+ return () => {
3297
+ if (tagRef.current != null) removeSensitiveView(tagRef.current);
3298
+ };
3299
+ }, []);
3300
+ const handleLayout = React2.useCallback((e) => {
3301
+ const nativeTag = e.nativeEvent.target;
3302
+ if (typeof nativeTag === "number" && tagRef.current !== nativeTag) {
3303
+ if (tagRef.current != null) removeSensitiveView(tagRef.current);
3304
+ tagRef.current = nativeTag;
3305
+ addSensitiveView(nativeTag);
3306
+ }
3307
+ }, []);
3308
+ const { View } = __require("react-native");
3309
+ return React2.createElement(
3310
+ View,
3311
+ { collapsable: false, onLayout: handleLayout },
3312
+ props.children
3313
+ );
3314
+ }
3251
3315
 
3252
3316
  // src/react-native/error-handler.ts
3253
3317
  function setupRNErrorHandler(options = {}) {
@@ -3768,10 +3832,24 @@ function useRNPerformance(screenName) {
3768
3832
  }, [screenName, performanceManager]);
3769
3833
  return { duration };
3770
3834
  }
3835
+ var warnedMissing = false;
3836
+ function loadBridge() {
3837
+ return getScreenRecorderModule();
3838
+ }
3771
3839
  var started = false;
3772
3840
  function startWatchtower(config) {
3773
3841
  if (started) return;
3774
- screenRecorder.watchtowerStart({
3842
+ const bridge = loadBridge();
3843
+ if (!bridge) {
3844
+ if (!warnedMissing) {
3845
+ warnedMissing = true;
3846
+ console.warn(
3847
+ "[SitePong] Watchtower is enabled but the native module is unavailable. Rebuild the native app (npx expo prebuild && npx expo run:ios) so the bundled SitePong native module is linked."
3848
+ );
3849
+ }
3850
+ return;
3851
+ }
3852
+ bridge.watchtowerStart({
3775
3853
  apiKey: config.apiKey,
3776
3854
  projectId: config.projectId,
3777
3855
  endpoint: config.endpoint,
@@ -3783,27 +3861,27 @@ function startWatchtower(config) {
3783
3861
  }
3784
3862
  function setWatchtowerUser(distinctId) {
3785
3863
  try {
3786
- screenRecorder.watchtowerSetUser(distinctId);
3864
+ loadBridge()?.watchtowerSetUser(distinctId);
3787
3865
  } catch {
3788
3866
  }
3789
3867
  }
3790
3868
  function getWatchtowerSessionId() {
3791
3869
  try {
3792
- return screenRecorder.watchtowerGetSessionId();
3870
+ return loadBridge()?.watchtowerGetSessionId() ?? null;
3793
3871
  } catch {
3794
3872
  return null;
3795
3873
  }
3796
3874
  }
3797
3875
  function stopWatchtower() {
3798
3876
  if (!started) return;
3799
- screenRecorder.watchtowerStop();
3877
+ loadBridge()?.watchtowerStop();
3800
3878
  started = false;
3801
3879
  }
3802
3880
  function subscribeNavigationToWatchtower(navigationRef) {
3803
3881
  const unsubscribe = createNavigationTracker(navigationRef, {
3804
3882
  screenNameFilter: (name) => {
3805
3883
  try {
3806
- screenRecorder.watchtowerSetScreen(name);
3884
+ loadBridge()?.watchtowerSetScreen(name);
3807
3885
  } catch {
3808
3886
  }
3809
3887
  return name;
@@ -3811,7 +3889,7 @@ function subscribeNavigationToWatchtower(navigationRef) {
3811
3889
  });
3812
3890
  try {
3813
3891
  const route = navigationRef.getCurrentRoute?.();
3814
- if (route?.name) screenRecorder.watchtowerSetScreen(route.name);
3892
+ if (route?.name) loadBridge()?.watchtowerSetScreen(route.name);
3815
3893
  } catch {
3816
3894
  }
3817
3895
  return unsubscribe;
@@ -3933,16 +4011,84 @@ function createTrackedDatabase(db, options = {}) {
3933
4011
  });
3934
4012
  }
3935
4013
 
3936
- // src/react-native/widgets.ts
3937
- var syncTimer = null;
3938
- var cachedVersion = 0;
3939
- function getNativeModule2() {
4014
+ // src/react-native/live-activity.ts
4015
+ var activityTypeById = /* @__PURE__ */ new Map();
4016
+ function requireNative() {
4017
+ const mod = getLiveActivityModule();
4018
+ if (!mod) {
4019
+ throw new Error(
4020
+ '[SitePong] Live Activities native module unavailable. Enable the config plugin (["sitepong", { "liveActivities": true }]) and rebuild native (iOS 16.2+).'
4021
+ );
4022
+ }
4023
+ return mod;
4024
+ }
4025
+ async function startLiveActivity(options) {
4026
+ const environment = options.environment ?? "production";
4027
+ const native = requireNative();
4028
+ ensureTokenListener();
4029
+ const result = await native.startActivity({
4030
+ activityType: options.activityType,
4031
+ attributes: options.attributes ?? {},
4032
+ contentState: options.contentState,
4033
+ alert: options.alert
4034
+ });
4035
+ activityTypeById.set(result.activityId, options.activityType);
4036
+ registerLiveActivityToken(options.activityType, result.activityId, result.pushToken, { environment });
4037
+ return result;
4038
+ }
4039
+ async function updateLiveActivity(activityId, contentState, options) {
4040
+ await requireNative().updateActivity({ activityId, contentState, alert: options?.alert });
4041
+ }
4042
+ async function endLiveActivity2(activityId, options) {
4043
+ await requireNative().endActivity({
4044
+ activityId,
4045
+ contentState: options?.finalContentState,
4046
+ dismissalDate: options?.dismissalDate
4047
+ });
4048
+ const activityType = activityTypeById.get(activityId);
4049
+ activityTypeById.delete(activityId);
4050
+ if (activityType) endLiveActivity(activityType, activityId);
4051
+ }
4052
+ async function areLiveActivitiesEnabled() {
4053
+ const mod = getLiveActivityModule();
4054
+ if (!mod?.areActivitiesEnabled) return false;
3940
4055
  try {
3941
- return __require("@sitepong/expo-live-activity");
4056
+ return await mod.areActivitiesEnabled();
3942
4057
  } catch {
3943
- return null;
4058
+ return false;
3944
4059
  }
3945
4060
  }
4061
+ function onPushTokenUpdate(listener) {
4062
+ ensureTokenListener();
4063
+ const emitter = getNativeEventEmitter(getLiveActivityModule());
4064
+ if (!emitter) return { remove: () => {
4065
+ } };
4066
+ const subscription = emitter.addListener("liveActivityPushTokenUpdate", listener);
4067
+ return { remove: () => subscription.remove() };
4068
+ }
4069
+ var tokenListenerInstalled = false;
4070
+ function ensureTokenListener() {
4071
+ if (tokenListenerInstalled) return;
4072
+ const emitter = getNativeEventEmitter(getLiveActivityModule());
4073
+ if (!emitter) return;
4074
+ tokenListenerInstalled = true;
4075
+ emitter.addListener(
4076
+ "liveActivityPushTokenUpdate",
4077
+ (event) => {
4078
+ activityTypeById.set(event.activityId, event.activityType);
4079
+ registerLiveActivityToken(event.activityType, event.activityId, event.pushToken, {
4080
+ environment: "production"
4081
+ });
4082
+ }
4083
+ );
4084
+ }
4085
+
4086
+ // src/react-native/widgets.ts
4087
+ var syncTimer = null;
4088
+ var cachedVersion = 0;
4089
+ function getNativeModule3() {
4090
+ return getLiveActivityModule();
4091
+ }
3946
4092
  function getEndpoint2() {
3947
4093
  const cfg = sitepong.config;
3948
4094
  return cfg?.endpoint || "https://api.sitepong.com";
@@ -3954,7 +4100,7 @@ function getApiKey2() {
3954
4100
  async function syncWidgets(options) {
3955
4101
  const apiKey = getApiKey2();
3956
4102
  if (!apiKey) return;
3957
- const mod = getNativeModule2();
4103
+ const mod = getNativeModule3();
3958
4104
  if (!mod) return;
3959
4105
  try {
3960
4106
  const res = await fetch(`${getEndpoint2()}/api/sdk/widgets/config`, {
@@ -3975,7 +4121,7 @@ async function syncWidgets(options) {
3975
4121
  syncTimer = setInterval(() => syncWidgets(options), interval);
3976
4122
  }
3977
4123
  async function updateWidgetData(data) {
3978
- const mod = getNativeModule2();
4124
+ const mod = getNativeModule3();
3979
4125
  if (!mod) return;
3980
4126
  try {
3981
4127
  await mod.updateWidgetData(JSON.stringify(data));
@@ -3984,7 +4130,7 @@ async function updateWidgetData(data) {
3984
4130
  }
3985
4131
  }
3986
4132
  function triggerWidgetRefresh() {
3987
- const mod = getNativeModule2();
4133
+ const mod = getNativeModule3();
3988
4134
  if (!mod) return;
3989
4135
  mod.updateWidgetData("{}").catch(() => {
3990
4136
  });
@@ -4179,10 +4325,13 @@ function setRNStorage(storage) {
4179
4325
 
4180
4326
  exports.RNAutocaptureModule = RNAutocaptureModule;
4181
4327
  exports.RNPerformanceManager = RNPerformanceManager;
4328
+ exports.SensitiveView = SensitiveView;
4182
4329
  exports.SitePongRNProvider = SitePongRNProvider;
4183
4330
  exports.WatchtowerProvider = WatchtowerProvider;
4184
4331
  exports.addBreadcrumb = addBreadcrumb;
4332
+ exports.addSensitiveView = addSensitiveView;
4185
4333
  exports.areFlagsReady = areFlagsReady;
4334
+ exports.areLiveActivitiesEnabled = areLiveActivitiesEnabled;
4186
4335
  exports.captureError = captureError;
4187
4336
  exports.captureMessage = captureMessage;
4188
4337
  exports.clearAnonymousId = clearAnonymousId;
@@ -4193,7 +4342,7 @@ exports.createAsyncStorageAdapter = createAsyncStorageAdapter;
4193
4342
  exports.createNavigationTracker = createNavigationTracker;
4194
4343
  exports.createSuperLinkListener = createSuperLinkListener;
4195
4344
  exports.createTrackedDatabase = createTrackedDatabase;
4196
- exports.endLiveActivity = endLiveActivity;
4345
+ exports.endLiveActivity = endLiveActivity2;
4197
4346
  exports.fetchNativeDeviceSignals = fetchNativeDeviceSignals;
4198
4347
  exports.fetchPersistentDeviceId = fetchPersistentDeviceId;
4199
4348
  exports.flush = flush;
@@ -4217,6 +4366,7 @@ exports.isRemoteConfigFeatureEnabled = isRemoteConfigFeatureEnabled;
4217
4366
  exports.isScreenRecording = isScreenRecording;
4218
4367
  exports.markColdStart = markColdStart;
4219
4368
  exports.onDeferredDeepLink = onDeferredDeepLink;
4369
+ exports.onPushTokenUpdate = onPushTokenUpdate;
4220
4370
  exports.onRemoteConfigChange = onRemoteConfigChange;
4221
4371
  exports.parseUniversalLink = parseUniversalLink;
4222
4372
  exports.refreshFlags = refreshFlags;
@@ -4224,6 +4374,7 @@ exports.registerDeviceToken = registerDeviceToken;
4224
4374
  exports.registerLiveActivityToken = registerLiveActivityToken;
4225
4375
  exports.registerPushToStartToken = registerPushToStartToken;
4226
4376
  exports.registerPushToken = registerPushToken;
4377
+ exports.removeSensitiveView = removeSensitiveView;
4227
4378
  exports.reportConversion = reportConversion;
4228
4379
  exports.resetAnalytics = resetAnalytics;
4229
4380
  exports.setAnonymousId = setAnonymousId;
@@ -4235,6 +4386,7 @@ exports.setUser = setUser;
4235
4386
  exports.setWatchtowerUser = setWatchtowerUser;
4236
4387
  exports.setupNetworkInterception = setupNetworkInterception;
4237
4388
  exports.setupRNErrorHandler = setupRNErrorHandler;
4389
+ exports.startLiveActivity = startLiveActivity;
4238
4390
  exports.startScreenRecording = startScreenRecording;
4239
4391
  exports.startWatchtower = startWatchtower;
4240
4392
  exports.stopScreenRecording = stopScreenRecording;
@@ -4245,6 +4397,7 @@ exports.superlinkIdentify = identify3;
4245
4397
  exports.syncWidgets = syncWidgets;
4246
4398
  exports.track = track;
4247
4399
  exports.triggerWidgetRefresh = triggerWidgetRefresh;
4400
+ exports.updateLiveActivity = updateLiveActivity;
4248
4401
  exports.updateWidgetData = updateWidgetData;
4249
4402
  exports.useAppState = useAppState;
4250
4403
  exports.useRNPerformance = useRNPerformance;