sitepong 0.2.3 → 0.2.5

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 +187 -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 +17 -10
@@ -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,33 @@ 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
+ return React2.createElement(
3309
+ reactNative.View,
3310
+ { collapsable: false, onLayout: handleLayout },
3311
+ props.children
3312
+ );
3313
+ }
3251
3314
 
3252
3315
  // src/react-native/error-handler.ts
3253
3316
  function setupRNErrorHandler(options = {}) {
@@ -3768,10 +3831,24 @@ function useRNPerformance(screenName) {
3768
3831
  }, [screenName, performanceManager]);
3769
3832
  return { duration };
3770
3833
  }
3834
+ var warnedMissing = false;
3835
+ function loadBridge() {
3836
+ return getScreenRecorderModule();
3837
+ }
3771
3838
  var started = false;
3772
3839
  function startWatchtower(config) {
3773
3840
  if (started) return;
3774
- screenRecorder.watchtowerStart({
3841
+ const bridge = loadBridge();
3842
+ if (!bridge) {
3843
+ if (!warnedMissing) {
3844
+ warnedMissing = true;
3845
+ console.warn(
3846
+ "[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."
3847
+ );
3848
+ }
3849
+ return;
3850
+ }
3851
+ bridge.watchtowerStart({
3775
3852
  apiKey: config.apiKey,
3776
3853
  projectId: config.projectId,
3777
3854
  endpoint: config.endpoint,
@@ -3783,27 +3860,27 @@ function startWatchtower(config) {
3783
3860
  }
3784
3861
  function setWatchtowerUser(distinctId) {
3785
3862
  try {
3786
- screenRecorder.watchtowerSetUser(distinctId);
3863
+ loadBridge()?.watchtowerSetUser(distinctId);
3787
3864
  } catch {
3788
3865
  }
3789
3866
  }
3790
3867
  function getWatchtowerSessionId() {
3791
3868
  try {
3792
- return screenRecorder.watchtowerGetSessionId();
3869
+ return loadBridge()?.watchtowerGetSessionId() ?? null;
3793
3870
  } catch {
3794
3871
  return null;
3795
3872
  }
3796
3873
  }
3797
3874
  function stopWatchtower() {
3798
3875
  if (!started) return;
3799
- screenRecorder.watchtowerStop();
3876
+ loadBridge()?.watchtowerStop();
3800
3877
  started = false;
3801
3878
  }
3802
3879
  function subscribeNavigationToWatchtower(navigationRef) {
3803
3880
  const unsubscribe = createNavigationTracker(navigationRef, {
3804
3881
  screenNameFilter: (name) => {
3805
3882
  try {
3806
- screenRecorder.watchtowerSetScreen(name);
3883
+ loadBridge()?.watchtowerSetScreen(name);
3807
3884
  } catch {
3808
3885
  }
3809
3886
  return name;
@@ -3811,7 +3888,7 @@ function subscribeNavigationToWatchtower(navigationRef) {
3811
3888
  });
3812
3889
  try {
3813
3890
  const route = navigationRef.getCurrentRoute?.();
3814
- if (route?.name) screenRecorder.watchtowerSetScreen(route.name);
3891
+ if (route?.name) loadBridge()?.watchtowerSetScreen(route.name);
3815
3892
  } catch {
3816
3893
  }
3817
3894
  return unsubscribe;
@@ -3933,16 +4010,84 @@ function createTrackedDatabase(db, options = {}) {
3933
4010
  });
3934
4011
  }
3935
4012
 
3936
- // src/react-native/widgets.ts
3937
- var syncTimer = null;
3938
- var cachedVersion = 0;
3939
- function getNativeModule2() {
4013
+ // src/react-native/live-activity.ts
4014
+ var activityTypeById = /* @__PURE__ */ new Map();
4015
+ function requireNative() {
4016
+ const mod = getLiveActivityModule();
4017
+ if (!mod) {
4018
+ throw new Error(
4019
+ '[SitePong] Live Activities native module unavailable. Enable the config plugin (["sitepong", { "liveActivities": true }]) and rebuild native (iOS 16.2+).'
4020
+ );
4021
+ }
4022
+ return mod;
4023
+ }
4024
+ async function startLiveActivity(options) {
4025
+ const environment = options.environment ?? "production";
4026
+ const native = requireNative();
4027
+ ensureTokenListener();
4028
+ const result = await native.startActivity({
4029
+ activityType: options.activityType,
4030
+ attributes: options.attributes ?? {},
4031
+ contentState: options.contentState,
4032
+ alert: options.alert
4033
+ });
4034
+ activityTypeById.set(result.activityId, options.activityType);
4035
+ registerLiveActivityToken(options.activityType, result.activityId, result.pushToken, { environment });
4036
+ return result;
4037
+ }
4038
+ async function updateLiveActivity(activityId, contentState, options) {
4039
+ await requireNative().updateActivity({ activityId, contentState, alert: options?.alert });
4040
+ }
4041
+ async function endLiveActivity2(activityId, options) {
4042
+ await requireNative().endActivity({
4043
+ activityId,
4044
+ contentState: options?.finalContentState,
4045
+ dismissalDate: options?.dismissalDate
4046
+ });
4047
+ const activityType = activityTypeById.get(activityId);
4048
+ activityTypeById.delete(activityId);
4049
+ if (activityType) endLiveActivity(activityType, activityId);
4050
+ }
4051
+ async function areLiveActivitiesEnabled() {
4052
+ const mod = getLiveActivityModule();
4053
+ if (!mod?.areActivitiesEnabled) return false;
3940
4054
  try {
3941
- return __require("@sitepong/expo-live-activity");
4055
+ return await mod.areActivitiesEnabled();
3942
4056
  } catch {
3943
- return null;
4057
+ return false;
3944
4058
  }
3945
4059
  }
4060
+ function onPushTokenUpdate(listener) {
4061
+ ensureTokenListener();
4062
+ const emitter = getNativeEventEmitter(getLiveActivityModule());
4063
+ if (!emitter) return { remove: () => {
4064
+ } };
4065
+ const subscription = emitter.addListener("liveActivityPushTokenUpdate", listener);
4066
+ return { remove: () => subscription.remove() };
4067
+ }
4068
+ var tokenListenerInstalled = false;
4069
+ function ensureTokenListener() {
4070
+ if (tokenListenerInstalled) return;
4071
+ const emitter = getNativeEventEmitter(getLiveActivityModule());
4072
+ if (!emitter) return;
4073
+ tokenListenerInstalled = true;
4074
+ emitter.addListener(
4075
+ "liveActivityPushTokenUpdate",
4076
+ (event) => {
4077
+ activityTypeById.set(event.activityId, event.activityType);
4078
+ registerLiveActivityToken(event.activityType, event.activityId, event.pushToken, {
4079
+ environment: "production"
4080
+ });
4081
+ }
4082
+ );
4083
+ }
4084
+
4085
+ // src/react-native/widgets.ts
4086
+ var syncTimer = null;
4087
+ var cachedVersion = 0;
4088
+ function getNativeModule3() {
4089
+ return getLiveActivityModule();
4090
+ }
3946
4091
  function getEndpoint2() {
3947
4092
  const cfg = sitepong.config;
3948
4093
  return cfg?.endpoint || "https://api.sitepong.com";
@@ -3954,7 +4099,7 @@ function getApiKey2() {
3954
4099
  async function syncWidgets(options) {
3955
4100
  const apiKey = getApiKey2();
3956
4101
  if (!apiKey) return;
3957
- const mod = getNativeModule2();
4102
+ const mod = getNativeModule3();
3958
4103
  if (!mod) return;
3959
4104
  try {
3960
4105
  const res = await fetch(`${getEndpoint2()}/api/sdk/widgets/config`, {
@@ -3975,7 +4120,7 @@ async function syncWidgets(options) {
3975
4120
  syncTimer = setInterval(() => syncWidgets(options), interval);
3976
4121
  }
3977
4122
  async function updateWidgetData(data) {
3978
- const mod = getNativeModule2();
4123
+ const mod = getNativeModule3();
3979
4124
  if (!mod) return;
3980
4125
  try {
3981
4126
  await mod.updateWidgetData(JSON.stringify(data));
@@ -3984,7 +4129,7 @@ async function updateWidgetData(data) {
3984
4129
  }
3985
4130
  }
3986
4131
  function triggerWidgetRefresh() {
3987
- const mod = getNativeModule2();
4132
+ const mod = getNativeModule3();
3988
4133
  if (!mod) return;
3989
4134
  mod.updateWidgetData("{}").catch(() => {
3990
4135
  });
@@ -4179,10 +4324,13 @@ function setRNStorage(storage) {
4179
4324
 
4180
4325
  exports.RNAutocaptureModule = RNAutocaptureModule;
4181
4326
  exports.RNPerformanceManager = RNPerformanceManager;
4327
+ exports.SensitiveView = SensitiveView;
4182
4328
  exports.SitePongRNProvider = SitePongRNProvider;
4183
4329
  exports.WatchtowerProvider = WatchtowerProvider;
4184
4330
  exports.addBreadcrumb = addBreadcrumb;
4331
+ exports.addSensitiveView = addSensitiveView;
4185
4332
  exports.areFlagsReady = areFlagsReady;
4333
+ exports.areLiveActivitiesEnabled = areLiveActivitiesEnabled;
4186
4334
  exports.captureError = captureError;
4187
4335
  exports.captureMessage = captureMessage;
4188
4336
  exports.clearAnonymousId = clearAnonymousId;
@@ -4193,7 +4341,7 @@ exports.createAsyncStorageAdapter = createAsyncStorageAdapter;
4193
4341
  exports.createNavigationTracker = createNavigationTracker;
4194
4342
  exports.createSuperLinkListener = createSuperLinkListener;
4195
4343
  exports.createTrackedDatabase = createTrackedDatabase;
4196
- exports.endLiveActivity = endLiveActivity;
4344
+ exports.endLiveActivity = endLiveActivity2;
4197
4345
  exports.fetchNativeDeviceSignals = fetchNativeDeviceSignals;
4198
4346
  exports.fetchPersistentDeviceId = fetchPersistentDeviceId;
4199
4347
  exports.flush = flush;
@@ -4217,6 +4365,7 @@ exports.isRemoteConfigFeatureEnabled = isRemoteConfigFeatureEnabled;
4217
4365
  exports.isScreenRecording = isScreenRecording;
4218
4366
  exports.markColdStart = markColdStart;
4219
4367
  exports.onDeferredDeepLink = onDeferredDeepLink;
4368
+ exports.onPushTokenUpdate = onPushTokenUpdate;
4220
4369
  exports.onRemoteConfigChange = onRemoteConfigChange;
4221
4370
  exports.parseUniversalLink = parseUniversalLink;
4222
4371
  exports.refreshFlags = refreshFlags;
@@ -4224,6 +4373,7 @@ exports.registerDeviceToken = registerDeviceToken;
4224
4373
  exports.registerLiveActivityToken = registerLiveActivityToken;
4225
4374
  exports.registerPushToStartToken = registerPushToStartToken;
4226
4375
  exports.registerPushToken = registerPushToken;
4376
+ exports.removeSensitiveView = removeSensitiveView;
4227
4377
  exports.reportConversion = reportConversion;
4228
4378
  exports.resetAnalytics = resetAnalytics;
4229
4379
  exports.setAnonymousId = setAnonymousId;
@@ -4235,6 +4385,7 @@ exports.setUser = setUser;
4235
4385
  exports.setWatchtowerUser = setWatchtowerUser;
4236
4386
  exports.setupNetworkInterception = setupNetworkInterception;
4237
4387
  exports.setupRNErrorHandler = setupRNErrorHandler;
4388
+ exports.startLiveActivity = startLiveActivity;
4238
4389
  exports.startScreenRecording = startScreenRecording;
4239
4390
  exports.startWatchtower = startWatchtower;
4240
4391
  exports.stopScreenRecording = stopScreenRecording;
@@ -4245,6 +4396,7 @@ exports.superlinkIdentify = identify3;
4245
4396
  exports.syncWidgets = syncWidgets;
4246
4397
  exports.track = track;
4247
4398
  exports.triggerWidgetRefresh = triggerWidgetRefresh;
4399
+ exports.updateLiveActivity = updateLiveActivity;
4248
4400
  exports.updateWidgetData = updateWidgetData;
4249
4401
  exports.useAppState = useAppState;
4250
4402
  exports.useRNPerformance = useRNPerformance;