catalyst-core-internal 0.1.0 → 0.1.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.
Files changed (27) hide show
  1. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +10 -2
  2. package/dist/native/bridge/hooks.js +4 -4
  3. package/dist/native/bridge/useBaseHook.js +3 -4
  4. package/dist/native/bridge/utils/NativeBridge.js +4 -4
  5. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +2 -13
  6. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +0 -36
  7. package/dist/native/iosnativeWebView/iosnativeWebView.xctestplan +0 -1
  8. package/dist/native/iosnativeWebView/iosnativeWebViewTests/FrameworkServerUtilsTests.swift +4 -14
  9. package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +21 -9
  10. package/mcp_v2/conversion-tasks.json +371 -0
  11. package/mcp_v2/knowledge-base.json +1450 -0
  12. package/mcp_v2/lib/helpers.js +145 -0
  13. package/mcp_v2/mcp.js +366 -0
  14. package/mcp_v2/package.json +13 -0
  15. package/mcp_v2/schema.sql +88 -0
  16. package/mcp_v2/setup.js +262 -0
  17. package/mcp_v2/tools/build.js +449 -0
  18. package/mcp_v2/tools/config.js +262 -0
  19. package/mcp_v2/tools/conversion.js +492 -0
  20. package/mcp_v2/tools/debug.js +62 -0
  21. package/mcp_v2/tools/knowledge.js +213 -0
  22. package/mcp_v2/tools/sync.js +21 -0
  23. package/mcp_v2/tools/tasks.js +844 -0
  24. package/package.json +1 -1
  25. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/SecurityBridgeTest.kt +0 -199
  26. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeCommandHandlerSecurityTests.swift +0 -212
  27. package/dist/native/iosnativeWebView/iosnativeWebViewTests/ScreenSecureManagerTests.swift +0 -121
@@ -62,8 +62,16 @@ object BridgeMessageValidator {
62
62
  private const val TAG = "BridgeMessageValidator"
63
63
  private const val MAX_MESSAGE_SIZE = 10 * 1024 * 1024 // 10MB (match iOS)
64
64
 
65
- // Valid commands single source of truth from CatalystConstants
66
- private val validCommands = CatalystConstants.Bridge.VALID_COMMANDS
65
+ // Valid commands (should match iOS CatalystConstants.Bridge.validCommands)
66
+ private val validCommands = setOf(
67
+ "openCamera",
68
+ "requestCameraPermission",
69
+ "pickFile",
70
+ "requestHapticFeedback",
71
+ "openFileWithIntent",
72
+ "getDeviceInfo",
73
+ "logger"
74
+ )
67
75
 
68
76
  // MARK: - Schema Definitions (mirror iOS schemas)
69
77
 
@@ -103,12 +103,12 @@ if(_NativeBridge.default.isAvailable()){try{// Call native bridge directly witho
103
103
  if(_NativeBridge.default.isAndroid&&window.NativeBridge?.checkNotificationPermissionStatus){window.NativeBridge.checkNotificationPermissionStatus(null);}else if(_NativeBridge.default.isIOS&&window.webkit?.messageHandlers?.NativeBridge){window.webkit.messageHandlers.NativeBridge.postMessage({command:"checkNotificationPermissionStatus",data:null});}}catch(error){console.error("🔔 Error checking notification permission status:",error);}}window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.success===false||result?.error){base.handleNativeError(result.error||result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.success===false||result?.error){base.handleNativeError(result.error||result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setPushToken(result.token);base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_RECEIVED,data=>{const notification=typeof data==="string"?JSON.parse(data):data;setLastNotification(notification);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_TAPPED,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED,data=>{const action=typeof data==="string"?JSON.parse(data):data;base.setDataAndComplete(action);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}const success=result?.success;if(success===false){base.handleNativeError(result);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}if(result?.success===false){base.handleNativeError(result);return;}setSubscribedTopics(result.topics||[]);base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}});return()=>{// Cleanup - unregister from shared callback system
104
104
  unregister();// Cleanup other callbacks
105
105
  window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_RECEIVED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_TAPPED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT);};},[]);// Local notification scheduling with all supported styles
106
- const scheduleLocal=config=>{base.executeOperation(()=>{_NativeBridge.default.notification.scheduleLocal(config);},"schedule local notification");};const cancelLocal=notificationId=>{base.executeOperation(()=>{_NativeBridge.default.notification.cancelLocal(notificationId);},"cancel notification");};const registerForPush=()=>{base.executeOperation(()=>{_NativeBridge.default.notification.registerForPush();},"register for push");};const updateBadge=count=>{base.callNative(()=>_NativeBridge.default.notification.updateBadge(count));setBadges(count);};const subscribeToTopic=topic=>{base.executeOperation(()=>{_NativeBridge.default.notification.subscribeToTopic(topic);},"subscribe to topic");};const unsubscribeFromTopic=topic=>{base.executeOperation(()=>{_NativeBridge.default.notification.unsubscribeFromTopic(topic);},"unsubscribe from topic");};const getSubscribedTopics=()=>{base.executeOperation(()=>{_NativeBridge.default.notification.getSubscribedTopics();},"get subscribed topics");};return{// Standardized interface
106
+ const scheduleLocal=config=>{base.executeOperation(()=>{_NativeBridge.default.notification.scheduleLocal(config);},"schedule local notification");};const cancelLocal=notificationId=>{base.executeOperation(()=>{_NativeBridge.default.notification.cancelLocal(notificationId);},"cancel notification");};const registerForPush=()=>{base.executeOperation(()=>{_NativeBridge.default.notification.registerForPush();},"register for push");};const updateBadge=count=>{_NativeBridge.default.notification.updateBadge(count);setBadges(count);};const subscribeToTopic=topic=>{base.executeOperation(()=>{_NativeBridge.default.notification.subscribeToTopic(topic);},"subscribe to topic");};const unsubscribeFromTopic=topic=>{base.executeOperation(()=>{_NativeBridge.default.notification.unsubscribeFromTopic(topic);},"unsubscribe from topic");};const getSubscribedTopics=()=>{base.executeOperation(()=>{_NativeBridge.default.notification.getSubscribedTopics();},"get subscribed topics");};return{// Standardized interface
107
107
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,execute:scheduleLocal,clear:base.clear,clearError:base.clearError,// Notification-specific
108
108
  permissionStatus,pushToken,badges,lastNotification,subscribedTopics,scheduleLocal,cancelLocal,registerForPush,updateBadge,subscribeToTopic,unsubscribeFromTopic,getSubscribedTopics};};/**
109
109
  * Network status hook
110
110
  * Listens to native network changes and exposes online/offline state
111
- */exports.useNotification=useNotification;const useNetworkStatus=()=>{const initialOnline=typeof navigator!=="undefined"?navigator.onLine:true;const[status,setStatus]=(0,_react.useState)({online:initialOnline,type:null});const[error,setError]=(0,_react.useState)(null);const isNative=_NativeBridge.default.isAvailable();(0,_react.useEffect)(()=>{if(typeof window==="undefined")return;if(!window.WebBridge)return;if(!isNative)return;const handleStatus=payload=>{try{const parsed=parseNativePayload(payload)||{};setStatus({online:Boolean(parsed.online),type:parsed.type||null});setError(null);}catch(e){console.error("🌐 Error parsing network status:",e);setError(e.message);}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED,handleStatus);try{_NativeBridge.default.network.getStatus();}catch(e){setError(e.message||"Network status unavailable");}return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED);};},[isNative]);return{...status,error};};/**
111
+ */exports.useNotification=useNotification;const useNetworkStatus=()=>{const initialOnline=typeof navigator!=="undefined"?navigator.onLine:true;const[status,setStatus]=(0,_react.useState)({online:initialOnline,type:null});const[error,setError]=(0,_react.useState)(null);const isNative=_NativeBridge.default.isNativeEnvironment;(0,_react.useEffect)(()=>{if(typeof window==="undefined")return;if(!window.WebBridge)return;if(!isNative)return;const handleStatus=payload=>{try{const parsed=parseNativePayload(payload)||{};setStatus({online:Boolean(parsed.online),type:parsed.type||null});setError(null);}catch(e){console.error("🌐 Error parsing network status:",e);setError(e.message);}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED,handleStatus);try{_NativeBridge.default.network.getStatus();}catch(e){setError(e.message||"Network status unavailable");}return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED);};},[isNative]);return{...status,error};};/**
112
112
  * React hook for data protection primitives.
113
113
  *
114
114
  * Exposes screen-capture prevention and web data clearing as
@@ -130,8 +130,8 @@ permissionStatus,pushToken,badges,lastNotification,subscribedTopics,scheduleLoca
130
130
  * clearError: () => void,
131
131
  * }}
132
132
  */exports.useNetworkStatus=useNetworkStatus;const useDataProtection=()=>{const base=(0,_useBaseHook.useBaseHook)("useDataProtection");const[screenSecure,setScreenSecureState]=(0,_react.useState)(false);// Server-side rendering safety
133
- if(typeof window==="undefined"){return{screenSecure:false,setScreenSecure:()=>{},clearWebData:()=>{},data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,clear:()=>{},clearError:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{const handleScreenSecureSet=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setScreenSecureState(Boolean(result?.secure));base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}};const handleScreenSecureError=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.handleNativeError(result?.error||"setScreenSecure failed");}catch(error){base.handleNativeError(error);}};const handleWebDataCleared=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}};const handleWebDataClearError=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.handleNativeError(result?.error||"clearWebData failed");}catch(error){base.handleNativeError(error);}};const handleScreenSecureStatus=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setScreenSecureState(Boolean(result?.secure));}catch(error){base.handleNativeError(error);}};const cleanup=registerNativeHandlers([[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_SET,handleScreenSecureSet],[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_STATUS,handleScreenSecureStatus],[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_ERROR,handleScreenSecureError],[_NativeInterfaces.NATIVE_CALLBACKS.ON_WEB_DATA_CLEARED,handleWebDataCleared],[_NativeInterfaces.NATIVE_CALLBACKS.ON_WEB_DATA_CLEAR_ERROR,handleWebDataClearError]]);// Initialize screenSecure state from native on mount (native env only)
134
- base.callNative(()=>_NativeBridge.default.security.getScreenSecure());return cleanup;},[base.setDataAndComplete,base.handleNativeError]);const setScreenSecure=(0,_react.useCallback)(enable=>{base.callNative(()=>_NativeBridge.default.security.setScreenSecure(enable));},[base.callNative]);const clearWebData=(0,_react.useCallback)(()=>{base.callNative(()=>_NativeBridge.default.security.clearWebData());},[base.callNative]);return{// Standardized base interface
133
+ if(typeof window==="undefined"){return{screenSecure:false,setScreenSecure:()=>{},clearWebData:()=>{},data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,clear:()=>{},clearError:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{const handleScreenSecureSet=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setScreenSecureState(Boolean(result?.secure));base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}};const handleScreenSecureError=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.handleNativeError(result?.error||"setScreenSecure failed");}catch(error){base.handleNativeError(error);}};const handleWebDataCleared=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}base.setDataAndComplete(result);}catch(error){base.handleNativeError(error);}};const handleWebDataClearError=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.handleNativeError(result?.error||"clearWebData failed");}catch(error){base.handleNativeError(error);}};const handleScreenSecureStatus=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;if(result?.error){base.handleNativeError(result.error);return;}setScreenSecureState(Boolean(result?.secure));}catch(error){base.handleNativeError(error);}};const cleanup=registerNativeHandlers([[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_SET,handleScreenSecureSet],[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_STATUS,handleScreenSecureStatus],[_NativeInterfaces.NATIVE_CALLBACKS.ON_SCREEN_SECURE_ERROR,handleScreenSecureError],[_NativeInterfaces.NATIVE_CALLBACKS.ON_WEB_DATA_CLEARED,handleWebDataCleared],[_NativeInterfaces.NATIVE_CALLBACKS.ON_WEB_DATA_CLEAR_ERROR,handleWebDataClearError]]);// Initialize screenSecure state from native on mount
134
+ _NativeBridge.default.security.getScreenSecure();return cleanup;},[base.setDataAndComplete,base.handleNativeError]);const setScreenSecure=(0,_react.useCallback)(enable=>{base.executeOperation(()=>{_NativeBridge.default.security.setScreenSecure(enable);},"setScreenSecure");},[base.executeOperation]);const clearWebData=(0,_react.useCallback)(()=>{base.executeOperation(()=>{_NativeBridge.default.security.clearWebData();},"clearWebData");},[base.executeOperation]);return{// Standardized base interface
135
135
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,clear:base.clear,clearError:base.clearError,// Security state
136
136
  screenSecure,// Security actions
137
137
  setScreenSecure,clearWebData};};exports.useDataProtection=useDataProtection;const useSafeArea=()=>{const fromGlobal=(0,_safeArea.getSafeAreaFromGlobal)();const initialValue=fromGlobal||{..._safeArea.DEFAULT_INSETS};const[insets,setInsets]=(0,_react.useState)(()=>initialValue);(0,_react.useEffect)(()=>{if(typeof window==="undefined")return;if(!window.WebBridge)return;if(!_NativeBridge.default.isAvailable())return;const handleInsetsUpdate=data=>{try{const parsed=typeof data==="string"?JSON.parse(data):data;const normalized={top:Number(parsed.top)||0,right:Number(parsed.right)||0,bottom:Number(parsed.bottom)||0,left:Number(parsed.left)||0};setInsets(normalized);(0,_safeArea.setSafeAreaGlobal)(normalized);}catch(error){console.error("Error parsing safe area insets:",error);setInsets({..._safeArea.DEFAULT_INSETS});}};window.WebBridge.register("ON_SAFE_AREA_INSETS_UPDATED",handleInsetsUpdate);_NativeBridge.default.safeArea.get();return()=>{window.WebBridge.unregister("ON_SAFE_AREA_INSETS_UPDATED");};},[]);return insets;};exports.useSafeArea=useSafeArea;
@@ -17,9 +17,8 @@ bytesTotal:null// Total bytes
17
17
  const updateProgress=(0,_react.useCallback)(updates=>{setProgress(prev=>({...prev,...updates}));},[]);const resetProgress=(0,_react.useCallback)(()=>{setProgress({state:"idle",percentage:null,message:null,phase:null,transport:null,bytesLoaded:null,bytesTotal:null});},[]);const startProgress=(0,_react.useCallback)((phase=null,message=null)=>{updateProgress({state:"active",phase,message,percentage:null});},[updateProgress]);const completeProgress=(0,_react.useCallback)(()=>{updateProgress({state:"complete",percentage:100});},[updateProgress]);const errorProgress=(0,_react.useCallback)(()=>{updateProgress({state:"error"});},[updateProgress]);// Error handling utilities
18
18
  const handleNativeError=(0,_react.useCallback)(nativeError=>{const standardError=(0,_errors.translateError)(nativeError);setError(standardError);setLoading(false);errorProgress();// Development logging
19
19
  if((0,_errors.isDevelopment)()){console.group(`🚨 ${hookName} Error`);console.log("Standard Error:",standardError);console.log("Native Error:",nativeError);console.groupEnd();}return standardError;},[hookName,errorProgress]);const clearError=(0,_react.useCallback)(()=>{setError(null);if(progress.state==="error"){resetProgress();}},[progress.state,resetProgress]);// Data management utilities
20
- const setDataAndComplete=(0,_react.useCallback)(newData=>{setData(newData);setLoading(false);completeProgress();setError(null);if((0,_errors.isDevelopment)()){console.log(`✅ ${hookName} Success:`,newData);}},[hookName,completeProgress]);const clear=(0,_react.useCallback)(()=>{setData(null);setError(null);resetProgress();if((0,_errors.isDevelopment)()){console.log(`🗑️ ${hookName} Cleared`);}},[hookName,resetProgress]);// Fire-and-forget native call no-ops silently on web, routes errors through handleNativeError on native
21
- const callNative=(0,_react.useCallback)(fn=>{if(!isNative()){if((0,_errors.isDevelopment)()){console.warn(`${hookName} callNative skipped not in native environment`);}return;}try{fn();}catch(err){if((0,_errors.isDevelopment)()){console.warn(`${hookName} callNative failed silently:`,err);}}},[hookName,isNative]);// Operation wrapper that handles common patterns
22
- const executeOperation=(0,_react.useCallback)((operationCallback,operationName="operation")=>{try{if(isWeb()){console.warn(`${hookName} requires web fallback implementation (isWeb: true)`);return;}if(!isNative()){console.error(`${hookName} executeOperation: Native bridge not available`);return;}setLoading(true);setError(null);startProgress("starting",`Starting ${operationName}...`);if((0,_errors.isDevelopment)()){console.log(`🚀 ${hookName} ${operationName} started`);}// Execute the actual operation
20
+ const setDataAndComplete=(0,_react.useCallback)(newData=>{setData(newData);setLoading(false);completeProgress();setError(null);if((0,_errors.isDevelopment)()){console.log(`✅ ${hookName} Success:`,newData);}},[hookName,completeProgress]);const clear=(0,_react.useCallback)(()=>{setData(null);setError(null);resetProgress();if((0,_errors.isDevelopment)()){console.log(`🗑️ ${hookName} Cleared`);}},[hookName,resetProgress]);// Operation wrapper that handles common patterns
21
+ const executeOperation=(0,_react.useCallback)((operationCallback,operationName="operation")=>{try{if(isWeb()){console.warn(`${hookName} requires web fallback implementation (isWeb: true)`);return;}if(!isNative()){throw new Error("Native bridge not available");}setLoading(true);setError(null);startProgress("starting",`Starting ${operationName}...`);if((0,_errors.isDevelopment)()){console.log(`🚀 ${hookName} ${operationName} started`);}// Execute the actual operation
23
22
  operationCallback();}catch(err){handleNativeError(err);console.error(`❌ ${hookName} ${operationName} failed:`,err);}},[hookName,isWeb,startProgress,handleNativeError]);// Environment flags (computed values, not functions)
24
23
  const environmentFlags={isWeb:isWeb(),isNative:isNative()};// Return standardized interface
25
24
  return{// Data state
@@ -28,7 +27,7 @@ loading,progress,// Error handling
28
27
  error,// Environment detection
29
28
  ...environmentFlags,// Actions
30
29
  clear,clearError,// Internal utilities for specific hooks
31
- setData,setLoading,setError,setProgress,updateProgress,resetProgress,startProgress,completeProgress,errorProgress,setDataAndComplete,handleNativeError,callNative,executeOperation};};/**
30
+ setData,setLoading,setError,setProgress,updateProgress,resetProgress,startProgress,completeProgress,errorProgress,setDataAndComplete,handleNativeError,executeOperation};};/**
32
31
  * Hook for development debugging and testing
33
32
  * @param {string} hookName
34
33
  * @returns {Object} Environment information and utilities
@@ -10,10 +10,10 @@
10
10
  * Log environment detection info for debugging
11
11
  */_logEnvironmentInfo(){console.log("🌉 NativeBridge Environment Detection:",{isAndroid:this.isAndroid,isIOS:this.isIOS,isNativeEnvironment:this.isNativeEnvironment,hasAndroidBridge:!!window.NativeBridge,hasIOSBridge:!!window.webkit?.messageHandlers?.NativeBridge});}/**
12
12
  * Validate command before execution
13
- */_validateCommand(command){if(!command){throw new Error("Command is required");}if(!(0,_NativeInterfaces.isValidCommand)(command)){throw new Error(`Invalid command: ${command}. Available commands: ${Object.values(_NativeInterfaces.NATIVE_COMMANDS).join(", ")}`);}if(!this.isAvailable()){throw new Error("Native bridge not available. Ensure you are running in a native WebView environment.");}}/**
13
+ */_validateCommand(command){if(!command){throw new Error("Command is required");}if(!(0,_NativeInterfaces.isValidCommand)(command)){throw new Error(`Invalid command: ${command}. Available commands: ${Object.values(_NativeInterfaces.NATIVE_COMMANDS).join(", ")}`);}if(!this.isNativeEnvironment){throw new Error("Native bridge not available. Ensure you are running in a native WebView environment.");}}/**
14
14
  * Execute command on Android platform
15
15
  */_executeAndroidCommand(command,data){try{if(typeof window.NativeBridge[command]==="function"){console.log(`🌉 Calling Android method '${command}'`,data?{data}:"(no data)");// Always pass data parameter to match Kotlin method signatures
16
- window.NativeBridge[command](data);return true;}else{console.error(`Android bridge method '${command}' not found`);return false;}}catch(error){console.error(`Error executing Android command '${command}':`,error);return false;}}/**
16
+ window.NativeBridge[command](data);return true;}else{throw new Error(`Android bridge method '${command}' not found`);}}catch(error){console.error(`Error executing Android command '${command}':`,error);throw error;}}/**
17
17
  * Execute command on iOS platform
18
18
  */_executeIOSCommand(command,data){try{// For iOS, we always send a message object
19
19
  const message={command:command,data:data// This can be null/undefined, iOS bridge should handle it
@@ -102,8 +102,8 @@ if(process.env.NODE_ENV==="development"){console.warn(`🌉 Development mode: Na
102
102
  */clearWebData:()=>this.call(_NativeInterfaces.NATIVE_COMMANDS.CLEAR_WEB_DATA)};/**
103
103
  * Get environment info
104
104
  */getEnvironmentInfo(){return{isAndroid:this.isAndroid,isIOS:this.isIOS,isNativeEnvironment:this.isNativeEnvironment,platform:this.isAndroid?"android":this.isIOS?"ios":"web"};}/**
105
- * Check if native bridge is available — live check, never stale
106
- */isAvailable(){return this._detectAndroid()||this._detectIOS();}/**
105
+ * Check if native bridge is available
106
+ */isAvailable(){return this.isNativeEnvironment;}/**
107
107
  * Debug method to test native bridge connectivity
108
108
  */testConnection(){console.group("🌉 NativeBridge Connection Test");console.log("Environment:",this.getEnvironmentInfo());console.log("Available commands:",Object.values(_NativeInterfaces.NATIVE_COMMANDS));if(this.isAndroid&&window.NativeBridge){console.log("Android bridge methods:",Object.getOwnPropertyNames(window.NativeBridge));}if(this.isIOS&&window.webkit?.messageHandlers?.NativeBridge){console.log("iOS webkit bridge available");}console.groupEnd();}}// Export singleton instance
109
109
  exports.NativeBridgeUtil=NativeBridgeUtil;const nativeBridge=new NativeBridgeUtil();var _default=exports.default=nativeBridge;// Also export the class for advanced usage
@@ -265,20 +265,9 @@ public final class CacheManager {
265
265
  }
266
266
 
267
267
  func getCacheStatistics() -> (memoryUsed: Int, diskUsed: Int) {
268
+ // URLCache methods are thread-safe, no need for sync queue
268
269
  let memoryUsed = session.configuration.urlCache?.currentMemoryUsage ?? 0
269
-
270
- // Sum size of all custom .cache files written to disk (separate from URLCache internals)
271
- var diskUsed = 0
272
- if let files = try? FileManager.default.contentsOfDirectory(
273
- at: cacheDirectory,
274
- includingPropertiesForKeys: [.fileSizeKey]
275
- ) {
276
- diskUsed = files.reduce(0) { total, url in
277
- let size = (try? url.resourceValues(forKeys: [.fileSizeKey]))?.fileSize ?? 0
278
- return total + size
279
- }
280
- }
281
-
270
+ let diskUsed = session.configuration.urlCache?.currentDiskUsage ?? 0
282
271
  return (memoryUsed, diskUsed)
283
272
  }
284
273
  }
@@ -1,42 +1,6 @@
1
1
  {
2
2
  "originHash" : "3d4b03cf830e06eaa66e0727fd91253b9df9d2675cbe24eb099df0d4bb4d1f01",
3
3
  "pins" : [
4
- {
5
- "identity" : "appauth-ios",
6
- "kind" : "remoteSourceControl",
7
- "location" : "https://github.com/openid/AppAuth-iOS.git",
8
- "state" : {
9
- "revision" : "2781038865a80e2c425a1da12cc1327bcd56501f",
10
- "version" : "1.7.6"
11
- }
12
- },
13
- {
14
- "identity" : "googlesignin-ios",
15
- "kind" : "remoteSourceControl",
16
- "location" : "https://github.com/google/GoogleSignIn-iOS",
17
- "state" : {
18
- "revision" : "a7965d134c5d3567026c523e0a8a583f73b62b0d",
19
- "version" : "7.1.0"
20
- }
21
- },
22
- {
23
- "identity" : "gtm-session-fetcher",
24
- "kind" : "remoteSourceControl",
25
- "location" : "https://github.com/google/gtm-session-fetcher.git",
26
- "state" : {
27
- "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b",
28
- "version" : "3.5.0"
29
- }
30
- },
31
- {
32
- "identity" : "gtmappauth",
33
- "kind" : "remoteSourceControl",
34
- "location" : "https://github.com/google/GTMAppAuth.git",
35
- "state" : {
36
- "revision" : "5d7d66f647400952b1758b230e019b07c0b4b22a",
37
- "version" : "4.1.1"
38
- }
39
- },
40
4
  {
41
5
  "identity" : "jsonschema.swift",
42
6
  "kind" : "remoteSourceControl",
@@ -9,7 +9,6 @@
9
9
  }
10
10
  ],
11
11
  "defaultOptions" : {
12
- "codeCoverage" : true,
13
12
  "commandLineArgumentEntries" : [
14
13
  {
15
14
  "argument" : "-OSLogEnable"
@@ -47,16 +47,12 @@ final class FrameworkServerUtilsTests: XCTestCase {
47
47
  // CATEGORY 1: HTTPS Server Setup (2 tests)
48
48
  // ========================================
49
49
 
50
- func testHTTPSServerSetup_ServerInitialization() async {
50
+ func testHTTPSServerSetup_ServerInitialization() {
51
51
  // Test server initialization
52
52
 
53
53
  // Note: Server may fail to start in test environment due to network restrictions
54
- // This test verifies the API works without crashing.
55
- // Run startServer() off the main thread to avoid UI-responsiveness warnings
56
- // (NWListener setup must not block the main thread).
57
- let started = await Task.detached(priority: .userInitiated) {
58
- self.frameworkServer.startServer()
59
- }.value
54
+ // This test verifies the API works without crashing
55
+ let started = frameworkServer.startServer()
60
56
 
61
57
  // If server starts successfully, verify state
62
58
  if started {
@@ -147,14 +143,8 @@ final class FrameworkServerUtilsTests: XCTestCase {
147
143
  // Start server first
148
144
  let started = frameworkServer.startServer()
149
145
 
150
- // Wait briefly for NWListener stateUpdateHandler to fire (it's async on a background queue).
151
- // The listener can flip isServerRunning back to false if it fails after startServer() returns.
146
+ // Only test file serving if server started
152
147
  if started {
153
- Thread.sleep(forTimeInterval: 0.3)
154
- }
155
-
156
- // Only test file serving if server is still running after NWListener settled
157
- if started && frameworkServer.isRunning() {
158
148
  // Create a temporary test file
159
149
  let testData = "Test file content".data(using: .utf8)!
160
150
  let tempURL = FileManager.default.temporaryDirectory
@@ -38,7 +38,7 @@ final class WebViewTests: XCTestCase {
38
38
  viewModel = WebViewModel()
39
39
 
40
40
  // Create navigation delegate
41
- navigationDelegate = WebViewNavigationDelegate(viewModel: viewModel, initialURL: nil)
41
+ navigationDelegate = WebViewNavigationDelegate(viewModel: viewModel)
42
42
 
43
43
  // Create a basic WKWebView for testing
44
44
  let config = WKWebViewConfiguration()
@@ -124,7 +124,7 @@ final class WebViewTests: XCTestCase {
124
124
  // ========================================
125
125
 
126
126
  @MainActor
127
- func testNavigationHandling_AllowedURLLoading() async {
127
+ func testNavigationHandling_AllowedURLLoading() {
128
128
  // Test that allowed URLs are permitted for navigation
129
129
 
130
130
  let allowedURL = URL(string: "https://example.com")!
@@ -138,23 +138,29 @@ final class WebViewTests: XCTestCase {
138
138
  #endif
139
139
 
140
140
  let navigationAction = createNavigationAction(url: allowedURL)
141
- let expectation = XCTestExpectation(description: "Navigation decision received")
141
+ var decisionReceived = false
142
142
  var allowedDecision = false
143
143
 
144
144
  navigationDelegate.webView(mockWebView,
145
145
  decidePolicyFor: navigationAction) { policy in
146
+ decisionReceived = true
146
147
  allowedDecision = (policy == .allow)
147
- expectation.fulfill()
148
148
  }
149
149
 
150
- await fulfillment(of: [expectation], timeout: 2.0)
150
+ // Wait briefly for async decision
151
+ let expectation = XCTestExpectation(description: "Navigation decision")
152
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
153
+ expectation.fulfill()
154
+ }
155
+ wait(for: [expectation], timeout: 1.0)
151
156
 
157
+ XCTAssertTrue(decisionReceived, "Navigation decision should be received")
152
158
  XCTAssertTrue(allowedDecision || !URLWhitelistManager.shared.isAccessControlEnabled,
153
159
  "Allowed URL should be permitted")
154
160
  }
155
161
 
156
162
  @MainActor
157
- func testNavigationHandling_BlockedURLNavigation() async {
163
+ func testNavigationHandling_BlockedURLNavigation() {
158
164
  // Test that blocked URLs are rejected
159
165
 
160
166
  let blockedURL = URL(string: "https://blocked.com")!
@@ -168,17 +174,23 @@ final class WebViewTests: XCTestCase {
168
174
  #endif
169
175
 
170
176
  let navigationAction = createNavigationAction(url: blockedURL)
171
- let expectation = XCTestExpectation(description: "Navigation decision received")
177
+ var decisionReceived = false
172
178
  var blockedDecision = false
173
179
 
174
180
  navigationDelegate.webView(mockWebView,
175
181
  decidePolicyFor: navigationAction) { policy in
182
+ decisionReceived = true
176
183
  blockedDecision = (policy == .cancel)
177
- expectation.fulfill()
178
184
  }
179
185
 
180
- await fulfillment(of: [expectation], timeout: 2.0)
186
+ // Wait briefly for async decision
187
+ let expectation = XCTestExpectation(description: "Navigation decision")
188
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
189
+ expectation.fulfill()
190
+ }
191
+ wait(for: [expectation], timeout: 1.0)
181
192
 
193
+ XCTAssertTrue(decisionReceived, "Navigation decision should be received")
182
194
  XCTAssertTrue(blockedDecision, "Blocked URL should be cancelled")
183
195
  }
184
196