catalyst-core 0.1.0-beta.2 → 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.
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
 
@@ -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
@@ -101,21 +101,33 @@ function findCatalystRoot() {
101
101
  * File-system helpers scoped to a project root.
102
102
  */
103
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
+
104
111
  function fileExists(rel) {
105
- return fs.existsSync(path.join(root, rel))
112
+ const filePath = safeProjectPath(rel)
113
+ return Boolean(filePath && fs.existsSync(filePath))
106
114
  }
107
115
 
108
116
  function readJson(rel) {
117
+ const filePath = safeProjectPath(rel)
118
+ if (!filePath) return null
109
119
  try {
110
- return JSON.parse(fs.readFileSync(path.join(root, rel), "utf8"))
120
+ return JSON.parse(fs.readFileSync(filePath, "utf8"))
111
121
  } catch {
112
122
  return null
113
123
  }
114
124
  }
115
125
 
116
126
  function readText(rel) {
127
+ const filePath = safeProjectPath(rel)
128
+ if (!filePath) return null
117
129
  try {
118
- return fs.readFileSync(path.join(root, rel), "utf8")
130
+ return fs.readFileSync(filePath, "utf8")
119
131
  } catch {
120
132
  return null
121
133
  }
@@ -125,7 +137,6 @@ function makeProjectHelpers(root) {
125
137
  * Walk src/**\/*.{js,jsx,ts,tsx} and return relative paths that match pattern.
126
138
  */
127
139
  function grepSrc(pattern) {
128
- const re = new RegExp(pattern)
129
140
  const matches = []
130
141
  function walk(dir) {
131
142
  let entries
@@ -136,12 +147,13 @@ function makeProjectHelpers(root) {
136
147
  }
137
148
  for (const e of entries) {
138
149
  if (e.name === "node_modules" || e.name === ".git") continue
139
- const full = path.join(dir, e.name)
150
+ const full = `${dir}${path.sep}${e.name}`
140
151
  if (e.isDirectory()) {
141
152
  walk(full)
142
153
  } else if (/\.(js|jsx|ts|tsx)$/.test(e.name)) {
143
154
  try {
144
- if (re.test(fs.readFileSync(full, "utf8"))) {
155
+ const content = fs.readFileSync(full, "utf8")
156
+ if (pattern.test(content)) {
145
157
  matches.push(path.relative(root, full))
146
158
  }
147
159
  } catch {
@@ -150,7 +162,7 @@ function makeProjectHelpers(root) {
150
162
  }
151
163
  }
152
164
  }
153
- walk(path.join(root, "src"))
165
+ walk(`${root}${path.sep}src`)
154
166
  return matches
155
167
  }
156
168
 
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "catalyst-core",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.3",
4
4
  "main": "dist/index.js",
5
5
  "description": "Web framework that provides great performance out of the box",
6
6
  "bin": {
@@ -97,7 +97,7 @@
97
97
  "webpack": "^5.88.2",
98
98
  "webpack-bundle-analyzer": "^4.9.1",
99
99
  "webpack-cli": "^5.1.4",
100
- "webpack-dev-server": "^4.15.2",
100
+ "webpack-dev-server": "^5.2.4",
101
101
  "webpack-merge": "^5.9.0",
102
102
  "webpack-node-externals": "^3.0.0",
103
103
  "winston": "^3.11.0",