catalyst-core 0.1.0-beta.1 → 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/changelog.md +5 -0
  3. package/dist/native/androidProject/app/src/main/AndroidManifest.xml +2 -1
  4. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +3 -3
  5. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/FridaDetector.kt +16 -5
  6. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +7 -3
  7. package/dist/native/androidSetup.js +1 -1
  8. package/dist/native/bridge/useBaseHook.js +4 -4
  9. package/dist/native/bridge/utils/NativeBridge.js +5 -5
  10. package/dist/native/buildAppAndroid.js +5 -7
  11. package/dist/native/buildAppIos.js +6 -7
  12. package/dist/native/renameAndroidProject.js +3 -3
  13. package/dist/native/setupServer.js +2 -3
  14. package/dist/native/utils.js +4 -1
  15. package/dist/scripts/devServe.js +1 -1
  16. package/dist/scripts/registerAliases.js +6 -2
  17. package/dist/scripts/scriptUtils.js +2 -1
  18. package/dist/scripts/serve.js +2 -1
  19. package/dist/scripts/start.js +4 -1
  20. package/dist/server/renderer/extract.js +2 -2
  21. package/dist/server/utils/validator.js +1 -1
  22. package/dist/webpack/base.babel.js +2 -1
  23. package/dist/webpack/development.client.babel.js +1 -1
  24. package/mcp_v2/lib/helpers.js +30 -12
  25. package/mcp_v2/mcp.js +4 -3
  26. package/mcp_v2/setup.js +5 -3
  27. package/mcp_v2/tools/build.js +1 -1
  28. package/mcp_v2/tools/config.js +16 -4
  29. package/mcp_v2/tools/conversion.js +11 -11
  30. package/mcp_v2/tools/knowledge.js +16 -3
  31. package/mcp_v2/tools/tasks.js +13 -4
  32. package/package.json +7 -14
  33. package/.release-meta.json +0 -4
  34. package/license +0 -10
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  Catalyst offers a comprehensive suite of features designed for modern web development. It includes isomorphic rendering for optimal performance, an extendable server with full-stack capabilities, and configurable state management. The framework employs smart prefetching of data and chunks, allows easy configuration of global styles and layouts, and provides SEO optimization at both global and page levels.
16
16
 
17
- This version adds support for Universal app for both Android and iOS. Please checkout [Universal App Documentaion](https://catalyst.1mg.com/public_docs/content/Universal%20App/RunUniversalApp)
17
+ Catalyst also supports universal apps for Android and iOS. See the [Universal App guide](https://catalyst.1mg.com/public_docs/content/Guides%20and%20Tutorials/First%20Universal%20App/first-universal-app) to get started.
18
18
 
19
19
  ## Installation
20
20
 
package/changelog.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.0-beta.2] - 2026-05-06
4
+
5
+ - Moved Catalyst into a monorepo structure with `catalyst-core`, `create-catalyst-app`, the Catalyst docs app, and the Catalyst core test app managed from one repository.
6
+ - Replaced the internal-package pre-release flow with a local release sandbox that scaffolds a real app using current-branch CCA and a locally packed current-branch `catalyst-core`.
7
+
3
8
  ## [0.1.0-beta.1] - 2026-04-15
4
9
 
5
10
  - Promoted `0.1.0-canary.7` to the first proper beta release after stabilization.
@@ -24,6 +24,7 @@
24
24
  android:usesCleartextTraffic="true"
25
25
  android:networkSecurityConfig="@xml/network_security_config"
26
26
  tools:targetApi="31">
27
+ <!-- nosemgrep: java.android.security.exported_activity.exported_activity - Launcher activity must be exported so Android can start the app from the launcher. -->
27
28
  <activity
28
29
  android:name="io.yourname.androidproject.SplashActivity"
29
30
  android:exported="true"
@@ -51,4 +52,4 @@
51
52
  </provider>
52
53
  </application>
53
54
 
54
- </manifest>
55
+ </manifest>
@@ -404,12 +404,12 @@ class WebCacheManager(private val context: Context, private val properties: java
404
404
 
405
405
  private fun generateCacheKey(url: String): String {
406
406
  try {
407
- val md = MessageDigest.getInstance("MD5")
407
+ val md = MessageDigest.getInstance("SHA-256")
408
408
  val digest = md.digest(url.toByteArray())
409
409
  return digest.joinToString("") { "%02x".format(it) }
410
410
  } catch (e: Exception) {
411
411
  Log.e(TAG, "Error generating cache key: ${e.message}")
412
- // Fallback to a simple hash code if MD5 fails
412
+ // Fallback to a simple hash code if SHA-256 fails
413
413
  return url.hashCode().toString()
414
414
  }
415
415
  }
@@ -459,4 +459,4 @@ class WebCacheManager(private val context: Context, private val properties: java
459
459
  )
460
460
  }
461
461
  }
462
- }
462
+ }
@@ -5,7 +5,8 @@ import io.yourname.androidproject.utils.BridgeUtils
5
5
  import java.io.BufferedReader
6
6
  import java.io.File
7
7
  import java.io.InputStreamReader
8
- import java.net.Socket
8
+ import java.net.InetSocketAddress
9
+ import java.nio.channels.SocketChannel
9
10
 
10
11
  /**
11
12
  * Local Frida and hooking framework detection.
@@ -68,10 +69,20 @@ object FridaDetector {
68
69
  private fun checkFridaPorts(): Boolean {
69
70
  return FRIDA_PORTS.any { port ->
70
71
  try {
71
- Socket().use { socket ->
72
- val address = java.net.InetSocketAddress("127.0.0.1", port)
73
- socket.connect(address, 2000) // 2 second timeout
74
- socket.isConnected
72
+ SocketChannel.open().use { channel ->
73
+ val address = InetSocketAddress("127.0.0.1", port)
74
+ val deadline = System.currentTimeMillis() + 2000
75
+ channel.configureBlocking(false)
76
+ channel.connect(address)
77
+
78
+ while (!channel.finishConnect()) {
79
+ if (System.currentTimeMillis() >= deadline) {
80
+ return@use false
81
+ }
82
+ Thread.sleep(25)
83
+ }
84
+
85
+ true
75
86
  }
76
87
  } catch (e: Exception) {
77
88
  // Connection failed or timed out - no Frida on this port
@@ -12,7 +12,8 @@ import io.ktor.server.routing.*
12
12
  import kotlinx.coroutines.*
13
13
  import java.io.File
14
14
  import java.io.IOException
15
- import java.net.ServerSocket
15
+ import java.net.InetSocketAddress
16
+ import java.nio.channels.ServerSocketChannel
16
17
  import java.security.SecureRandom
17
18
  import java.util.*
18
19
  import java.util.concurrent.ConcurrentHashMap
@@ -279,7 +280,10 @@ object FrameworkServerUtils {
279
280
 
280
281
  private fun isPortAvailable(port: Int): Boolean {
281
282
  return try {
282
- ServerSocket(port).use { true }
283
+ ServerSocketChannel.open().use { channel ->
284
+ channel.bind(InetSocketAddress("127.0.0.1", port))
285
+ true
286
+ }
283
287
  } catch (e: IOException) {
284
288
  false
285
289
  }
@@ -408,4 +412,4 @@ object FrameworkServerUtils {
408
412
  }
409
413
  }
410
414
  }
411
- }
415
+ }
@@ -3,6 +3,6 @@ try{await(0,_utils.runCommand)("brew install --cask zulu@17");}catch(error){if(e
3
3
  if(!_fs.default.existsSync(javaHome)){throw new Error(`Java installation not found at ${javaHome}. Please verify the installation manually.`);}// Set up environment variables
4
4
  const homeDir=process.env.HOME;const shellProfile=`${homeDir}/.zshrc`;const exportCommand=`\n# Java Configuration\nexport JAVA_HOME=${javaHome}\nexport PATH=$JAVA_HOME/bin:$PATH\n`;try{_fs.default.appendFileSync(shellProfile,exportCommand);}catch(error){throw new Error(`Failed to update ${shellProfile}: ${error.message}. Please add the following lines manually:\n${exportCommand}`);}// Set for current process
5
5
  process.env.JAVA_HOME=javaHome;process.env.PATH=`${javaHome}/bin:${process.env.PATH}`;// Verify Java configuration
6
- try{const javaVersion=(0,_utils.runCommand)("java -version");progress.log("Java installed and configured successfully","success");progress.log(`Java Version: ${javaVersion}`,"info");progress.log(`IMPORTANT: Please run the following commands to complete setup:`,"warning");progress.log(`1. source ${shellProfile}`,"info");progress.log(`2. echo $JAVA_HOME`,"info");return true;}catch(error){throw new Error(`Java installation verified but environment not configured. Please run:\n1. source ${shellProfile}\n2. echo $JAVA_HOME`);}}catch(error){progress.log(error.message,"error");progress.log("Manual Setup Instructions:","info");progress.log("1. Run: brew reinstall --cask zulu@17","info");progress.log("2. Add to ~/.zshrc:","info");progress.log(" export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home","info");progress.log(" export PATH=$JAVA_HOME/bin:$PATH","info");progress.log("3. Run: source ~/.zshrc","info");throw error;}}async function validateJavaEnvironment(){progress.start("java");const hasJava=await checkJavaInstallation();if(!hasJava){progress.log("Java installation required. Starting installation process...");const javaInstalled=await installJava();if(!javaInstalled){progress.fail("java","Failed to set up Java environment");throw new Error("Failed to set up Java environment");}}progress.complete("java");}async function initializeConfig(){const configFile=_fs.default.readFileSync(configPath,"utf8");const config=JSON.parse(configFile);const{WEBVIEW_CONFIG}=config;if(!WEBVIEW_CONFIG||Object.keys(WEBVIEW_CONFIG).length===0){progress.log("WebView Config missing in "+configPath,"error");process.exit(1);}if(!WEBVIEW_CONFIG.android){WEBVIEW_CONFIG.android={};}return{WEBVIEW_CONFIG};}async function saveConfig(newConfig){try{const existingConfigFile=_fs.default.readFileSync(configPath,"utf8");const existingConfig=JSON.parse(existingConfigFile);const updatedConfig={...existingConfig,WEBVIEW_CONFIG:{...existingConfig.WEBVIEW_CONFIG,android:newConfig.WEBVIEW_CONFIG.android}};_fs.default.writeFileSync(configPath,JSON.stringify(updatedConfig,null,2));progress.log("Configuration saved successfully","success");}catch(error){progress.log("Failed to save configuration: "+error.message,"error");process.exit(1);}}function validateAndroidTools(androidConfig){const ANDROID_SDK=androidConfig.sdkPath;const EMULATOR_PATH=`${ANDROID_SDK}/emulator/emulator`;const ADB_PATH=`${ANDROID_SDK}/platform-tools/adb`;if(!ANDROID_SDK){throw new Error("Android SDK path is not configured");}if(!_fs.default.existsSync(ANDROID_SDK)){throw new Error(`Android SDK path does not exist: ${ANDROID_SDK}`);}if(!_fs.default.existsSync(ADB_PATH)){throw new Error(`ADB not found at: ${ADB_PATH}`);}try{(0,_utils.runCommand)(`${ADB_PATH} version`);progress.log("ADB is valid","success");}catch(error){throw new Error(`ADB is not working properly: ${error.message}`);}if(!_fs.default.existsSync(EMULATOR_PATH)){throw new Error(`Emulator not found at: ${EMULATOR_PATH}`);}try{(0,_utils.runCommand)(`${EMULATOR_PATH} -version`);progress.log("Emulator is valid","success");}catch(error){throw new Error(`Emulator is not working properly: ${error.message}`);}try{const avdOutput=(0,_utils.runCommand)(`${EMULATOR_PATH} -list-avds`);if(!avdOutput.includes(androidConfig.emulatorName)){throw new Error(`Specified emulator "${androidConfig.emulatorName}" not found in available AVDs`);}progress.log(`Emulator "${androidConfig.emulatorName}" exists`,"success");}catch(error){throw new Error(`Error checking emulator AVD: ${error.message}`);}progress.log("Android tools validation completed successfully!","success");return{EMULATOR_PATH,ADB_PATH};}async function checkEmulator(ADB_PATH){try{const devices=(0,_utils.runCommand)(`${ADB_PATH} devices`);return devices.includes("emulator");}catch(error){progress.log(`Error checking emulator: ${error}`,"error");return false;}}async function startEmulator(EMULATOR_PATH,androidConfig){progress.log(`Starting emulator: ${androidConfig.emulatorName}...`);// eslint-disable-next-line security/detect-child-process
6
+ try{const javaVersion=(0,_utils.runCommand)("java -version");progress.log("Java installed and configured successfully","success");progress.log(`Java Version: ${javaVersion}`,"info");progress.log(`IMPORTANT: Please run the following commands to complete setup:`,"warning");progress.log(`1. source ${shellProfile}`,"info");progress.log(`2. echo $JAVA_HOME`,"info");return true;}catch(error){throw new Error(`Java installation verified but environment not configured. Please run:\n1. source ${shellProfile}\n2. echo $JAVA_HOME`);}}catch(error){progress.log(error.message,"error");progress.log("Manual Setup Instructions:","info");progress.log("1. Run: brew reinstall --cask zulu@17","info");progress.log("2. Add to ~/.zshrc:","info");progress.log(" export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home","info");progress.log(" export PATH=$JAVA_HOME/bin:$PATH","info");progress.log("3. Run: source ~/.zshrc","info");throw error;}}async function validateJavaEnvironment(){progress.start("java");const hasJava=await checkJavaInstallation();if(!hasJava){progress.log("Java installation required. Starting installation process...");const javaInstalled=await installJava();if(!javaInstalled){progress.fail("java","Failed to set up Java environment");throw new Error("Failed to set up Java environment");}}progress.complete("java");}async function initializeConfig(){const configFile=_fs.default.readFileSync(configPath,"utf8");const config=JSON.parse(configFile);const{WEBVIEW_CONFIG}=config;if(!WEBVIEW_CONFIG||Object.keys(WEBVIEW_CONFIG).length===0){progress.log("WebView Config missing in "+configPath,"error");process.exit(1);}if(!WEBVIEW_CONFIG.android){WEBVIEW_CONFIG.android={};}return{WEBVIEW_CONFIG};}async function saveConfig(newConfig){try{const existingConfigFile=_fs.default.readFileSync(configPath,"utf8");const existingConfig=JSON.parse(existingConfigFile);const updatedConfig={...existingConfig,WEBVIEW_CONFIG:{...existingConfig.WEBVIEW_CONFIG,android:newConfig.WEBVIEW_CONFIG.android}};_fs.default.writeFileSync(configPath,JSON.stringify(updatedConfig,null,2));progress.log("Configuration saved successfully","success");}catch(error){progress.log("Failed to save configuration: "+error.message,"error");process.exit(1);}}function validateAndroidTools(androidConfig){const ANDROID_SDK=androidConfig.sdkPath;const EMULATOR_PATH=`${ANDROID_SDK}/emulator/emulator`;const ADB_PATH=`${ANDROID_SDK}/platform-tools/adb`;if(!ANDROID_SDK){throw new Error("Android SDK path is not configured");}if(!_fs.default.existsSync(ANDROID_SDK)){throw new Error(`Android SDK path does not exist: ${ANDROID_SDK}`);}if(!_fs.default.existsSync(ADB_PATH)){throw new Error(`ADB not found at: ${ADB_PATH}`);}try{(0,_utils.runCommand)(`${ADB_PATH} version`);progress.log("ADB is valid","success");}catch(error){throw new Error(`ADB is not working properly: ${error.message}`);}if(!_fs.default.existsSync(EMULATOR_PATH)){throw new Error(`Emulator not found at: ${EMULATOR_PATH}`);}try{(0,_utils.runCommand)(`${EMULATOR_PATH} -version`);progress.log("Emulator is valid","success");}catch(error){throw new Error(`Emulator is not working properly: ${error.message}`);}try{const avdOutput=(0,_utils.runCommand)(`${EMULATOR_PATH} -list-avds`);if(!avdOutput.includes(androidConfig.emulatorName)){throw new Error(`Specified emulator "${androidConfig.emulatorName}" not found in available AVDs`);}progress.log(`Emulator "${androidConfig.emulatorName}" exists`,"success");}catch(error){throw new Error(`Error checking emulator AVD: ${error.message}`);}progress.log("Android tools validation completed successfully!","success");return{EMULATOR_PATH,ADB_PATH};}async function checkEmulator(ADB_PATH){try{const devices=(0,_utils.runCommand)(`${ADB_PATH} devices`);return devices.includes("emulator");}catch(error){progress.log(`Error checking emulator: ${error}`,"error");return false;}}async function startEmulator(EMULATOR_PATH,androidConfig){progress.log(`Starting emulator: ${androidConfig.emulatorName}...`);// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process - EMULATOR_PATH is validated and emulatorName comes from local Android config.
7
7
  (0,_child_process.exec)(`${EMULATOR_PATH} -avd ${androidConfig.emulatorName} -read-only > /dev/null &`,error=>{if(error){progress.log(`Error starting emulator: ${error}`,"error");}});}async function updateLocalProperties(sdkPath){progress.start("properties");try{(0,_utils.runCommand)(`cd ${pwd}/androidProject && ./gradlew updateSdkPath -PsdkPath="${sdkPath}"`);progress.log("Updated local.properties successfully","success");progress.complete("properties");}catch(error){progress.fail("properties",`Failed to update local.properties: ${error.message}`);throw error;}}async function setupAndroidEnvironment(){try{await validateJavaEnvironment();progress.start("config");await initializeConfig();const config=await(0,_utils.validateAndCompleteConfig)("android",configPath);progress.complete("config");progress.start("androidTools");const{EMULATOR_PATH,ADB_PATH}=validateAndroidTools(config.android);progress.complete("androidTools");progress.start("emulator");const emulatorRunning=await checkEmulator(ADB_PATH);if(!emulatorRunning){progress.log("No emulator running, attempting to start one...");await startEmulator(EMULATOR_PATH,config.android);}else{progress.log("Emulator already running, proceeding with installation...","success");}progress.complete("emulator");if(config.android?.sdkPath){await updateLocalProperties(config.android.sdkPath);}progress.start("saveConfig");await saveConfig({WEBVIEW_CONFIG:config});progress.complete("saveConfig");progress.start("setupServer");await(0,_setupServer.setupServer)(configPath);progress.complete("setupServer");progress.printTreeContent("Configuration Explanation",["WEBVIEW_CONFIG: Main configuration object for the WebView setup",{text:"port: Port number for the WebView server",indent:1,prefix:"├─ ",color:"gray"},{text:"android: Android-specific configuration",indent:1,prefix:"└─ ",color:"gray"},{text:"buildType: Build type (debug/release)",indent:2,prefix:"├─ ",color:"gray"},{text:"sdkPath: Android SDK path",indent:2,prefix:"├─ ",color:"gray"},{text:"emulatorName: Selected Android emulator name",indent:2,prefix:"└─ ",color:"gray"}]);progress.printTreeContent("Final Configuration",[JSON.stringify(config,null,2)]);process.exit(0);}catch(error){if(progress.currentStep){progress.fail(progress.currentStep.id,error.message);}process.exit(1);}}// Execute the main setup
8
8
  setupAndroidEnvironment();
@@ -17,10 +17,10 @@ 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
20
+ const setDataAndComplete=(0,_react.useCallback)(newData=>{setData(newData);setLoading(false);completeProgress();setError(null);if((0,_errors.isDevelopment)()){console.log("✅ Native hook success:",hookName,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("Native hook call failed silently:",hookName,err);}}},[hookName,isNative]);// Operation wrapper that handles common patterns
22
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
23
- operationCallback();}catch(err){handleNativeError(err);console.error(`❌ ${hookName} ${operationName} failed:`,err);}},[hookName,isWeb,isNative,startProgress,handleNativeError]);// Environment flags (computed values, not functions)
23
+ operationCallback();}catch(err){handleNativeError(err);console.error("❌ Native hook operation failed:",hookName,operationName,err);}},[hookName,isWeb,isNative,startProgress,handleNativeError]);// Environment flags (computed values, not functions)
24
24
  const environmentFlags={isWeb:isWeb(),isNative:isNative()};// Return standardized interface
25
25
  return{// Data state
26
26
  data,// Loading states
@@ -32,7 +32,7 @@ setData,setLoading,setError,setProgress,updateProgress,resetProgress,startProgre
32
32
  * Hook for development debugging and testing
33
33
  * @param {string} hookName
34
34
  * @returns {Object} Environment information and utilities
35
- */exports.useBaseHook=useBaseHook;const useEnvironmentInfo=hookName=>{const isNativeAvailable=()=>{if(typeof window==="undefined")return false;return!!(window.WebBridge&&_NativeBridge.default.isAvailable());};const getEnvironmentDetails=()=>{const details={hasWindow:typeof window!=="undefined",hasWebBridge:typeof window!=="undefined"&&!!window.WebBridge,nativeBridgeAvailable:false,userAgent:typeof navigator!=="undefined"?navigator.userAgent:"SSR",timestamp:new Date().toISOString()};if(details.hasWebBridge){details.nativeBridgeAvailable=_NativeBridge.default.isAvailable();}return details;};if((0,_errors.isDevelopment)()){console.log(`🔍 ${hookName} Environment:`,getEnvironmentDetails());}return{isNative:isNativeAvailable(),isWeb:!isNativeAvailable(),environmentDetails:getEnvironmentDetails()};};/**
35
+ */exports.useBaseHook=useBaseHook;const useEnvironmentInfo=hookName=>{const isNativeAvailable=()=>{if(typeof window==="undefined")return false;return!!(window.WebBridge&&_NativeBridge.default.isAvailable());};const getEnvironmentDetails=()=>{const details={hasWindow:typeof window!=="undefined",hasWebBridge:typeof window!=="undefined"&&!!window.WebBridge,nativeBridgeAvailable:false,userAgent:typeof navigator!=="undefined"?navigator.userAgent:"SSR",timestamp:new Date().toISOString()};if(details.hasWebBridge){details.nativeBridgeAvailable=_NativeBridge.default.isAvailable();}return details;};if((0,_errors.isDevelopment)()){console.log("🔍 Native hook environment:",hookName,getEnvironmentDetails());}return{isNative:isNativeAvailable(),isWeb:!isNativeAvailable(),environmentDetails:getEnvironmentDetails()};};/**
36
36
  * Progress state constants for consistency across hooks
37
37
  */exports.useEnvironmentInfo=useEnvironmentInfo;const PROGRESS_STATES=exports.PROGRESS_STATES={// Common states
38
38
  IDLE:"idle",STARTING:"starting",COMPLETE:"complete",ERROR:"error",// File Picker states
@@ -12,19 +12,19 @@
12
12
  * Validate command before execution
13
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.");}}/**
14
14
  * Execute command on Android platform
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;}}/**
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;}}/**
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
20
- };console.log(`🌉 Calling iOS method '${command}'`,data?{data}:"(no data)");window.webkit.messageHandlers.NativeBridge.postMessage(message);return true;}catch(error){console.error(`Error executing iOS command '${command}':`,error);throw error;}}/**
20
+ };console.log("🌉 Calling iOS method",command,data?{data}:"(no data)");window.webkit.messageHandlers.NativeBridge.postMessage(message);return true;}catch(error){console.error("Error executing iOS command:",command,error);throw error;}}/**
21
21
  * Main method to call native commands
22
22
  * @param {string} command - The command to execute (from NATIVE_COMMANDS)
23
23
  * @param {any} data - Optional data to pass to the native side
24
24
  * @returns {boolean} - Success status
25
25
  */call(command,data=null){try{// Validate environment and command
26
- this._validateCommand(command);console.log(`🌉 Executing native command: ${command}`,data?{data}:"");// Execute on appropriate platform
27
- if(this.isAndroid){return this._executeAndroidCommand(command,data);}else if(this.isIOS){return this._executeIOSCommand(command,data);}return false;}catch(error){console.error(`🌉 Failed to execute command '${command}':`,error);// In development, we might want to show user-friendly errors
26
+ this._validateCommand(command);console.log("🌉 Executing native command:",command,data?{data}:"");// Execute on appropriate platform
27
+ if(this.isAndroid){return this._executeAndroidCommand(command,data);}else if(this.isIOS){return this._executeIOSCommand(command,data);}return false;}catch(error){console.error("🌉 Failed to execute command:",command,error);// In development, we might want to show user-friendly errors
28
28
  if(process.env.NODE_ENV==="development"){console.warn(`🌉 Development mode: Native command '${command}' failed. This is expected when running in browser.`);}throw error;}}/**
29
29
  * Camera-specific methods for easier usage
30
30
  */camera={/**
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-extra-semi */"use strict";var _renameAndroidProject=require("./renameAndroidProject.js");var _child_process=require("child_process");var _fs=_interopRequireDefault(require("fs"));var _path=_interopRequireDefault(require("path"));var _utils=require("./utils.js");var _TerminalProgress=_interopRequireDefault(require("./TerminalProgress.js"));// Import the AAB builder
2
- function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const configPath=`${process.env.PWD}/config/config.json`;const publicPath=`${process.env.PWD}/public`;const catalystCorePath=_path.default.dirname(require.resolve("catalyst-core/package.json"));const pwd=_path.default.join(catalystCorePath,"dist/native");const ANDROID_PACKAGE="io.yourname.androidproject";// Default values for AAB building
2
+ function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const configPath=`${process.cwd()}/config/config.json`;const publicPath=`${process.cwd()}/public`;const catalystCorePath=_path.default.dirname(require.resolve("catalyst-core/package.json"));const pwd=_path.default.join(catalystCorePath,"dist/native");const ANDROID_PACKAGE="io.yourname.androidproject";// Default values for AAB building
3
3
  const DEFAULT_PROJECT_PATH=`${pwd}/androidProject`;const DEFAULT_DEPLOYMENT_PATH="./deployment";const DEFAULT_OLD_PROJECT_NAME="androidProject";// Use actual project name in catalyst
4
4
  const DEFAULT_OVERWRITE_EXISTING=true;const steps={config:"Initialize Configuration",tools:"Validate Android Tools",emulator:"Check and Start Emulator",copyAssets:"Copy Build Assets",build:"Build and Install Application",aab:"Build Signed AAB"};const progressConfig={titlePaddingTop:2,titlePaddingBottom:1,stepPaddingLeft:4,stepSpacing:1,errorPaddingLeft:6,bottomMargin:2};const progress=new _TerminalProgress.default(steps,"Catalyst Android Build",progressConfig);async function initializeConfig(){const configFile=_fs.default.readFileSync(configPath,"utf8");const config=JSON.parse(configFile);const{WEBVIEW_CONFIG,BUILD_OUTPUT_PATH}=config;if(!WEBVIEW_CONFIG||Object.keys(WEBVIEW_CONFIG).length===0){throw new Error("WebView Config missing in "+configPath);}if(!WEBVIEW_CONFIG.android){throw new Error("Android config missing in WebView Config");}// Log build type information
5
5
  const buildType=WEBVIEW_CONFIG.android.buildType||"debug";progress.log(`Build Type: ${buildType}`,"info");if(buildType==="release"){progress.log("Release build detected - AAB will be generated","info");}return{WEBVIEW_CONFIG,BUILD_OUTPUT_PATH};}function validateAndroidTools(androidConfig){const ANDROID_SDK=androidConfig.sdkPath;const ADB_PATH=`${ANDROID_SDK}/platform-tools/adb`;const EMULATOR_PATH=`${ANDROID_SDK}/emulator/emulator`;progress.log("Validating Android tools...","info");if(!ANDROID_SDK){throw new Error("Android SDK path is not configured");}if(!_fs.default.existsSync(ANDROID_SDK)){throw new Error(`Android SDK path does not exist: ${ANDROID_SDK}`);}if(!_fs.default.existsSync(ADB_PATH)){throw new Error(`ADB not found at: ${ADB_PATH}`);}try{;(0,_utils.runCommand)(`${ADB_PATH} version`);progress.log("ADB validation successful","success");}catch(error){throw new Error(`ADB is not working properly: ${error.message}`);}// Skip emulator validation for release builds
@@ -13,7 +13,7 @@ continue;}}}}progress.log("No physical devices detected","info");return null;}ca
13
13
  const connectionTest=(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell echo "test"`);if(!connectionTest.includes("test")){progress.log("Device connection test failed","error");return false;}// Test 2: Check if developer options are enabled
14
14
  try{const developerOptions=(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell settings get global development_settings_enabled`);if(developerOptions.trim()!=="1"){progress.log("Developer options not enabled on device","warning");return false;}}catch(error){progress.log("Cannot verify developer options status","warning");}// Test 3: Check USB debugging status
15
15
  try{const usbDebugging=(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell settings get global adb_enabled`);if(usbDebugging.trim()!=="1"){progress.log("USB debugging not enabled on device","warning");return false;}}catch(error){progress.log("Cannot verify USB debugging status","warning");}// Test 4: Check if we can access package manager
16
- try{;(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell pm list packages -3 | head -1`);}catch(error){progress.log("Cannot access package manager on device","error");return false;}progress.log(`Device ${deviceId} is ready for installation`,"success");return true;}catch(error){progress.log(`Installation capability test failed: ${error.message}`,"error");return false;}}async function checkEmulator(ADB_PATH){try{const devices=(0,_utils.runCommand)(`${ADB_PATH} devices`);return devices.includes("emulator");}catch(error){progress.log("Error checking emulator status: "+error.message,"error");return false;}}async function handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig){progress.log("Setting up emulator...","info");const emulatorRunning=await checkEmulator(ADB_PATH);if(!emulatorRunning){progress.log("No emulator running, attempting to start one...","info");await startEmulator(EMULATOR_PATH,androidConfig);await new Promise(resolve=>setTimeout(resolve,5000));}else{progress.log("Emulator already running","success");}return{type:"emulator",name:androidConfig.emulatorName};}async function startEmulator(EMULATOR_PATH,androidConfig){progress.log(`Starting emulator: ${androidConfig.emulatorName}...`,"info");return new Promise((resolve,reject)=>{;(0,_child_process.exec)(`${EMULATOR_PATH} -avd ${androidConfig.emulatorName} -read-only > /dev/null &`,error=>{if(error){progress.log("Error starting emulator: "+error.message,"error");reject(error);}else{progress.log("Emulator started successfully","success");resolve();}});});}async function copyBuildAssets(androidConfig,buildOptimisation=false){if(!buildOptimisation)return;progress.log("Copying build assets to Android project...","info");try{// Define source and destination paths
16
+ try{;(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell pm list packages -3 | head -1`);}catch(error){progress.log("Cannot access package manager on device","error");return false;}progress.log(`Device ${deviceId} is ready for installation`,"success");return true;}catch(error){progress.log(`Installation capability test failed: ${error.message}`,"error");return false;}}async function checkEmulator(ADB_PATH){try{const devices=(0,_utils.runCommand)(`${ADB_PATH} devices`);return devices.includes("emulator");}catch(error){progress.log("Error checking emulator status: "+error.message,"error");return false;}}async function handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig){progress.log("Setting up emulator...","info");const emulatorRunning=await checkEmulator(ADB_PATH);if(!emulatorRunning){progress.log("No emulator running, attempting to start one...","info");await startEmulator(EMULATOR_PATH,androidConfig);await new Promise(resolve=>setTimeout(resolve,5000));}else{progress.log("Emulator already running","success");}return{type:"emulator",name:androidConfig.emulatorName};}async function startEmulator(EMULATOR_PATH,androidConfig){progress.log(`Starting emulator: ${androidConfig.emulatorName}...`,"info");return(0,_utils.runInteractiveCommand)(EMULATOR_PATH,["-avd",androidConfig.emulatorName,"-read-only"],{}).then(()=>{progress.log("Emulator started successfully","success");}).catch(error=>{progress.log("Error starting emulator: "+error.message,"error");throw error;});}async function copyBuildAssets(androidConfig,buildOptimisation=false){if(!buildOptimisation)return;progress.log("Copying build assets to Android project...","info");try{// Define source and destination paths
17
17
  const sourcePath=`${process.env.PWD}/build/`;const destPath=`${pwd}/androidProject/app/src/main/assets/build/`// Create destination directory if it doesn't exist
18
18
  ;(0,_utils.runCommand)(`mkdir -p ${destPath}`)// Clear existing destination to avoid conflicts
19
19
  ;(0,_utils.runCommand)(`rm -rf ${destPath}/*`);// Files to exclude from copying
@@ -86,13 +86,11 @@ const iconXml=`<vector xmlns:android="http://schemas.android.com/apk/res/android
86
86
  <path
87
87
  android:fillColor="@android:color/white"
88
88
  android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
89
- </vector>`;_fs.default.writeFileSync(`${destPath}/drawable/${iconName}.xml`,iconXml);}// Cleanup functions to remove notification configurations
90
- async function cleanupNotificationPermissions(){try{const manifestPath=`${pwd}/androidProject/app/src/main/AndroidManifest.xml`;let manifestContent=_fs.default.readFileSync(manifestPath,"utf8");// Remove notification permissions
91
- const notificationPermissions=["android.permission.POST_NOTIFICATIONS","android.permission.VIBRATE","android.permission.WAKE_LOCK","android.permission.RECEIVE_BOOT_COMPLETED"];notificationPermissions.forEach(permission=>{const permissionRegex=new RegExp(`\\s*<uses-permission android:name="${permission}"[^>]*/>`,"g");manifestContent=manifestContent.replace(permissionRegex,"");});_fs.default.writeFileSync(manifestPath,manifestContent);}catch(error){// Don't throw error for cleanup operations, just log
89
+ </vector>`;_fs.default.writeFileSync(`${destPath}/drawable/${iconName}.xml`,iconXml);}async function cleanupNotificationPermissions(){try{const manifestPath=`${pwd}/androidProject/app/src/main/AndroidManifest.xml`;let manifestContent=_fs.default.readFileSync(manifestPath,"utf8");// Remove notification permissions
90
+ const notificationPermissions=["android.permission.POST_NOTIFICATIONS","android.permission.VIBRATE","android.permission.WAKE_LOCK","android.permission.RECEIVE_BOOT_COMPLETED"];manifestContent=manifestContent.split("\n").filter(line=>{return!notificationPermissions.some(permission=>line.includes(`<uses-permission android:name="${permission}"`));}).join("\n");_fs.default.writeFileSync(manifestPath,manifestContent);}catch(error){// Don't throw error for cleanup operations, just log
92
91
  progress.log(`Warning: Error cleaning notification permissions: ${error.message}`,"warning");}}async function cleanupNotificationResources(){try{const colorsPath=`${pwd}/androidProject/app/src/main/res/values/colors.xml`;let colorsContent=_fs.default.readFileSync(colorsPath,"utf8");// Remove notification color
93
92
  const existingColorLine=colorsContent.split("\n").find(line=>line.includes('name="notification_color"'));if(existingColorLine){colorsContent=colorsContent.replace(existingColorLine,"");_fs.default.writeFileSync(colorsPath,colorsContent);}}catch(error){progress.log(`Warning: Error cleaning notification resources: ${error.message}`,"warning");}}async function cleanupNotificationMetadata(){try{const manifestPath=`${pwd}/androidProject/app/src/main/AndroidManifest.xml`;let manifestContent=_fs.default.readFileSync(manifestPath,"utf8");// Remove notification metadata entries
94
- const metadataNames=["default_notification_channel_id","default_notification_icon","default_notification_color","com.google.firebase.messaging.default_notification_channel_id","com.google.firebase.messaging.default_notification_icon","com.google.firebase.messaging.default_notification_sound"];metadataNames.forEach(metadataName=>{// Remove the meta-data block including comments
95
- const metadataRegex=new RegExp(`\\s*<!--[^>]*notification[^>]*-->\\s*<meta-data[^>]*android:name="${metadataName}"[\\s\\S]*?/>|\\s*<meta-data[^>]*android:name="${metadataName}"[\\s\\S]*?/>`,"gi");manifestContent=manifestContent.replace(metadataRegex,"");});// Remove Push Notification Service
93
+ const metadataNames=["default_notification_channel_id","default_notification_icon","default_notification_color","com.google.firebase.messaging.default_notification_channel_id","com.google.firebase.messaging.default_notification_icon","com.google.firebase.messaging.default_notification_sound"];manifestContent=manifestContent.split("\n").filter(line=>{return!metadataNames.some(metadataName=>line.includes(`android:name="${metadataName}"`));}).join("\n");// Remove Push Notification Service
96
94
  const serviceRegex=/\s*<!--\s*Push Notification Service\s*-->\s*<service[^>]*android:name="[^"]*PushNotificationUtils"[\s\S]*?<\/service>/gi;manifestContent=manifestContent.replace(serviceRegex,"");// Clean up any standalone notification comments
97
95
  manifestContent=manifestContent.replace(/\s*<!--\s*Default notification configuration\s*-->/gi,"");manifestContent=manifestContent.replace(/\s*<!--\s*Firebase default notification configuration\s*-->/gi,"");manifestContent=manifestContent.replace(/\s*<!--\s*Push Notification Service\s*-->/gi,"");_fs.default.writeFileSync(manifestPath,manifestContent);}catch(error){progress.log(`Warning: Error cleaning notification metadata: ${error.message}`,"warning");}}async function cleanupNotificationAssets(){try{const destPath=`${pwd}/androidProject/app/src/main/res`;const imageFormats=["png","jpg","jpeg","gif","bmp","svg","webp","xml"];const audioFormats=["mp3","wav","ogg"];// Define notification icons to clean up
98
96
  const notificationIcons=["ic_notification","ic_notification_large"];// Define notification sounds to clean up
@@ -1,11 +1,10 @@
1
- "use strict";const{exec,execSync}=require("child_process");const fs=require("fs");const path=require("path");const TerminalProgress=require("./TerminalProgress.js").default;const crypto=require("crypto");const catalystCorePath=path.dirname(require.resolve("catalyst-core/package.json"));const pwd=path.join(catalystCorePath,"dist/native");const{WEBVIEW_CONFIG,BUILD_OUTPUT_PATH}=require(`${process.env.PWD}/config/config.json`);// Configuration constants
1
+ "use strict";const{execFile,execSync}=require("child_process");const fs=require("fs");const path=require("path");const TerminalProgress=require("./TerminalProgress.js").default;const crypto=require("crypto");const{runInteractiveCommand}=require("./utils.js");const catalystCorePath=path.dirname(require.resolve("catalyst-core/package.json"));const pwd=path.join(catalystCorePath,"dist/native");const{WEBVIEW_CONFIG,BUILD_OUTPUT_PATH}=require(`${process.cwd()}/config/config.json`);// Configuration constants
2
2
  const iosConfig=WEBVIEW_CONFIG.ios;const isGoogleSignInEnabled=WEBVIEW_CONFIG.googleSignIn?.enabled??false;const protocol=WEBVIEW_CONFIG.useHttps?"https":"http";const ip=WEBVIEW_CONFIG.LOCAL_IP||"localhost";const port=WEBVIEW_CONFIG.port?WEBVIEW_CONFIG.useHttps?443:WEBVIEW_CONFIG.port:null;let url=port?`${protocol}://${ip}:${port}`:`${protocol}://${ip}`;const PUBLIC_PATH=`${process.env.PWD}/public`;const PROJECT_DIR=`${pwd}/iosnativeWebView`;const SCHEME_NAME="iosnativeWebView";const APP_BUNDLE_ID=iosConfig.appBundleId||"com.debug.webview";const PROJECT_NAME=path.basename(PROJECT_DIR);const IPHONE_MODEL=iosConfig.simulatorName;const GOOGLE_SERVICES_FILENAME="GoogleService-Info.plist";function generateProjectObjectId(identifier,suffix=""){return crypto.createHash("md5").update(`${identifier}:${suffix}`).digest("hex").substring(0,24).toUpperCase();}function getXcodeProjectFilePath(){return path.join(PROJECT_DIR,`${PROJECT_NAME}.xcodeproj`,"project.pbxproj");}// Define build steps for progress tracking
3
3
  const steps={config:"Generating Required Configuration for build",deviceDetection:"Detecting Physical Device",launchSimulator:"Launch iOS Simulator",clean:"Clean Build Artifacts",assets:"Process Notification Assets",build:"Build IOS Project",findApp:"Locate Built Application",install:"Install Application",launch:"Launch Application"};// Configure progress display
4
- const progressConfig={titlePaddingTop:2,titlePaddingBottom:1,stepPaddingLeft:4,stepSpacing:1,errorPaddingLeft:6,bottomMargin:2};const progress=new TerminalProgress(steps,"Catalyst iOS Build",progressConfig);// Utility function to run shell commands
5
- function runCommand(command,options={}){return new Promise((resolve,reject)=>{// eslint-disable-next-line security/detect-child-process
6
- exec(command,{maxBuffer:1024*1024*10,...options},(error,stdout,stderr)=>{if(error){console.error(`Command failed: ${command}`);console.error(`Error: ${error.message}`);console.error(`stderr: ${stderr}`);reject(error);return;}if(stderr){console.warn(`Warning: ${stderr}`);}resolve(stdout.trim());});});}async function getBootedSimulatorUUID(modelName){try{// First try to find a booted simulator of the specified model
7
- let command=`xcrun simctl list devices | grep "${modelName}" | grep "Booted" | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})" | head -n 1`;let uuid=execSync(command).toString().trim();if(uuid){console.log(`Found booted simulator of model ${modelName}`);return uuid;}// If no booted simulator of the specified model is found, check any booted simulator
8
- console.log(`No booted simulator of model ${modelName} found, checking for any booted simulator...`);command=`xcrun simctl list devices | grep "Booted" | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})" | head -n 1`;uuid=execSync(command).toString().trim();if(uuid){console.log("Found another booted simulator, will use it instead");return uuid;}return null;}catch(error){console.log("No booted simulators found");return null;}}async function getBootedSimulatorInfo(){try{const listCommand="xcrun simctl list devices --json";const simulatorList=JSON.parse(execSync(listCommand).toString());for(const runtime in simulatorList.devices){const devices=simulatorList.devices[runtime];for(const device of devices){if(device.state==="Booted"){return{udid:device.udid,name:device.name,runtime:runtime};}}}return null;}catch(error){console.log("Failed to get booted simulator info:",error.message);return null;}}async function generatePackageSwift(){try{const isNotificationsEnabled=WEBVIEW_CONFIG.notifications?.enabled??false;progress.log(`🔧 Generating Package.swift (notifications: ${isNotificationsEnabled})`,"info");// Create hash of current config to detect changes
4
+ const progressConfig={titlePaddingTop:2,titlePaddingBottom:1,stepPaddingLeft:4,stepSpacing:1,errorPaddingLeft:6,bottomMargin:2};const progress=new TerminalProgress(steps,"Catalyst iOS Build",progressConfig);const shellCommand=process.platform==="win32"?"cmd.exe":"sh";const shellArgs=command=>process.platform==="win32"?["/d","/s","/c",command]:["-c",command];// Utility function to run shell commands
5
+ function runCommand(command,options={}){return new Promise((resolve,reject)=>{execFile(shellCommand,shellArgs(command),{maxBuffer:1024*1024*10,...options},(error,stdout,stderr)=>{if(error){console.error(`Command failed: ${command}`);console.error(`Error: ${error.message}`);console.error(`stderr: ${stderr}`);reject(error);return;}if(stderr){console.warn(`Warning: ${stderr}`);}resolve(stdout.trim());});});}async function getBootedSimulatorUUID(modelName){const UUID_RE=/[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}/i;try{const output=await runInteractiveCommand("xcrun",["simctl","list","devices"],{});const lines=output.split("\n");// First pass: find a booted simulator matching modelName
6
+ for(const line of lines){if(line.includes(modelName)&&line.includes("Booted")){const m=line.match(UUID_RE);if(m){console.log(`Found booted simulator of model ${modelName}`);return m[0];}}}// Second pass: any booted simulator
7
+ console.log(`No booted simulator of model ${modelName} found, checking for any booted simulator...`);for(const line of lines){if(line.includes("Booted")){const m=line.match(UUID_RE);if(m){console.log("Found another booted simulator, will use it instead");return m[0];}}}return null;}catch(error){console.log("No booted simulators found");return null;}}async function getBootedSimulatorInfo(){try{const listCommand="xcrun simctl list devices --json";const simulatorList=JSON.parse(execSync(listCommand).toString());for(const runtime in simulatorList.devices){const devices=simulatorList.devices[runtime];for(const device of devices){if(device.state==="Booted"){return{udid:device.udid,name:device.name,runtime:runtime};}}}return null;}catch(error){console.log("Failed to get booted simulator info:",error.message);return null;}}async function generatePackageSwift(){try{const isNotificationsEnabled=WEBVIEW_CONFIG.notifications?.enabled??false;progress.log(`🔧 Generating Package.swift (notifications: ${isNotificationsEnabled})`,"info");// Create hash of current config to detect changes
9
8
  const configHash=crypto.createHash("md5").update(JSON.stringify({notifications:isNotificationsEnabled,googleSignIn:isGoogleSignInEnabled})).digest("hex");const hashFilePath=path.join(PROJECT_DIR,".package-config-hash");const targetPath=path.join(PROJECT_DIR,"Package.swift");let shouldUpdate=true;// Check if we need to update based on config change
10
9
  if(fs.existsSync(hashFilePath)){const previousHash=fs.readFileSync(hashFilePath,"utf8");if(previousHash===configHash){shouldUpdate=false;progress.log("Package.swift already up to date","info");}}// Ensure Package.swift exists even if hash matches
11
10
  if(!fs.existsSync(targetPath)){shouldUpdate=true;progress.log("Package.swift missing, will generate it now","info");}if(shouldUpdate){progress.log("Generating Package.swift dynamically","info");// Build the Package.swift content dynamically
@@ -295,7 +294,7 @@ const destinationPath=`${imagesetDir}/launchscreen.${ext}`;fs.copyFileSync(sourc
295
294
  const contentsJson={images:[{filename:`launchscreen.${ext}`,idiom:"universal",scale:"1x"}],info:{author:"xcode",version:1}};fs.writeFileSync(`${imagesetDir}/Contents.json`,JSON.stringify(contentsJson,null,2));progress.log(`Created launch screen imageset: launchscreen.${ext}`,"success");splashImageFound=true;break;}}if(!splashImageFound){progress.log("No custom splash screen image found in public folder","info");progress.log("Supported formats: splashscreen.png, splashscreen.jpg, splashscreen.jpeg","info");}}catch(error){progress.log(`Warning: Error copying splash screen assets: ${error.message}`,"warning");}}async function copyAppIcon(){try{const publicDir=`${process.env.PWD}/public/ios/appIcons`;const assetsDir=`${PROJECT_DIR}/${PROJECT_NAME}/Assets.xcassets`;const iconSetDir=`${assetsDir}/AppIcon.appiconset`;// Check if public directory exists
296
295
  if(!fs.existsSync(publicDir)){progress.log("Public directory not found, skipping app icon copy","info");return;}// Define iPhone icon sizes with their configurations
297
296
  const iconSizes=[{size:"20x20",idiom:"iphone",scale:"2x"},{size:"20x20",idiom:"iphone",scale:"3x"},{size:"29x29",idiom:"iphone",scale:"2x"},{size:"29x29",idiom:"iphone",scale:"3x"},{size:"40x40",idiom:"iphone",scale:"2x"},{size:"40x40",idiom:"iphone",scale:"3x"},{size:"60x60",idiom:"iphone",scale:"2x"},{size:"60x60",idiom:"iphone",scale:"3x"},{size:"1024x1024",idiom:"ios-marketing",scale:"1x"}];const imageExtensions=["png","jpg","jpeg"];// Recursively find all image files in a directory
298
- const findImagesRecursively=(dir,extensions)=>{let results=[];try{const items=fs.readdirSync(dir);for(const item of items){const fullPath=path.join(dir,item);const stat=fs.statSync(fullPath);if(stat.isDirectory()){results=results.concat(findImagesRecursively(fullPath,extensions));}else if(stat.isFile()){const ext=path.extname(item).toLowerCase().slice(1);if(extensions.includes(ext)){results.push(fullPath);}}}}catch(err){// Ignore directory read errors
297
+ const findImagesRecursively=(dir,extensions)=>{let results=[];try{const items=fs.readdirSync(dir);for(const item of items){const fullPath=`${dir}${path.sep}${item}`;const stat=fs.statSync(fullPath);if(stat.isDirectory()){results=results.concat(findImagesRecursively(fullPath,extensions));}else if(stat.isFile()){const ext=path.extname(item).toLowerCase().slice(1);if(extensions.includes(ext)){results.push(fullPath);}}}}catch(err){// Ignore directory read errors
299
298
  }return results;};// Get all image files from public directory
300
299
  const allImages=findImagesRecursively(publicDir,imageExtensions);const foundIcons=[];// Create icon set directory
301
300
  if(!fs.existsSync(iconSetDir)){fs.mkdirSync(iconSetDir,{recursive:true});}// Load existing Contents.json or create new
@@ -49,7 +49,7 @@ const gradlewPath=_path.default.join(tempDir,"gradlew");if(!_fs.default.existsSy
49
49
  progress.log(`Checking copied files (informational only):`,"info");const filesToCheck=[{path:"gradlew",desc:"Gradle wrapper"},{path:"build.gradle",desc:"Root build.gradle"},{path:"build.gradle.kts",desc:"Root build.gradle.kts"},{path:"app/build.gradle",desc:"App build.gradle"},{path:"app/build.gradle.kts",desc:"App build.gradle.kts"},{path:"settings.gradle",desc:"Settings gradle"},{path:"settings.gradle.kts",desc:"Settings gradle.kts"},{path:"app/src/main/AndroidManifest.xml",desc:"Android manifest"}];filesToCheck.forEach(file=>{const fullPath=_path.default.join(tempDir,file.path);const exists=_fs.default.existsSync(fullPath);progress.log(` ${file.desc}: ${exists?"✅ Found":"⚠️ Not found"}`,exists?"info":"warning");});// List all files in temp directory for debugging
50
50
  if(_fs.default.existsSync(tempDir)){const tempFiles=_fs.default.readdirSync(tempDir);progress.log(`Temp directory contents: ${tempFiles.join(", ")}`,"info");}}catch(error){// Clean up on error
51
51
  if(_fs.default.existsSync(tempDir)){try{;(0,_utils.runCommand)(`rm -rf "${tempDir}"`);}catch(cleanupError){progress.log(`Warning: Could not cleanup temp directory: ${cleanupError.message}`,"warning");}}throw new Error(`Failed to create temporary deployment copy: ${error.message}`);}// Find and rename any subdirectories that contain the old project name within the temp copy
52
- try{const findCommand=`find "${tempDir}" -type d -name "*${oldProjectName}*"`;const result=(0,_utils.runCommand)(findCommand);if(result.trim()){const dirsToRename=result.trim().split("\n").filter(dir=>dir.trim());for(const dir of dirsToRename){const newDirName=dir.replace(new RegExp(oldProjectName,"g"),newProjectName);if(dir!==newDirName){;(0,_utils.runCommand)(`mv "${dir}" "${newDirName}"`);progress.log(`Renamed subdirectory: ${_path.default.basename(dir)} → ${_path.default.basename(newDirName)}`,"success");}}}}catch(error){progress.log(`Warning: Error finding subdirectories to rename: ${error.message}`,"warning");}progress.log("Temporary deployment project created successfully","success");return tempDir;}async function finalizeAABAndCleanup(projectPaths,androidConfig,aabResult,tempDeploymentPath){const{oldProjectPath}=projectPaths;progress.log("Finalizing AAB and cleaning up temporary files...","info");try{// Create final output directory next to original project
52
+ try{const findCommand=`find "${tempDir}" -type d -name "*${oldProjectName}*"`;const result=(0,_utils.runCommand)(findCommand);if(result.trim()){const dirsToRename=result.trim().split("\n").filter(dir=>dir.trim());for(const dir of dirsToRename){const newDirName=dir.split(oldProjectName).join(newProjectName);if(dir!==newDirName){;(0,_utils.runCommand)(`mv "${dir}" "${newDirName}"`);progress.log(`Renamed subdirectory: ${_path.default.basename(dir)} → ${_path.default.basename(newDirName)}`,"success");}}}}catch(error){progress.log(`Warning: Error finding subdirectories to rename: ${error.message}`,"warning");}progress.log("Temporary deployment project created successfully","success");return tempDir;}async function finalizeAABAndCleanup(projectPaths,androidConfig,aabResult,tempDeploymentPath){const{oldProjectPath}=projectPaths;progress.log("Finalizing AAB and cleaning up temporary files...","info");try{// Create final output directory next to original project
53
53
  const outputDir=androidConfig.outputPath||_path.default.join(_path.default.dirname(oldProjectPath),"build-output");if(!_fs.default.existsSync(outputDir)){_fs.default.mkdirSync(outputDir,{recursive:true});progress.log(`Created output directory: ${outputDir}`,"success");}// Copy AAB to final location with descriptive name
54
54
  const timestamp=new Date().toISOString().replace(/[:.]/g,"-").split("T")[0];const finalAabName=`${androidConfig.newProjectName}-release-${timestamp}.aab`;const finalAabPath=_path.default.join(outputDir,finalAabName);_fs.default.copyFileSync(aabResult.outputAabPath,finalAabPath);progress.log(`AAB saved to final location: ${finalAabPath}`,"success");// Copy keystore to output directory for future use
55
55
  const keystorePath=_path.default.join(tempDeploymentPath,"app","keystore.jks");if(_fs.default.existsSync(keystorePath)){const finalKeystorePath=_path.default.join(outputDir,`${androidConfig.newProjectName}-keystore.jks`);_fs.default.copyFileSync(keystorePath,finalKeystorePath);progress.log(`Keystore copied to: ${finalKeystorePath}`,"success");}// Clean up temporary deployment directory
@@ -64,8 +64,8 @@ try{;(0,_utils.runCommand)(`find "${_path.default.join(newProjectPath,"app","src
64
64
  }}}if(totalUpdatedFiles>0){progress.log(`✅ Total package declarations updated: ${totalUpdatedFiles} files`,"success");}// Update AndroidManifest.xml
65
65
  const manifestPath=_path.default.join(newProjectPath,"app","src","main","AndroidManifest.xml");if(_fs.default.existsSync(manifestPath)){let content=_fs.default.readFileSync(manifestPath,"utf8");if(content.includes(OLD_PACKAGE)){content=content.replace(new RegExp(OLD_PACKAGE,"g"),packageName);_fs.default.writeFileSync(manifestPath,content,"utf8");progress.log(`✅ Updated AndroidManifest.xml with package: ${packageName}`,"success");}}// Update proguard-rules.pro
66
66
  const proguardPath=_path.default.join(newProjectPath,"app","proguard-rules.pro");if(_fs.default.existsSync(proguardPath)){let content=_fs.default.readFileSync(proguardPath,"utf8");if(content.includes(OLD_PACKAGE)){content=content.replace(new RegExp(OLD_PACKAGE.replace(/\./g,"\\."),"g"),packageName);_fs.default.writeFileSync(proguardPath,content,"utf8");progress.log(`✅ Updated proguard-rules.pro with package: ${packageName}`,"success");}}}// STEP 2: Find and rename files that contain the old project name
67
- const findFilesCommand=`find "${newProjectPath}" -type f -name "*${oldProjectName}*"`;const fileResult=(0,_utils.runCommand)(findFilesCommand);if(fileResult.trim()){const filesToRename=fileResult.trim().split("\n").filter(file=>file.trim());for(const file of filesToRename){const newFileName=file.replace(new RegExp(oldProjectName,"g"),newProjectName);if(file!==newFileName){;(0,_utils.runCommand)(`mv "${file}" "${newFileName}"`);progress.log(`Renamed file: ${_path.default.basename(file)} → ${_path.default.basename(newFileName)}`,"success");}}}// STEP 3: Update file contents that reference the old project name
68
- const fileTypes=["*.gradle","*.gradle.kts","*.xml","*.json","*.properties","*.java","*.kt","*.kts","*.js","*.ts","*.md"];for(const fileType of fileTypes){try{const findContentCommand=`find "${newProjectPath}" -name "${fileType}" -type f`;const files=(0,_utils.runCommand)(findContentCommand);if(files.trim()){const fileList=files.trim().split("\n").filter(file=>file.trim());for(const file of fileList){try{const content=_fs.default.readFileSync(file,"utf8");if(content.includes(oldProjectName)){const updatedContent=content.replace(new RegExp(oldProjectName,"g"),newProjectName);_fs.default.writeFileSync(file,updatedContent,"utf8");progress.log(`Updated content in: ${_path.default.relative(newProjectPath,file)}`,"info");}}catch(fileError){progress.log(`Warning: Could not update file ${file}: ${fileError.message}`,"warning");}}}}catch(typeError){// Continue with other file types if one fails
67
+ const findFilesCommand=`find "${newProjectPath}" -type f -name "*${oldProjectName}*"`;const fileResult=(0,_utils.runCommand)(findFilesCommand);if(fileResult.trim()){const filesToRename=fileResult.trim().split("\n").filter(file=>file.trim());for(const file of filesToRename){const newFileName=file.split(oldProjectName).join(newProjectName);if(file!==newFileName){;(0,_utils.runCommand)(`mv "${file}" "${newFileName}"`);progress.log(`Renamed file: ${_path.default.basename(file)} → ${_path.default.basename(newFileName)}`,"success");}}}// STEP 3: Update file contents that reference the old project name
68
+ const fileTypes=["*.gradle","*.gradle.kts","*.xml","*.json","*.properties","*.java","*.kt","*.kts","*.js","*.ts","*.md"];for(const fileType of fileTypes){try{const findContentCommand=`find "${newProjectPath}" -name "${fileType}" -type f`;const files=(0,_utils.runCommand)(findContentCommand);if(files.trim()){const fileList=files.trim().split("\n").filter(file=>file.trim());for(const file of fileList){try{const content=_fs.default.readFileSync(file,"utf8");if(content.includes(oldProjectName)){const updatedContent=content.split(oldProjectName).join(newProjectName);_fs.default.writeFileSync(file,updatedContent,"utf8");progress.log(`Updated content in: ${_path.default.relative(newProjectPath,file)}`,"info");}}catch(fileError){progress.log(`Warning: Could not update file ${file}: ${fileError.message}`,"warning");}}}}catch(typeError){// Continue with other file types if one fails
69
69
  progress.log(`Warning: Error processing ${fileType} files: ${typeError.message}`,"warning");}}progress.log("File content updates completed","success");}catch(error){throw new Error(`Error updating file contents: ${error.message}`);}}async function buildAndroidAAB(configPathOrConfig){let androidConfig;let projectPaths;let backupPath;let tempDeploymentPath;try{// Initialize configuration - handle both config path and direct config object
70
70
  progress.start("config");if(typeof configPathOrConfig==="string"){const{android}=await initializeConfig(configPathOrConfig);androidConfig=android;}else{// Direct config object passed (from buildAppAndroid.js)
71
71
  // All defaults should already be applied in buildAppAndroid.js
@@ -1,9 +1,8 @@
1
- "use strict";const{exec,execSync}=require("child_process");const fs=require("fs");/**
1
+ "use strict";const{exec,execFile,execSync}=require("child_process");const fs=require("fs");/**
2
2
  * Get local IP address on macOS using ifconfig
3
3
  */function getLocalIPAddress(){try{const command=`ifconfig | grep "inet " | grep -v 127.0.0.1 | head -n 1 | awk '{print $2}'`;return execSync(command).toString().trim();}catch(error){console.warn("Could not get local IP, using localhost");return"localhost";}}/**
4
4
  * Check if server is running on given port
5
- */async function isServerRunning(port){return new Promise(resolve=>{const command=`lsof -i :${port}`;// eslint-disable-next-line security/detect-child-process
6
- exec(command,(error,stdout)=>{if(error){resolve(false);return;}resolve(stdout.includes("LISTEN"));});});}/**
5
+ */async function isServerRunning(port){return new Promise(resolve=>{const normalizedPort=Number(port);if(!Number.isInteger(normalizedPort)||normalizedPort<1||normalizedPort>65535){resolve(false);return;}execFile("lsof",["-i",`:${normalizedPort}`],(error,stdout)=>{if(error){resolve(false);return;}resolve(stdout.includes("LISTEN"));});});}/**
7
6
  * Start server in background using npm start
8
7
  */function startServerBackground(){console.log("Starting server in background...");const serverProcess=exec("npm start",{detached:true,stdio:"ignore"});if(serverProcess.pid){serverProcess.unref();// Allow parent to exit
9
8
  console.log("Server started in background");}}/**
@@ -1,4 +1,7 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.commandExists=commandExists;exports.promptUser=promptUser;exports.runCommand=runCommand;exports.runInteractiveCommand=runInteractiveCommand;exports.runSdkManagerCommand=runSdkManagerCommand;exports.validateAndCompleteConfig=validateAndCompleteConfig;var _child_process=require("child_process");var _readline=_interopRequireDefault(require("readline"));var _fs=_interopRequireDefault(require("fs"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const rl=_readline.default.createInterface({input:process.stdin,output:process.stdout});function runCommand(command){try{return(0,_child_process.execSync)(command,{encoding:"utf8"});}catch(error){console.error(`Error executing command: ${command}`);console.error(`Error message: ${error.message}`);throw error;}}function commandExists(command){try{(0,_child_process.execSync)(`which ${command}`,{stdio:"ignore"});return true;}catch(error){return false;}}async function promptUser(question){return new Promise(resolve=>{rl.question(question,answer=>{resolve(answer.trim());});});}async function runInteractiveCommand(command,args,promptResponses={}){return new Promise((resolve,reject)=>{const process=(0,_child_process.spawn)(command,args,{stdio:["pipe","pipe","pipe"]});let buffer="";let outputBuffer="";process.stdout.on("data",data=>{buffer+=data.toString();outputBuffer+=data.toString();console.log(data.toString());handlePrompts(process,buffer,promptResponses);});process.stderr.on("data",data=>{buffer+=data.toString();outputBuffer+=data.toString();console.error(data.toString());handlePrompts(process,buffer,promptResponses);});process.on("close",code=>{if(code===0){resolve(outputBuffer);}else{reject(new Error(`Command failed with exit code ${code}`));}});});}function handlePrompts(process,buffer,promptResponses){for(const[prompt,response]of Object.entries(promptResponses)){if(buffer.includes(prompt)){process.stdin.write(response+"\n");return;}}}async function runSdkManagerCommand(sdkManagerPath,args){const promptResponses={"(y/N)":"y","Accept? (y/N):":"y"};return runInteractiveCommand(sdkManagerPath,args,promptResponses);}async function validateAndCompleteConfig(platform,configPath){// Read existing config
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.commandExists=commandExists;exports.promptUser=promptUser;exports.runCommand=runCommand;exports.runInteractiveCommand=runInteractiveCommand;exports.runSdkManagerCommand=runSdkManagerCommand;exports.validateAndCompleteConfig=validateAndCompleteConfig;var _child_process=require("child_process");var _readline=_interopRequireDefault(require("readline"));var _fs=_interopRequireDefault(require("fs"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const rl=_readline.default.createInterface({input:process.stdin,output:process.stdout});function runCommand(command){try{// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process - Native setup/build uses fixed internal command strings; preserve existing shell behavior.
2
+ return(0,_child_process.execSync)(command,{encoding:"utf8"});}catch(error){console.error(`Error executing command: ${command}`);console.error(`Error message: ${error.message}`);throw error;}}function commandExists(command){try{// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process - Utility checks known local tool names during native setup.
3
+ (0,_child_process.execSync)(`which ${command}`,{stdio:"ignore"});return true;}catch(error){return false;}}async function promptUser(question){return new Promise(resolve=>{rl.question(question,answer=>{resolve(answer.trim());});});}async function runInteractiveCommand(command,args,promptResponses={}){return new Promise((resolve,reject)=>{// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process - Native build commands are fixed internal commands passed with argv arrays.
4
+ const process=(0,_child_process.spawn)(command,args,{stdio:["pipe","pipe","pipe"]});let buffer="";let outputBuffer="";process.stdout.on("data",data=>{buffer+=data.toString();outputBuffer+=data.toString();console.log(data.toString());handlePrompts(process,buffer,promptResponses);});process.stderr.on("data",data=>{buffer+=data.toString();outputBuffer+=data.toString();console.error(data.toString());handlePrompts(process,buffer,promptResponses);});process.on("close",code=>{if(code===0){resolve(outputBuffer);}else{reject(new Error(`Command failed with exit code ${code}`));}});});}function handlePrompts(process,buffer,promptResponses){for(const[prompt,response]of Object.entries(promptResponses)){if(buffer.includes(prompt)){process.stdin.write(response+"\n");return;}}}async function runSdkManagerCommand(sdkManagerPath,args){const promptResponses={"(y/N)":"y","Accept? (y/N):":"y"};return runInteractiveCommand(sdkManagerPath,args,promptResponses);}async function validateAndCompleteConfig(platform,configPath){// Read existing config
2
5
  let config;try{const configContent=_fs.default.readFileSync(configPath,"utf8");config=JSON.parse(configContent);}catch(error){console.error(`Error reading config file: ${error.message}`);config={};}// Initialize WEBVIEW_CONFIG if it doesn't exist
3
6
  if(!config.WEBVIEW_CONFIG){config.WEBVIEW_CONFIG={};}// Check if WEBVIEW_CONFIG already has all required fields
4
7
  const webviewConfig=config.WEBVIEW_CONFIG;const commonFields={port:"Enter port number (e.g., 3005): "};const platformConfigs={android:{buildType:"Enter Android build type (debug/release): ",sdkPath:"Enter Android SDK path: ",emulatorName:"Enter Android emulator name (e.g., Small_Phone_API_35): ",cachePattern:"Enter Cache pattern (e.g., *.css): "},ios:{buildType:"Enter iOS build type (debug/release): ",appBundleId:"Enter iOS bundle ID (e.g., com.test.test): ",simulatorName:"Enter iOS simulator name (e.g., iPhone 16 Pro): ",cachePattern:"Enter Cache pattern (e.g., *.css): "}};if(!platformConfigs[platform]){throw new Error('Invalid platform. Must be either "android" or "ios"');}// Check if all required fields are present
@@ -1,3 +1,3 @@
1
1
  "use strict";const path=require("path");const{spawnSync}=require("child_process");const{arrayToObject}=require("./scriptUtils");const{name}=require(`${process.cwd()}/package.json`);const{BUILD_OUTPUT_PATH}=require(`${process.cwd()}/config/config.json`);/**
2
2
  * @description - Serves production build of the application.
3
- */function devServe(){const commandLineArguments=process.argv.slice(2);const argumentsObject=arrayToObject(commandLineArguments);const dirname=path.resolve(__dirname,"../../");const command=`cross-env APPLICATION=${name||"catalyst_app"} node -r ./dist/scripts/loadScriptsBeforeServerStarts.js ${process.cwd()}/${BUILD_OUTPUT_PATH}/startServer.js`;spawnSync(command,[],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,NODE_ENV:"production",IS_DEV_COMMAND:true,...argumentsObject}});}devServe();
3
+ */function devServe(){const commandLineArguments=process.argv.slice(2);const argumentsObject=arrayToObject(commandLineArguments);const dirname=path.resolve(__dirname,"../../");spawnSync("node",["-r","./dist/scripts/loadScriptsBeforeServerStarts.js",path.join(process.cwd(),BUILD_OUTPUT_PATH,"startServer.js")],{cwd:dirname,stdio:"inherit",env:{...process.env,src_path:process.cwd(),BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,NODE_ENV:"production",IS_DEV_COMMAND:true,APPLICATION:name||"catalyst_app",...argumentsObject}});}devServe();
@@ -1,2 +1,6 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.catalystResultMap=void 0;var _path=_interopRequireDefault(require("path"));var _moduleAlias=_interopRequireDefault(require("module-alias"));var _package=require("../../package.json");var _validator=require("./validator.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const{_moduleAliases:templateModuleAliases}=require(`${process.env.src_path}/package.json`);const catalystResultMap=exports.catalystResultMap=Object.keys(_package._moduleAliases||[]).reduce((resultMap,aliasName)=>{const aliasPath=_package._moduleAliases[aliasName];if(aliasName?.includes("@catalyst/template")){if(aliasName?.includes("server")&&process.env.NODE_ENV==="production"){resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}else{resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);}return resultMap;}if(aliasName?.includes("@catalyst")&&!aliasName?.includes("@catalyst/template")){if(aliasName?.includes("server")&&process.env.NODE_ENV==="production"){resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}else{resultMap[aliasName]=_path.default.join(__dirname,"../",aliasPath);}return resultMap;}return resultMap;},{});_moduleAlias.default.addAliases(catalystResultMap);// resolves module alias imports
2
- if((0,_validator.validateModuleAlias)(templateModuleAliases)){_moduleAlias.default.addAliases(Object.keys(templateModuleAliases||[]).reduce((resultMap,aliasName)=>{const aliasPath=templateModuleAliases[aliasName];if(aliasPath.includes("server")){if(process.env.NODE_ENV!=="production"){resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);}else{resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}}else resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);return resultMap;},{}));}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.catalystResultMap=void 0;var _path=_interopRequireDefault(require("path"));var _moduleAlias=_interopRequireDefault(require("module-alias"));var _package=require("../../package.json");var _validator=require("./validator.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const{_moduleAliases:templateModuleAliases}=require(`${process.env.src_path}/package.json`);const catalystResultMap=exports.catalystResultMap=Object.keys(_package._moduleAliases||[]).reduce((resultMap,aliasName)=>{const aliasPath=_package._moduleAliases[aliasName];if(aliasName?.includes("@catalyst/template")){if(aliasName?.includes("server")&&process.env.NODE_ENV==="production"){resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}else{// nosemgrep
2
+ resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);}return resultMap;}if(aliasName?.includes("@catalyst")&&!aliasName?.includes("@catalyst/template")){if(aliasName?.includes("server")&&process.env.NODE_ENV==="production"){resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}else{// nosemgrep
3
+ resultMap[aliasName]=_path.default.join(__dirname,"../",aliasPath);}return resultMap;}return resultMap;},{});_moduleAlias.default.addAliases(catalystResultMap);// resolves module alias imports
4
+ if((0,_validator.validateModuleAlias)(templateModuleAliases)){_moduleAlias.default.addAliases(Object.keys(templateModuleAliases||[]).reduce((resultMap,aliasName)=>{const aliasPath=templateModuleAliases[aliasName];if(aliasPath.includes("server")){if(process.env.NODE_ENV!=="production"){// nosemgrep
5
+ resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);}else{resultMap[aliasName]=_path.default.join(process.env.src_path,process.env.BUILD_OUTPUT_PATH);}}else{// nosemgrep
6
+ resultMap[aliasName]=_path.default.join(process.env.src_path,aliasPath);}return resultMap;},{}));}
@@ -1,2 +1,3 @@
1
1
  "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.arrayToObject=arrayToObject;exports.runBuildCommands=exports.printBundleInformation=void 0;const fs=require("fs");const path=require("path");const util=require("node:util");const{spawnSync}=require("child_process");const{gray,cyan}=require("picocolors");const{BUILD_OUTPUT_PATH}=require(`${process.cwd()}/config/config.json`);// Function to get file size synchronously
2
- function getFileSizeSync(filePath){try{const stats=fs.statSync(filePath);return stats.size;}catch(err){console.error(`Error getting bundle information for file ${filePath}: ${err}`);return null;}}const printBundleInformation=()=>{let bundleList=[];const directoryPath=path.join(process.cwd(),`${BUILD_OUTPUT_PATH}/public`);try{const files=fs.readdirSync(directoryPath);files.forEach(file=>{if(!file.includes("txt")&&!file.includes("json")){const filePath=path.join(directoryPath,file);const fileSize=getFileSizeSync(filePath);if(fileSize!==null){bundleList.push({file,fileSize});}}});}catch(err){console.error("Unable to scan build folder: "+err);}bundleList.sort((a,b)=>b.fileSize-a.fileSize);bundleList.forEach(({file,fileSize})=>{const fileName=`${gray(`${BUILD_OUTPUT_PATH}/public/`)}${cyan(file)}`;const fileSizeInKb=(fileSize/1024).toFixed(2);const size=`\t${fileSizeInKb} kB`.padEnd(16);console.log(util.format(size,fileName));});};exports.printBundleInformation=printBundleInformation;function arrayToObject(array){const obj={};array.forEach(item=>{const[key,value]=item.split("=");if(value)obj[key]=value;});return obj;}const BUILD_FAILURE_MESSAGE="\nBuild Failed!";function shouldFailBuild(result){return result.error||result.signal||result.status!==null&&result.status!==0;}function logBuildFailure(result,failureMessage=BUILD_FAILURE_MESSAGE){console.error(failureMessage);if(result.error){console.error(`Error: ${result.error?.message||result.error}\n`);}if(result.signal){console.error(`Signal: ${result.signal}\n`);}if(result.status!==null){console.error(`Exit code: ${result.status}\n`);}}const runBuildCommands=({commands,cwd,env,failureMessage=BUILD_FAILURE_MESSAGE})=>{const command=commands.join(" && ");const result=spawnSync(command,[],{cwd,stdio:"inherit",shell:true,env});if(shouldFailBuild(result)){logBuildFailure(result,failureMessage);process.exit(result.status||1);}return result;};exports.runBuildCommands=runBuildCommands;
2
+ function getFileSizeSync(filePath){try{const stats=fs.statSync(filePath);return stats.size;}catch(err){console.error(`Error getting bundle information for file ${filePath}: ${err}`);return null;}}const printBundleInformation=()=>{let bundleList=[];const directoryPath=path.join(process.cwd(),`${BUILD_OUTPUT_PATH}/public`);try{const files=fs.readdirSync(directoryPath);files.forEach(file=>{if(!file.includes("txt")&&!file.includes("json")){const filePath=`${directoryPath}${path.sep}${file}`;const fileSize=getFileSizeSync(filePath);if(fileSize!==null){bundleList.push({file,fileSize});}}});}catch(err){console.error("Unable to scan build folder: "+err);}bundleList.sort((a,b)=>b.fileSize-a.fileSize);bundleList.forEach(({file,fileSize})=>{const fileName=`${gray(`${BUILD_OUTPUT_PATH}/public/`)}${cyan(file)}`;const fileSizeInKb=(fileSize/1024).toFixed(2);const size=`\t${fileSizeInKb} kB`.padEnd(16);console.log(util.format(size,fileName));});};exports.printBundleInformation=printBundleInformation;function arrayToObject(array){const obj={};array.forEach(item=>{const[key,value]=item.split("=");if(value)obj[key]=value;});return obj;}const BUILD_FAILURE_MESSAGE="\nBuild Failed!";function shouldFailBuild(result){return result.error||result.signal||result.status!==null&&result.status!==0;}function logBuildFailure(result,failureMessage=BUILD_FAILURE_MESSAGE){console.error(failureMessage);if(result.error){console.error(`Error: ${result.error?.message||result.error}\n`);}if(result.signal){console.error(`Signal: ${result.signal}\n`);}if(result.status!==null){console.error(`Exit code: ${result.status}\n`);}}const runBuildCommands=({commands,cwd,env,failureMessage=BUILD_FAILURE_MESSAGE})=>{const command=commands.join(" && ");// nosemgrep
3
+ const result=spawnSync(command,[],{cwd,stdio:"inherit",shell:true,env});if(shouldFailBuild(result)){logBuildFailure(result,failureMessage);process.exit(result.status||1);}return result;};exports.runBuildCommands=runBuildCommands;
@@ -1,3 +1,4 @@
1
1
  "use strict";const path=require("path");const{spawnSync}=require("child_process");const{arrayToObject}=require("./scriptUtils");const{name}=require(`${process.cwd()}/package.json`);const{BUILD_OUTPUT_PATH}=require(`${process.cwd()}/config/config.json`);/**
2
2
  * @description - Serves production build of the application.
3
- */function serve(){const commandLineArguments=process.argv.slice(2);const argumentsObject=arrayToObject(commandLineArguments);const dirname=path.resolve(__dirname,"../../");const command=`cross-env APPLICATION=${name||"catalyst_app"} node -r ./dist/scripts/loadScriptsBeforeServerStarts.js ${process.cwd()}/${BUILD_OUTPUT_PATH}/startServer.js`;spawnSync(command,[],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,NODE_ENV:"production",IS_DEV_COMMAND:false,...argumentsObject}});}serve();
3
+ */function serve(){const commandLineArguments=process.argv.slice(2);const argumentsObject=arrayToObject(commandLineArguments);const dirname=path.resolve(__dirname,"../../");// nosemgrep: javascript.lang.security.audit.spawn-shell-true.spawn-shell-true - Production serve keeps shell execution for existing script compatibility.
4
+ spawnSync("node",["-r","./dist/scripts/loadScriptsBeforeServerStarts.js",path.join(process.cwd(),BUILD_OUTPUT_PATH,"startServer.js")],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,NODE_ENV:"production",IS_DEV_COMMAND:false,APPLICATION:name||"catalyst_app",...argumentsObject}});}serve();
@@ -3,4 +3,7 @@
3
3
  */function start(){const isWindows=process.platform==="win32";const commandLineArguments=process.argv.slice(2);const argumentsObject=arrayToObject(commandLineArguments);const dirname=path.resolve(__dirname,"../../");const command=`
4
4
  node ./dist/scripts/checkVersion
5
5
  npx babel-node -r ./dist/scripts/loadScriptsBeforeServerStarts.js ./dist/webpack/development.client.babel --no-warnings=ExperimentalWarning --no-warnings=BABEL & npx babel-node -r ./dist/scripts/loadScriptsBeforeServerStarts.js ./dist/server/startServer.js --extensions .js,.ts,.jsx,.tsx --watch-path=${process.env.PWD}/server --watch-path=${process.env.PWD}/src --ignore='__IGNORE__' --no-warnings=ExperimentalWarning --no-warnings=BABEL
6
- `;if(isWindows){spawn(`node ./dist/scripts/checkVersion && start /b npx babel-node -r ./dist/scripts/loadScriptsBeforeServerStarts.js ./dist/webpack/development.client.babel --no-warnings=ExperimentalWarning --no-warnings=BABEL`,[],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),NODE_ENV:"development",IS_DEV_COMMAND:false,APPLICATION:name||"catalyst_app",BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,...argumentsObject}});spawn(`node ./dist/scripts/checkVersion && npx babel-node -r ./dist/scripts/loadScriptsBeforeServerStarts.js ./dist/server/startServer.js --watch-path=${process.cwd()}/server --watch-path=${process.cwd()}/src --ignore='__IGNORE__' --no-warnings=ExperimentalWarning --no-warnings=BABEL`,[],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),NODE_ENV:"development",IS_DEV_COMMAND:false,APPLICATION:name||"catalyst_app",BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,...argumentsObject}});}else{spawnSync(command,[],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),NODE_ENV:"development",IS_DEV_COMMAND:false,APPLICATION:name||"catalyst_app",BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,...argumentsObject}});}}start();
6
+ `;if(isWindows){// nosemgrep
7
+ spawn(`node ./dist/scripts/checkVersion && start /b npx babel-node -r ./dist/scripts/loadScriptsBeforeServerStarts.js ./dist/webpack/development.client.babel --no-warnings=ExperimentalWarning --no-warnings=BABEL`,[],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),NODE_ENV:"development",IS_DEV_COMMAND:false,APPLICATION:name||"catalyst_app",BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,...argumentsObject}});// nosemgrep
8
+ spawn(`node ./dist/scripts/checkVersion && npx babel-node -r ./dist/scripts/loadScriptsBeforeServerStarts.js ./dist/server/startServer.js --watch-path=${process.cwd()}/server --watch-path=${process.cwd()}/src --ignore='__IGNORE__' --no-warnings=ExperimentalWarning --no-warnings=BABEL`,[],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),NODE_ENV:"development",IS_DEV_COMMAND:false,APPLICATION:name||"catalyst_app",BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,...argumentsObject}});}else{// nosemgrep
9
+ spawnSync(command,[],{cwd:dirname,stdio:"inherit",shell:true,env:{...process.env,src_path:process.cwd(),NODE_ENV:"development",IS_DEV_COMMAND:false,APPLICATION:name||"catalyst_app",BUILD_OUTPUT_PATH:BUILD_OUTPUT_PATH,...argumentsObject}});}}start();
@@ -2,8 +2,8 @@
2
2
  * Stores css chunks styles into cache in string format
3
3
  * @param {string} key - router path
4
4
  * @param {object} data - css elements array extracted through loadable chunk extracter
5
- */function cacheCSS(key,data){if(!process.cssCache){process.cssCache={};}let pageCss="";let listOfCachedAssets={};if(Array.isArray(data)){try{if(process.env.NODE_ENV==="production"){data.map(assetChunk=>{const assetPathArr=assetChunk.key.split("/");const assetName=assetPathArr[assetPathArr.length-1];const ext=_path.default.extname(assetName);if(ext===".css"){// if css file has not already been cached, add the content of this CSS file in pageCSS
6
- if(!listOfCachedAssets[assetName]&&!process.cssCache?.[key]?.listOfCachedAssets?.[assetName]){pageCss+=_fs.default.readFileSync(_path.default.resolve(process.env.src_path,`${process.env.BUILD_OUTPUT_PATH}/public`,assetName));listOfCachedAssets[assetName]=true;}}});}}catch(error){logger.error("Error in caching CSS:"+error);}}// if css cache exists for a route and there are some uncached css, add that css to the cache
5
+ */function cacheCSS(key,data){if(!process.cssCache){process.cssCache={};}let pageCss="";let listOfCachedAssets={};if(Array.isArray(data)){try{if(process.env.NODE_ENV==="production"){data.map(assetChunk=>{const assetPathArr=assetChunk.key.split("/");const assetName=_path.default.basename(assetPathArr[assetPathArr.length-1]);const ext=_path.default.extname(assetName);if(ext===".css"){// if css file has not already been cached, add the content of this CSS file in pageCSS
6
+ if(!listOfCachedAssets[assetName]&&!process.cssCache?.[key]?.listOfCachedAssets?.[assetName]){pageCss+=_fs.default.readFileSync(`${process.env.src_path}${_path.default.sep}${process.env.BUILD_OUTPUT_PATH}${_path.default.sep}public${_path.default.sep}${assetName}`);listOfCachedAssets[assetName]=true;}}});}}catch(error){logger.error("Error in caching CSS:"+error);}}// if css cache exists for a route and there are some uncached css, add that css to the cache
7
7
  // this will run on subsequent hits and will add css of uncached widgets to the cache
8
8
  if(process.cssCache[key]){if(pageCss!==""){let existingListOfCachedAssets=process.cssCache[key].listOfCachedAssets;const newPageCSS=process.cssCache[key].pageCss+pageCss;let newListOfCachedAssets={...existingListOfCachedAssets,...listOfCachedAssets};process.cssCache[key]={pageCss:newPageCSS,listOfCachedAssets:newListOfCachedAssets};}}else{// create css cache for a page. This will run on the first hit.
9
9
  process.cssCache[key]={pageCss,listOfCachedAssets};}return pageCss;}/**
@@ -1 +1 @@
1
- "use strict";const pc=require("picocolors");const handleError=e=>{console.log(pc.red("Failed to start server: "),e);};const safeCall=(fn,...args)=>{try{if(!fn)return;if(typeof fn!=="function"){console.log(pc.red("Invalid lifecycle method defined in server/index.js"));return;}fn(...args);}catch(e){console.log(pc.red(`Failed to execute ${fn.name}: `),e);}};const validatePreInitServer=fn=>{try{if(!fn)throw new Error("preServerInit named function should be defined in server/index.js");if(typeof fn!=="function")throw new Error("preServerInit should be function present in server/index.js");return true;}catch(e){handleError(e);}};const validateMiddleware=fn=>{try{if(!fn)throw new Error("addMiddlewares named function not found in server/server.js");if(typeof fn!=="function")throw new Error("addMiddlewares should be function present in server/server.js");return true;}catch(e){handleError(e);}};const validateReducerFunction=fn=>{try{if(!fn)throw new Error("reducer not found in src/js/containers/App/reducer");if(typeof fn!=="function")throw new Error("reducer should present in src/js/containers/App/reducer");return true;}catch(e){handleError(e);}};const validateConfigFile=obj=>{try{if(!obj)throw new Error("config not found in config folder");if(typeof obj!=="object")throw new Error("config object should be exported from config folder inside your project root directory");if(typeof obj==="object"){const requiredConfigKeys={NODE_SERVER_HOSTNAME:"",NODE_SERVER_PORT:"",WEBPACK_DEV_SERVER_HOSTNAME:"",WEBPACK_DEV_SERVER_PORT:"",BUILD_OUTPUT_PATH:"",PUBLIC_STATIC_ASSET_PATH:"",PUBLIC_STATIC_ASSET_URL:"",CLIENT_ENV_VARIABLES:[],ANALYZE_BUNDLE:""};for(let key in requiredConfigKeys){if(!(key in obj))throw new Error(`${key} key not found inside config.json`);}}return true;}catch(e){handleError(e);}};const validatePackageJson=obj=>{try{if(!obj)throw new Error("package.json not found in the project");if(typeof obj!=="object")throw new Error("package.json should be defined in project root directory");return true;}catch(e){handleError(e);}};const validateModuleAlias=obj=>{try{if(!obj)throw new Error("moduleAliases not found in package.json file present in root directory.");if(typeof obj!=="object")throw new Error("moduleAliases named object should be exported from package.json");if(typeof obj==="object"){const requiredModuleAliases={"@api":"api.js","@containers":"src/js/containers","@server":"server","@config":"config","@css":"src/static/css","@routes":"src/js/routes/"};for(let key in requiredModuleAliases){if(key.includes("catalyst"))throw new Error(`Catalyst keyword is restricted for defining aliases`);if(!(key in obj))throw new Error(`${key} module alias not defined inside package.json`);}}return true;}catch(e){handleError(e);}};const validateConfigureStore=fn=>{try{if(!fn)throw new Error("configureStore not found in file src/js/store/index.js");if(typeof fn!=="function")throw new Error("configureStore should be function exported from src/js/store/index.js");return true;}catch(e){handleError(e);}};const validateGetRoutes=fn=>{try{if(!fn)throw new Error("getRoutes not found in file src/js/routes/utils.js");if(typeof fn!=="function")throw new Error("getRoutes should be function exported from src/js/routers/index.js");return true;}catch(e){handleError(e);}};const validateCustomDocument=fn=>{try{if(!fn)throw new Error("document not found in file server/document.js");if(typeof fn!=="function")throw new Error("document should be a react component exported from server/document.js");return true;}catch(e){handleError(e);}};module.exports={safeCall,validateConfigFile,validateConfigureStore,validateCustomDocument,validateGetRoutes,validatePackageJson,validateReducerFunction,validateModuleAlias,validatePreInitServer,validateMiddleware};
1
+ "use strict";const pc=require("picocolors");const handleError=e=>{console.log(pc.red("Failed to start server: "),e);};const safeCall=(fn,...args)=>{try{if(!fn)return;if(typeof fn!=="function"){console.log(pc.red("Invalid lifecycle method defined in server/index.js"));return;}fn(...args);}catch(e){console.log(pc.red("Failed to execute lifecycle method: "),fn.name,e);}};const validatePreInitServer=fn=>{try{if(!fn)throw new Error("preServerInit named function should be defined in server/index.js");if(typeof fn!=="function")throw new Error("preServerInit should be function present in server/index.js");return true;}catch(e){handleError(e);}};const validateMiddleware=fn=>{try{if(!fn)throw new Error("addMiddlewares named function not found in server/server.js");if(typeof fn!=="function")throw new Error("addMiddlewares should be function present in server/server.js");return true;}catch(e){handleError(e);}};const validateReducerFunction=fn=>{try{if(!fn)throw new Error("reducer not found in src/js/containers/App/reducer");if(typeof fn!=="function")throw new Error("reducer should present in src/js/containers/App/reducer");return true;}catch(e){handleError(e);}};const validateConfigFile=obj=>{try{if(!obj)throw new Error("config not found in config folder");if(typeof obj!=="object")throw new Error("config object should be exported from config folder inside your project root directory");if(typeof obj==="object"){const requiredConfigKeys={NODE_SERVER_HOSTNAME:"",NODE_SERVER_PORT:"",WEBPACK_DEV_SERVER_HOSTNAME:"",WEBPACK_DEV_SERVER_PORT:"",BUILD_OUTPUT_PATH:"",PUBLIC_STATIC_ASSET_PATH:"",PUBLIC_STATIC_ASSET_URL:"",CLIENT_ENV_VARIABLES:[],ANALYZE_BUNDLE:""};for(let key in requiredConfigKeys){if(!(key in obj))throw new Error(`${key} key not found inside config.json`);}}return true;}catch(e){handleError(e);}};const validatePackageJson=obj=>{try{if(!obj)throw new Error("package.json not found in the project");if(typeof obj!=="object")throw new Error("package.json should be defined in project root directory");return true;}catch(e){handleError(e);}};const validateModuleAlias=obj=>{try{if(!obj)throw new Error("moduleAliases not found in package.json file present in root directory.");if(typeof obj!=="object")throw new Error("moduleAliases named object should be exported from package.json");if(typeof obj==="object"){const requiredModuleAliases={"@api":"api.js","@containers":"src/js/containers","@server":"server","@config":"config","@css":"src/static/css","@routes":"src/js/routes/"};for(let key in requiredModuleAliases){if(key.includes("catalyst"))throw new Error(`Catalyst keyword is restricted for defining aliases`);if(!(key in obj))throw new Error(`${key} module alias not defined inside package.json`);}}return true;}catch(e){handleError(e);}};const validateConfigureStore=fn=>{try{if(!fn)throw new Error("configureStore not found in file src/js/store/index.js");if(typeof fn!=="function")throw new Error("configureStore should be function exported from src/js/store/index.js");return true;}catch(e){handleError(e);}};const validateGetRoutes=fn=>{try{if(!fn)throw new Error("getRoutes not found in file src/js/routes/utils.js");if(typeof fn!=="function")throw new Error("getRoutes should be function exported from src/js/routers/index.js");return true;}catch(e){handleError(e);}};const validateCustomDocument=fn=>{try{if(!fn)throw new Error("document not found in file server/document.js");if(typeof fn!=="function")throw new Error("document should be a react component exported from server/document.js");return true;}catch(e){handleError(e);}};module.exports={safeCall,validateConfigFile,validateConfigureStore,validateCustomDocument,validateGetRoutes,validatePackageJson,validateReducerFunction,validateModuleAlias,validatePreInitServer,validateMiddleware};
@@ -1,7 +1,8 @@
1
1
  "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=exports.basePlugins=void 0;var _path=_interopRequireDefault(require("path"));var _webpack=_interopRequireDefault(require("webpack"));var _miniCssExtractPlugin=_interopRequireDefault(require("mini-css-extract-plugin"));var _webpackBundleAnalyzer=require("webpack-bundle-analyzer");var _config=_interopRequireDefault(require("@catalyst/root/config.json"));var _package=require("@catalyst/template/package.json");var _scssParams=require("@catalyst/webpack/scssParams");var _babelConfig=_interopRequireDefault(require("@catalyst/webpack/babel.config.ssr"));var _babelConfig2=_interopRequireDefault(require("@catalyst/webpack/babel.config.client"));var _loadEnvironmentVariables=_interopRequireDefault(require("@catalyst/scripts/loadEnvironmentVariables"));var _config2=require("@catalyst/template/config/config.json");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}(0,_loadEnvironmentVariables.default)();const isDev=process.env.NODE_ENV==="development";const isSSR=!!process.env.SSR||false;const basePlugins=exports.basePlugins=[// **This loads process.env variable during webpack build process
2
2
  new _webpack.default.DefinePlugin({"process.env":([..._config2.CLIENT_ENV_VARIABLES,"BUILD_OUTPUT_PATH","PUBLIC_STATIC_ASSET_PATH","PUBLIC_STATIC_ASSET_URL","src_path","PWD","SENTRY_CONFIG"]||[]).reduce((clientEnvMap,env)=>{clientEnvMap[env]=JSON.stringify(process.env[env]);return clientEnvMap;},{})}),// ** This is used to analyze bundle size.
3
3
  _config2.ANALYZE_BUNDLE&&new _webpackBundleAnalyzer.BundleAnalyzerPlugin({generateStatsFile:_config2.ANALYZE_BUNDLE}),new _webpack.default.IgnorePlugin({resourceRegExp:/^\.\/locale$/,contextRegExp:/moment$/})];const{IS_DEV_COMMAND,PUBLIC_STATIC_ASSET_URL,PUBLIC_STATIC_ASSET_PATH,WEBPACK_DEV_SERVER_HOSTNAME,WEBPACK_DEV_SERVER_PORT,NODE_SERVER_HOSTNAME,NODE_SERVER_PORT}=process.env;let publicPath=isDev?`http://${WEBPACK_DEV_SERVER_HOSTNAME}:${WEBPACK_DEV_SERVER_PORT}/`:`${PUBLIC_STATIC_ASSET_URL}${PUBLIC_STATIC_ASSET_PATH}`;// serves assets from local on running devBuild and devServe command
4
- if(IS_DEV_COMMAND==="true"&&!isDev){publicPath=`http://${NODE_SERVER_HOSTNAME}:${NODE_SERVER_PORT}/assets/`;}var _default=exports.default={context:_path.default.resolve(process.env.src_path),mode:isDev?"development":"production",entry:{app:[_path.default.resolve(process.env.src_path,"./client/index.js")]},output:{path:_path.default.join(process.env.src_path,`${process.env.BUILD_OUTPUT_PATH}/public`),filename:process.env.NODE_ENV==="development"?"[name].bundle.js":"[name].[contenthash].js",chunkFilename:process.env.NODE_ENV==="development"?"[name].bundle.js":"[name].[contenthash].js",publicPath:publicPath},resolve:{fallback:{url:require.resolve("url")},extensions:[".js",".jsx",".scss",".ts",".tsx"],alias:Object.keys(_package._moduleAliases||{}).reduce((moduleEnvMap,alias)=>{moduleEnvMap[alias]=_path.default.join(process.env.src_path,..._package._moduleAliases[alias].split("/"));return moduleEnvMap;},{})},plugins:basePlugins,module:{rules:[{test:/\.jsx$|\.js$/,exclude:_path.default.resolve(process.env.src_path,"./node_modules"),use:{loader:"babel-loader",options:isSSR?_babelConfig.default:_babelConfig2.default}},{test:/\.tsx$|\.ts$/,exclude:_path.default.resolve(process.env.src_path,"./node_modules"),use:{loader:"babel-loader",options:isSSR?_babelConfig.default:_babelConfig2.default}},{// This loader processes all the .scss files that should be modularized. This should exclude anything inside node_modules and everything inside src/css/base since they should be globally scoped.
4
+ if(IS_DEV_COMMAND==="true"&&!isDev){publicPath=`http://${NODE_SERVER_HOSTNAME}:${NODE_SERVER_PORT}/assets/`;}var _default=exports.default={context:_path.default.resolve(process.env.src_path),mode:isDev?"development":"production",entry:{app:[_path.default.resolve(process.env.src_path,"./client/index.js")]},output:{path:_path.default.join(process.env.src_path,`${process.env.BUILD_OUTPUT_PATH}/public`),filename:process.env.NODE_ENV==="development"?"[name].bundle.js":"[name].[contenthash].js",chunkFilename:process.env.NODE_ENV==="development"?"[name].bundle.js":"[name].[contenthash].js",publicPath:publicPath},resolve:{fallback:{url:require.resolve("url")},extensions:[".js",".jsx",".scss",".ts",".tsx"],alias:Object.keys(_package._moduleAliases||{}).reduce((moduleEnvMap,alias)=>{// nosemgrep
5
+ moduleEnvMap[alias]=_path.default.join(process.env.src_path,..._package._moduleAliases[alias].split("/"));return moduleEnvMap;},{})},plugins:basePlugins,module:{rules:[{test:/\.jsx$|\.js$/,exclude:_path.default.resolve(process.env.src_path,"./node_modules"),use:{loader:"babel-loader",options:isSSR?_babelConfig.default:_babelConfig2.default}},{test:/\.tsx$|\.ts$/,exclude:_path.default.resolve(process.env.src_path,"./node_modules"),use:{loader:"babel-loader",options:isSSR?_babelConfig.default:_babelConfig2.default}},{// This loader processes all the .scss files that should be modularized. This should exclude anything inside node_modules and everything inside src/css/base since they should be globally scoped.
5
6
  test:/\.scss$/,exclude:[_path.default.resolve(process.env.src_path,"./node_modules"),_path.default.resolve(process.env.src_path,"./src/static/css/base")],use:[isDev&&"css-hot-loader",!isSSR&&_miniCssExtractPlugin.default.loader,{loader:"css-loader",options:{modules:{mode:"local",exportOnlyLocals:!isDev&&isSSR,localIdentName:isDev?_config.default.cssModulesIdentifierDev:_config.default.cssModulesIdentifierProd}}},{loader:"postcss-loader"},{loader:"sass-loader",options:{additionalData:"$font_url: "+(0,_scssParams.fontUrl)()+";"+"$url_for: "+(0,_scssParams.imageUrl)()+";"}},{loader:"sass-resources-loader",options:{resources:[_path.default.resolve(process.env.src_path,"./src/static/css/resources/*.scss")]}}]},{// In development mode, client request app.css ,which has all the css in node_modules and src/static/css/base, This is served by webpack-dev-server. However in prod this css is injected in the doc sent from the server and needs to be global, so we don't pass the files through css-loader to be modularized.
6
7
  test:/\.scss$/,include:[_path.default.resolve(process.env.src_path,"./node_modules"),_path.default.resolve(process.env.src_path,"./src/static/css/base")],use:[isDev&&"css-hot-loader",!isSSR&&_miniCssExtractPlugin.default.loader,{loader:"css-loader"},{loader:"postcss-loader"},{loader:"sass-loader",options:{additionalData:"$font_url: "+(0,_scssParams.fontUrl)()+";"+"$url_for: "+(0,_scssParams.imageUrl)()+";"}},{loader:"sass-resources-loader",options:{resources:[_path.default.resolve(process.env.src_path,"./src/static/css/resources/*.scss")]}}]},{test:/\.css$/,use:[!isSSR&&_miniCssExtractPlugin.default.loader,"css-loader","postcss-loader"]},{test:/\.(png|jpg|gif|jpeg|ico?)$/,use:["url-loader?limit=10240","img-loader"]},{test:/\.svg$/i,issuer:/\.[jt]sx?$/,use:["@svgr/webpack","url-loader?limit=10240","img-loader"]},{// This loader loads fonts in src/static/fonts using file-loader
7
8
  test:/\.(ttf|eot|woff2?)$/,use:[{loader:"url-loader",options:{limit:10240,outputPath:[_path.default.resolve(process.env.src_path,"./src/static/fonts/")]}},"file-loader"]},{// This loader loads html files
@@ -6,7 +6,7 @@ minRemainingSize:1000,// Disable to replicate stand alone chunking for all packa
6
6
  test:/[\\/]node_modules[\\/]/,name(module){const moduleFileName=module.identifier().split("node_modules")?.[1]?.split("/").reverse().slice(0,3).reduce((item,current)=>{item=current+"."+item;return item;},[]);return`npm.${moduleFileName}`;}}}}}});// Create SSR config
7
7
  const webpackSSRConfig=(0,_webpackMerge.mergeWithCustomize)({customizeArray:(0,_webpackMerge.customizeArray)({entry:"replace",optimization:"replace",plugins:"prepend"}),customizeObject:(0,_webpackMerge.customizeObject)({entry:"replace",optimization:"replace",plugins:"prepend"})})(_baseBabel.default,{mode:"development",stats:"none",target:"node",entry:{handler:_path.default.resolve(__dirname,"..","./server/renderer/handler.js")},externals:[/\.(html|png|gif|jpg)$/,(0,_webpackNodeExternals.default)({modulesDir:_path.default.resolve(process.env.src_path,"./node_modules"),allowlist:_webpackConfig.default.transpileModules?_webpackConfig.default.transpileModules:[]}),(0,_webpackNodeExternals.default)({modulesDir:_path.default.join(_appRootPath.default.path,"./node_modules"),allowlist:_webpackConfig.default.transpileModules?_webpackConfig.default.transpileModules:[]})],resolve:{alias:_registerAliases.catalystResultMap},output:{path:_path.default.join(__dirname,"../..",".catalyst-dev","/server","/renderer"),chunkFilename:_config.default.chunkFileName,filename:"handler.development.js",libraryTarget:"commonjs"},plugins:[new _webpackPlugin.default({filename:"loadable-stats.json",writeToDisk:{filename:_path.default.join(__dirname,"../..",".catalyst-dev","/server","/renderer")}}),new _miniCssExtractPlugin.default({filename:_config.default.cssChunkFileName,ignoreOrder:true}),..._webpackConfig.default.ssrPlugins].filter(Boolean)});// Create separate compiler for SSR that writes to disk
8
8
  const ssrCompiler=(0,_webpack.default)(webpackSSRConfig);const watchInstance=ssrCompiler.watch({},err=>{if(err){console.error(err);return;}});// Create dev server for client-side only
9
- let devServer=new _webpackDevServer.default({port:WEBPACK_DEV_SERVER_PORT,host:WEBPACK_DEV_SERVER_HOSTNAME,static:{publicPath:webpackClientConfig.output.publicPath},hot:true,historyApiFallback:true,headers:{"Access-Control-Allow-Origin":"*"},client:{logging:"error",overlay:{errors:false,warnings:false,runtimeErrors:false},reconnect:true}},(0,_webpack.default)(webpackClientConfig));devServer.startCallback(()=>{console.log("Catalyst is compiling your files.");console.log("Please wait until bundling is finished.\n");});// Cleanup on exit
9
+ let devServer=new _webpackDevServer.default({port:WEBPACK_DEV_SERVER_PORT,host:WEBPACK_DEV_SERVER_HOSTNAME,static:{publicPath:webpackClientConfig.output.publicPath},hot:true,historyApiFallback:true,headers:{"Access-Control-Allow-Origin":"*"},client:{logging:"error",overlay:{errors:false,warnings:false,runtimeErrors:false},reconnect:true}},(0,_webpack.default)(webpackClientConfig));devServer.start().then(()=>{console.log("Catalyst is compiling your files.");console.log("Please wait until bundling is finished.\n");}).catch(err=>{console.error("Failed to start webpack-dev-server:",err);process.exit(1);});// Cleanup on exit
10
10
  const cleanup=()=>{// Close webpack watch
11
11
  watchInstance.close(()=>{// Delete the development handler file
12
12
  try{// Delete the file
@@ -38,8 +38,8 @@ function versionOlderThan(a, b) {
38
38
  }
39
39
 
40
40
  /**
41
- * Walk up the directory tree looking for a package.json that depends on catalyst-core.
42
- * Returns { dir, pkg, catalystVersion, installedVersion, versionMeta } or null.
41
+ * Walk up the directory tree looking for a package.json that depends on Catalyst.
42
+ * Returns { dir, pkg, catalystPackageName, catalystVersion, installedVersion, versionMeta } or null.
43
43
  */
44
44
  function findCatalystRoot() {
45
45
  // Walk up from __dirname first (mcp always lives inside the project at .catalyst/mcp/),
@@ -55,7 +55,8 @@ function findCatalystRoot() {
55
55
  try {
56
56
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
57
57
  const deps = { ...pkg.dependencies, ...pkg.devDependencies }
58
- if (deps["catalyst-core"]) {
58
+ const declaredRef = deps["catalyst-core"]
59
+ if (declaredRef) {
59
60
  const nmPath = path.join(dir, "node_modules", "catalyst-core")
60
61
  const installed = fs.existsSync(nmPath)
61
62
  // Read the actual installed version from node_modules
@@ -70,11 +71,11 @@ function findCatalystRoot() {
70
71
  /* ignore */
71
72
  }
72
73
  }
73
- const declaredRef = deps["catalyst-core"]
74
74
  const isGithubRef = declaredRef.startsWith("github:") || declaredRef.includes("#")
75
75
  return {
76
76
  dir,
77
77
  pkg,
78
+ catalystPackageName: "catalyst-core",
78
79
  catalystVersion: declaredRef, // what package.json says (may be github ref)
79
80
  installedVersion, // what's actually in node_modules e.g. "0.0.3-canary.3"
80
81
  notInstalled: !installed,
@@ -100,21 +101,33 @@ function findCatalystRoot() {
100
101
  * File-system helpers scoped to a project root.
101
102
  */
102
103
  function makeProjectHelpers(root) {
104
+ function safeProjectPath(rel) {
105
+ if (typeof rel !== "string" || path.isAbsolute(rel)) return null
106
+ const normalized = path.normalize(rel)
107
+ if (normalized === ".." || normalized.startsWith(`..${path.sep}`)) return null
108
+ return `${root}${path.sep}${normalized}`
109
+ }
110
+
103
111
  function fileExists(rel) {
104
- return fs.existsSync(path.join(root, rel))
112
+ const filePath = safeProjectPath(rel)
113
+ return Boolean(filePath && fs.existsSync(filePath))
105
114
  }
106
115
 
107
116
  function readJson(rel) {
117
+ const filePath = safeProjectPath(rel)
118
+ if (!filePath) return null
108
119
  try {
109
- return JSON.parse(fs.readFileSync(path.join(root, rel), "utf8"))
120
+ return JSON.parse(fs.readFileSync(filePath, "utf8"))
110
121
  } catch {
111
122
  return null
112
123
  }
113
124
  }
114
125
 
115
126
  function readText(rel) {
127
+ const filePath = safeProjectPath(rel)
128
+ if (!filePath) return null
116
129
  try {
117
- return fs.readFileSync(path.join(root, rel), "utf8")
130
+ return fs.readFileSync(filePath, "utf8")
118
131
  } catch {
119
132
  return null
120
133
  }
@@ -124,7 +137,6 @@ function makeProjectHelpers(root) {
124
137
  * Walk src/**\/*.{js,jsx,ts,tsx} and return relative paths that match pattern.
125
138
  */
126
139
  function grepSrc(pattern) {
127
- const re = new RegExp(pattern)
128
140
  const matches = []
129
141
  function walk(dir) {
130
142
  let entries
@@ -135,12 +147,13 @@ function makeProjectHelpers(root) {
135
147
  }
136
148
  for (const e of entries) {
137
149
  if (e.name === "node_modules" || e.name === ".git") continue
138
- const full = path.join(dir, e.name)
150
+ const full = `${dir}${path.sep}${e.name}`
139
151
  if (e.isDirectory()) {
140
152
  walk(full)
141
153
  } else if (/\.(js|jsx|ts|tsx)$/.test(e.name)) {
142
154
  try {
143
- if (re.test(fs.readFileSync(full, "utf8"))) {
155
+ const content = fs.readFileSync(full, "utf8")
156
+ if (pattern.test(content)) {
144
157
  matches.push(path.relative(root, full))
145
158
  }
146
159
  } catch {
@@ -149,11 +162,16 @@ function makeProjectHelpers(root) {
149
162
  }
150
163
  }
151
164
  }
152
- walk(path.join(root, "src"))
165
+ walk(`${root}${path.sep}src`)
153
166
  return matches
154
167
  }
155
168
 
156
169
  return { fileExists, readJson, readText, grepSrc }
157
170
  }
158
171
 
159
- module.exports = { findCatalystRoot, makeProjectHelpers, versionOlderThan, parseVersion }
172
+ module.exports = {
173
+ findCatalystRoot,
174
+ makeProjectHelpers,
175
+ versionOlderThan,
176
+ parseVersion,
177
+ }
package/mcp_v2/mcp.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * Startup validation → module init → JSON-RPC loop over stdio.
6
6
  *
7
7
  * Hard fails if:
8
- * - No catalyst-core in package.json (not a catalyst project)
8
+ * - No Catalyst package in package.json (not a catalyst project)
9
9
  * - context.db missing (setup.js not run)
10
10
  *
11
11
  * Tool routing: LLM reads tool name + description to route.
@@ -39,7 +39,8 @@ if (!projectInfo) {
39
39
  jsonrpc: "2.0",
40
40
  error: {
41
41
  code: -32000,
42
- message: "Not a catalyst-core project. No catalyst-core dependency found in package.json.",
42
+ message:
43
+ "Not a Catalyst project. No catalyst-core or catalyst-core-internal dependency found in package.json.",
43
44
  },
44
45
  id: null,
45
46
  }) + "\n"
@@ -52,7 +53,7 @@ if (projectInfo.notInstalled) {
52
53
  jsonrpc: "2.0",
53
54
  error: {
54
55
  code: -32000,
55
- message: `catalyst-core is listed in package.json (${projectInfo.catalystVersion}) but not installed in node_modules. Run: npm install`,
56
+ message: `${projectInfo.catalystPackageName} is listed in package.json (${projectInfo.catalystVersion}) but not installed in node_modules. Run: npm install`,
56
57
  },
57
58
  id: null,
58
59
  }) + "\n"
package/mcp_v2/setup.js CHANGED
@@ -33,7 +33,7 @@ function findCatalystRoot() {
33
33
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
34
34
  const deps = { ...pkg.dependencies, ...pkg.devDependencies }
35
35
  if (deps["catalyst-core"]) {
36
- return { dir, pkg, version: deps["catalyst-core"] }
36
+ return { dir, pkg, catalystPackageName: "catalyst-core", version: deps["catalyst-core"] }
37
37
  }
38
38
  }
39
39
  dir = path.dirname(dir)
@@ -119,7 +119,9 @@ function seedKnowledgeBase(db, projectInfo) {
119
119
  package_name: projectInfo.pkg.name || "unknown",
120
120
  catalyst_version: projectInfo.version,
121
121
  })
122
- console.log(` ✓ Project context stored (${projectInfo.pkg.name}, catalyst-core@${projectInfo.version})`)
122
+ console.log(
123
+ ` ✓ Project context stored (${projectInfo.pkg.name}, ${projectInfo.catalystPackageName}@${projectInfo.version})`
124
+ )
123
125
  }
124
126
 
125
127
  // ── 5. sync_catalyst_docs (initial run) ───────────────────────────────────────
@@ -237,7 +239,7 @@ async function main() {
237
239
  process.exit(1)
238
240
  }
239
241
  console.log(`✓ Catalyst project: ${projectInfo.pkg.name || projectInfo.dir}`)
240
- console.log(` catalyst-core@${projectInfo.version}`)
242
+ console.log(` ${projectInfo.catalystPackageName}@${projectInfo.version}`)
241
243
 
242
244
  // 2. Init DB
243
245
  console.log("\nInitializing context.db...")
@@ -658,7 +658,7 @@ function handle_get_architecture_diagram({ feature, symptom } = {}) {
658
658
 
659
659
  if (diagram_key === "routing") {
660
660
  const { grepSrc } = makeProjectHelpers(catalystRoot.dir)
661
- const routeFiles = grepSrc("RouterDataProvider|createBrowserRouter|<Route")
661
+ const routeFiles = grepSrc(/RouterDataProvider|createBrowserRouter|<Route/)
662
662
  project_context.push(
663
663
  `RouterDataProvider found in: ${routeFiles.length ? routeFiles.slice(0, 3).join(", ") : "not detected"}`
664
664
  )
@@ -8,6 +8,14 @@ function init(projectInfo) {
8
8
  _projectInfo = projectInfo
9
9
  }
10
10
 
11
+ function projectPath(root, rel) {
12
+ const normalized = path.normalize(rel)
13
+ if (path.isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${path.sep}`)) {
14
+ return null
15
+ }
16
+ return `${root}${path.sep}${normalized}`
17
+ }
18
+
11
19
  function handle_check_config({ project_path, platform = "both" } = {}) {
12
20
  const root = project_path || _projectInfo.dir
13
21
  const configPath = path.join(root, "config", "config.json")
@@ -390,7 +398,10 @@ function handle_check_config({ project_path, platform = "both" } = {}) {
390
398
  })
391
399
  } else {
392
400
  const missingSplash = ["public/android/splashscreen.png", "public/ios/splashscreen.png"].filter(
393
- (f) => !fs.existsSync(path.join(root, f))
401
+ (f) => {
402
+ const filePath = projectPath(root, f)
403
+ return !filePath || !fs.existsSync(filePath)
404
+ }
394
405
  )
395
406
  if (missingSplash.length) {
396
407
  issues.push({
@@ -405,9 +416,10 @@ function handle_check_config({ project_path, platform = "both" } = {}) {
405
416
 
406
417
  // ── notifications (optional, validate if present) ─────────────────────────
407
418
  if (wc.notifications && wc.notifications.enabled) {
408
- const missingFirebase = ["google-services.json", "GoogleService-Info.plist"].filter(
409
- (f) => !fs.existsSync(path.join(root, f))
410
- )
419
+ const missingFirebase = ["google-services.json", "GoogleService-Info.plist"].filter((f) => {
420
+ const filePath = projectPath(root, f)
421
+ return !filePath || !fs.existsSync(filePath)
422
+ })
411
423
  if (missingFirebase.length) {
412
424
  issues.push({
413
425
  field: "WEBVIEW_CONFIG.notifications",
@@ -113,9 +113,9 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
113
113
 
114
114
  T4_DATA_FETCHING: () => {
115
115
  const useEffectFiles = grepSrc(
116
- "useEffect.*fetch|useEffect.*axios|useEffect.*\\.get\\(|useEffect.*\\.post\\("
116
+ /useEffect.*fetch|useEffect.*axios|useEffect.*\.get\(|useEffect.*\.post\(/
117
117
  )
118
- const fetcherFiles = grepSrc("\\.serverFetcher\\s*=|\\.clientFetcher\\s*=")
118
+ const fetcherFiles = grepSrc(/\.serverFetcher\s*=|\.clientFetcher\s*=/)
119
119
  if (useEffectFiles.length === 0) return { status: "completed" }
120
120
  return {
121
121
  status: "needs_review",
@@ -368,13 +368,13 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
368
368
  // ── Tier 3: Enhancements (feature-presence gated) ───────────────────────
369
369
 
370
370
  T17a_USE_FILEPICKER: () => {
371
- const oldPatternFiles = grepSrc("<input[^>]*type=['\"]file['\"]")
371
+ const oldPatternFiles = grepSrc(/<input[^>]*type=['"]file['"]/)
372
372
  if (oldPatternFiles.length === 0)
373
373
  return {
374
374
  status: "not_applicable",
375
375
  reason: 'No <input type="file"> found in project — file upload feature not used.',
376
376
  }
377
- const hookUsageFiles = grepSrc("useFilePicker")
377
+ const hookUsageFiles = grepSrc(/useFilePicker/)
378
378
  return {
379
379
  status: "needs_review",
380
380
  native_risk:
@@ -396,13 +396,13 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
396
396
  },
397
397
 
398
398
  T17b_USE_CAMERA: () => {
399
- const oldPatternFiles = grepSrc("accept=['\"]image[^>]*capture|<input[^>]+capture")
399
+ const oldPatternFiles = grepSrc(/accept=['"]image[^>]*capture|<input[^>]+capture/)
400
400
  if (oldPatternFiles.length === 0)
401
401
  return {
402
402
  status: "not_applicable",
403
403
  reason: "No camera capture inputs (<input capture>) found — camera feature not used.",
404
404
  }
405
- const hookUsageFiles = grepSrc("useCamera")
405
+ const hookUsageFiles = grepSrc(/useCamera/)
406
406
  return {
407
407
  status: "needs_review",
408
408
  native_risk:
@@ -424,13 +424,13 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
424
424
  },
425
425
 
426
426
  T18_USE_HAPTIC: () => {
427
- const oldPatternFiles = grepSrc("navigator\\.vibrate")
427
+ const oldPatternFiles = grepSrc(/navigator\.vibrate/)
428
428
  if (oldPatternFiles.length === 0)
429
429
  return {
430
430
  status: "not_applicable",
431
431
  reason: "No navigator.vibrate() calls found — haptic feedback not used.",
432
432
  }
433
- const hookUsageFiles = grepSrc("useHapticFeedback")
433
+ const hookUsageFiles = grepSrc(/useHapticFeedback/)
434
434
  return {
435
435
  status: "needs_review",
436
436
  native_risk:
@@ -453,7 +453,7 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
453
453
 
454
454
  T19_USE_NOTIFICATIONS: () => {
455
455
  if (!webviewConfig || !webviewConfig.notifications || !webviewConfig.notifications.enabled) {
456
- const pushFiles = grepSrc("firebase|FCM|pushNotif|useNotification")
456
+ const pushFiles = grepSrc(/firebase|FCM|pushNotif|useNotification/)
457
457
  if (pushFiles.length === 0)
458
458
  return {
459
459
  status: "not_applicable",
@@ -480,7 +480,7 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
480
480
 
481
481
  T20_USE_DEVICE_INFO: () => {
482
482
  const oldPatternFiles = grepSrc(
483
- "navigator\\.userAgent|/Android/i\\.test|/iPhone/i\\.test|/iPad/i\\.test"
483
+ /navigator\.userAgent|\/Android\/i\.test|\/iPhone\/i\.test|\/iPad\/i\.test/
484
484
  )
485
485
  if (oldPatternFiles.length === 0)
486
486
  return {
@@ -488,7 +488,7 @@ function handle_get_conversion_status({ project_path, include_not_applicable = f
488
488
  reason: "No navigator.userAgent / UA-sniffing found — platform detection not used.",
489
489
  }
490
490
  const correctPatternFiles = grepSrc(
491
- "isNative|window\\.NativeBridge|nativeBridge\\.isAndroid|nativeBridge\\.isIOS"
491
+ /isNative|window\.NativeBridge|nativeBridge\.isAndroid|nativeBridge\.isIOS/
492
492
  )
493
493
  return {
494
494
  status: "needs_review",
@@ -55,11 +55,20 @@ function fetchRaw(url) {
55
55
  })
56
56
  }
57
57
 
58
+ // Only allow known tag shapes or "main" — guards against a tampered/malformed
59
+ // installed version string being interpolated into the GitHub fetch URL.
60
+ // Covers all observed formats:
61
+ // v0.1.0, v0.1.0-canary.8, v0.0.3-canary-18, v0.1.0-beta.2
62
+ function sanitizeGithubTag(version) {
63
+ if (!version) return "main"
64
+ return /^v\d+\.\d+\.\d+(-[a-zA-Z0-9]+([.\-]\d+)?)?$/.test(version) ? version : "main"
65
+ }
66
+
58
67
  // Fetch first available file — used for KB-miss GitHub fallback
59
68
  async function fetchFromGithub(files, installedVersion) {
60
69
  if (!files || files.length === 0) return null
61
70
 
62
- const tag = installedVersion || "main"
71
+ const tag = sanitizeGithubTag(installedVersion)
63
72
  const base = `https://raw.githubusercontent.com/tata1mg/catalyst-core/${tag}`
64
73
 
65
74
  for (const file of files) {
@@ -88,9 +97,13 @@ async function fetchFromGithub(files, installedVersion) {
88
97
  return null
89
98
  }
90
99
 
91
- // Build FTS5 query string from keywords array — each term ORed, quoted for safety
100
+ // Build FTS5 query string from keywords array — each term ORed, quoted for safety.
101
+ // Strip all FTS5 special characters before quoting so keywords are treated as literals.
92
102
  function buildFtsQuery(keywords) {
93
- return keywords.map((k) => `"${k.replace(/"/g, "")}"`).join(" OR ")
103
+ return keywords
104
+ .map((k) => `"${k.replace(/[^a-zA-Z0-9 _\-]/g, " ").trim()}"`)
105
+ .filter((k) => k.length > 2) // drop empty/whitespace-only terms after stripping
106
+ .join(" OR ")
94
107
  }
95
108
 
96
109
  function stripGithubFiles(rows) {
@@ -26,6 +26,14 @@ function now() {
26
26
  return new Date().toISOString().replace("T", " ").slice(0, 19)
27
27
  }
28
28
 
29
+ function projectPath(projectRoot, rel) {
30
+ const normalized = path.normalize(rel)
31
+ if (path.isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${path.sep}`)) {
32
+ return null
33
+ }
34
+ return `${projectRoot}${path.sep}${normalized}`
35
+ }
36
+
29
37
  // ─── Improvement 1: Resolve needs_review inline before plan write ─────────────
30
38
  //
31
39
  // Reads signal_files from disk for each needs_review task.
@@ -67,7 +75,8 @@ function resolveNeedsReview(task, projectRoot) {
67
75
  let nativeBranchFound = false
68
76
  for (const f of samplesToCheck) {
69
77
  try {
70
- const absPath = path.isAbsolute(f) ? f : path.join(projectRoot, f)
78
+ const absPath = path.isAbsolute(f) ? f : projectPath(projectRoot, f)
79
+ if (!absPath) continue
71
80
  const content = fs.readFileSync(absPath, "utf8")
72
81
  if (hookRegex && hookRegex.test(content)) {
73
82
  nativeBranchFound = true
@@ -411,12 +420,12 @@ function summarisePlan(plan) {
411
420
  // ─── MD file helpers ─────────────────────────────────────────────────────────
412
421
 
413
422
  function getMdPath(projectRoot, slug) {
414
- return path.join(projectRoot, ".mcp_tasks", `${slug}.md`)
423
+ return `${projectRoot}${path.sep}.mcp_tasks${path.sep}${slug}.md`
415
424
  }
416
425
 
417
426
  function buildUserReviewWarnings(projectRoot) {
418
427
  const warnings = []
419
- const configPath = path.join(projectRoot, "config", "config.json")
428
+ const configPath = `${projectRoot}${path.sep}config${path.sep}config.json`
420
429
  let config = null
421
430
  try {
422
431
  config = JSON.parse(fs.readFileSync(configPath, "utf8"))
@@ -463,7 +472,7 @@ function buildUserReviewWarnings(projectRoot) {
463
472
  }
464
473
 
465
474
  function writeMdFile(projectRoot, slug, goal, steps, bareMinimum, userReviewWarnings) {
466
- const dir = path.join(projectRoot, ".mcp_tasks")
475
+ const dir = `${projectRoot}${path.sep}.mcp_tasks`
467
476
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
468
477
 
469
478
  const mdPath = getMdPath(projectRoot, slug)
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "catalyst-core",
3
- "version": "0.1.0-beta.1",
4
- "main": "index.js",
3
+ "version": "0.1.0-beta.3",
4
+ "main": "dist/index.js",
5
5
  "description": "Web framework that provides great performance out of the box",
6
6
  "bin": {
7
7
  "catalyst": "bin/catalyst.js"
8
8
  },
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/tata1mg/catalyst-core.git"
11
+ "url": "https://github.com/tata1mg/catalyst-core.git",
12
+ "directory": "packages/catalyst-core"
12
13
  },
13
14
  "_moduleAliases": {
14
15
  "@catalyst/template": ".",
@@ -37,10 +38,10 @@
37
38
  },
38
39
  "scripts": {
39
40
  "lint": "eslint .",
40
- "lint-staged": "lint-staged",
41
41
  "prettify": "prettier . --write",
42
42
  "prepare": "babel src --out-dir ./dist --ignore '**/.build/**' && mkdir -p dist/native && cp -r src/native/androidProject src/native/build.swift src/native/assets dist/native/ && mkdir -p dist/native/iosnativeWebView && tar -C src/native/iosnativeWebView --exclude='.build' --exclude='.package-config-hash' -cf - . | tar -C dist/native/iosnativeWebView -xf -",
43
- "prepublishOnly": "npm i && npm run prepare"
43
+ "prepublishOnly": "npm run prepare",
44
+ "test:fixture": "sh ../../scripts/test-catalyst-core.sh"
44
45
  },
45
46
  "license": "MIT",
46
47
  "dependencies": {
@@ -96,7 +97,7 @@
96
97
  "webpack": "^5.88.2",
97
98
  "webpack-bundle-analyzer": "^4.9.1",
98
99
  "webpack-cli": "^5.1.4",
99
- "webpack-dev-server": "^4.15.2",
100
+ "webpack-dev-server": "^5.2.4",
100
101
  "webpack-merge": "^5.9.0",
101
102
  "webpack-node-externals": "^3.0.0",
102
103
  "winston": "^3.11.0",
@@ -113,14 +114,6 @@
113
114
  "eslint-plugin-react-hooks": "^4.6.0",
114
115
  "eslint-plugin-risxss": "^2.1.0",
115
116
  "eslint-plugin-security": "^3.0.0",
116
- "husky": "^9.0.11",
117
- "lint-staged": "^15.2.2",
118
117
  "prettier": "^3.2.5"
119
- },
120
- "lint-staged": {
121
- "*.{js,jsx}": [
122
- "eslint .",
123
- "prettier . --write"
124
- ]
125
118
  }
126
119
  }
@@ -1,4 +0,0 @@
1
- {
2
- "tag": "beta",
3
- "version": "0.1.0-beta.1"
4
- }
package/license DELETED
@@ -1,10 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 1mg Healthcare Solutions Private Limited.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
-
7
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
-
9
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
-