catalyst-core 0.0.3-canary.5 → 0.0.3-canary.6

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/changelog.md CHANGED
@@ -1,20 +1,34 @@
1
1
  # Changelog
2
2
 
3
3
 
4
- ## [0.0.3-canary.5] - 12-06-2025
4
+ ## [0.0.3-canary.6] - 12-06-2025
5
5
 
6
- ### Features
7
- - Production project creation functionality implemented
8
- - Android App Bundle (.aab) file generation capability
9
- - Project name replacement functionality
10
6
 
11
- ### Fixed
12
- - Production deployment issues resolved
13
- - Merge conflicts fixed
14
- - Branch integration improvements
7
+ This update specifically targets production deployment workflows with:
15
8
 
16
- ### Changes
17
- - Enhanced production deployment workflow for Android
18
- - Improved build process for production releases
9
+ • Automated IP Resolution: No more manual IP configuration for development
10
+ • Streamlined Build Process: One-command builds for both Android and iOS
11
+ • Better Error Handling: More informative error messages and recovery options
12
+ • CLI-First Approach: Enhanced command-line interface for CI/CD integration
13
+
14
+ 🧪 Testing & Validation
15
+
16
+ • ✅ Android Build: Verified APK generation and installation
17
+ • ✅ IP Detection: Tested automatic IP address resolution on macOS
18
+ • ✅ Configuration: Validated dynamic config updates
19
+ • ✅ CLI Operations: Tested enhanced command-line workflows
20
+
21
+ 🔄 Breaking Changes
22
+ • None: All changes are backward compatible
23
+ • Enhanced: Existing functionality improved without breaking existing workflows
24
+
25
+ 📝 Commit History
26
+ • cc4584e: feat: finishing cli and updating ip address updation
27
+ • 0076d8c: feat: adding cli option and upgrading gradle
28
+
29
+ 🔗 Related
30
+ • Base Branch: universal_main
31
+ • Target: Production deployment automation
32
+ • Platform: Universal (Android + iOS)
19
33
  ## Version
20
- - Target version: 0.0.3-canary.5
34
+ - Target version: 0.0.3-canary.6
@@ -239,15 +239,12 @@ tasks.register("generateWebViewConfig") {
239
239
  val assetsDir = File("${project.projectDir}/src/main/assets")
240
240
  if (!assetsDir.exists()) {
241
241
  assetsDir.mkdirs()
242
- println("Created assets directory at ${assetsDir.absolutePath}")
243
242
  }
244
243
 
245
244
  // Write to properties file
246
245
  File(assetsDir, "webview_config.properties").outputStream().use {
247
246
  properties.store(it, "WebView Configuration")
248
247
  }
249
-
250
- println("WebView config generated at ${assetsDir.absolutePath}/webview_config.properties")
251
248
  }
252
249
  }
253
250
 
@@ -1,5 +1,5 @@
1
1
  [versions]
2
- agp = "8.9.1"
2
+ agp = "8.10.1"
3
3
  kotlin = "1.9.0"
4
4
  coreKtx = "1.13.1"
5
5
  junit = "4.13.2"
@@ -1,28 +1,31 @@
1
- "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 pwd=`${process.cwd()}/node_modules/catalyst-core/dist/native`;const ANDROID_PACKAGE="com.example.androidProject";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}=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
1
+ "use strict";var _renameAndroidProject=require("./renameAndroidProject.js");var _setupServer=require("./setupServer.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 pwd=`${process.cwd()}/node_modules/catalyst-core/dist/native`;const ANDROID_PACKAGE="io.yourname.androidproject";// Default values for AAB building
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
+ 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}=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
3
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};}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
4
6
  const buildType=androidConfig.buildType||'debug';if(buildType!=='release'){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 validation successful','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}`);}}else{progress.log('Skipping emulator validation for release build','info');}progress.log('Android tools validation completed successfully!','success');return{ANDROID_SDK,ADB_PATH,EMULATOR_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 status: '+error.message,'error');return false;}}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,stdout,stderr)=>{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
5
7
  const sourcePath=`${process.env.PWD}/build/`;const destPath=`${pwd}/androidProject/app/src/main/assets/build/`;// Create destination directory if it doesn't exist
6
8
  (0,_utils.runCommand)(`mkdir -p ${destPath}`);// Clear existing destination to avoid conflicts
7
9
  (0,_utils.runCommand)(`rm -rf ${destPath}/*`);// Files to exclude from copying
8
10
  const excludePatterns=['route-manifest.json.gz','route-manifest.json.br'];if(buildOptimisation){progress.log('Running with build optimization...','info');const excludeParams=excludePatterns.map(pattern=>`--exclude="${pattern}"`).join(' ');const rsyncCommand=`rsync -av ${excludeParams} ${sourcePath} ${destPath}`;progress.log('Executing rsync command with exclusions...','info');(0,_utils.runCommand)(rsyncCommand);// Verify excluded files don't exist in destination
9
- for(const pattern of excludePatterns){const checkCommand=`find ${destPath} -name "${pattern}" | wc -l`;const count=parseInt((0,_utils.runCommand)(checkCommand).trim(),10);if(count>0){progress.log(`Warning: Found ${count} instances of excluded file ${pattern}`,'warning');(0,_utils.runCommand)(`find ${destPath} -name "${pattern}" -delete`);}}progress.log('Build assets copied with optimization (excluded route-manifest JSON files)','success');}else{progress.log('Running without build optimization...','info');const exclusions=excludePatterns.map(pattern=>`-not -name "${pattern}"`).join(' ');const copyCommand=`find ${sourcePath} -type f ${exclusions} -exec cp --parents {} ${destPath} \\;`;progress.log(`Executing copy command with exclusions...`,'info');(0,_utils.runCommand)(copyCommand);progress.log('Build assets copied successfully!','success');}}catch(error){throw new Error('Error copying build assets: '+error.message);}}async function installApp(ADB_PATH,androidConfig,buildOptimisation){progress.log('Building and installing app...','info');try{const buildCommand=`cd ${pwd}/androidProject && ./gradlew generateWebViewConfig -PconfigPath=${configPath} -PbuildOptimisation=${buildOptimisation} && ./gradlew clean installDebug && ${ADB_PATH} shell monkey -p ${ANDROID_PACKAGE} 1`;await(0,_utils.runInteractiveCommand)('sh',['-c',buildCommand],{'BUILD SUCCESSFUL':''});progress.log('Installation completed successfully!','success');}catch(error){throw new Error('Error installing app: '+error.message);}}async function createAABConfig(androidConfig){// Create AAB configuration based on WebView config
10
- const aabConfig={android:{oldProjectName:"androidProject",// Default project name in catalyst
11
- newProjectName:androidConfig.appName||androidConfig.packageName?.split('.').pop()||"catalystapp",projectPath:`${pwd}/androidProject`,createSignedAAB:true,outputPath:androidConfig.outputPath||`${process.env.PWD}/build-output`,overwriteExisting:true}};// Add keystore configuration if available
12
- if(androidConfig.keystoreConfig){aabConfig.android.keystoreConfig=androidConfig.keystoreConfig;}else if(androidConfig.keystore){// Map old keystore format to new format
13
- aabConfig.android.keystoreConfig={keyAlias:androidConfig.keystore.alias||"release",storePassword:androidConfig.keystore.storePassword,keyPassword:androidConfig.keystore.keyPassword,validityYears:25,organizationInfo:{companyName:androidConfig.keystore.organizationName||"YourCompany",city:androidConfig.keystore.city||"YourCity",state:androidConfig.keystore.state||"YourState",countryCode:androidConfig.keystore.countryCode||"US"}};}// Write temporary config file for AAB builder
14
- const tempConfigPath=`${process.env.PWD}/temp-aab-config.json`;_fs.default.writeFileSync(tempConfigPath,JSON.stringify(aabConfig,null,2));return tempConfigPath;}async function buildSignedAAB(androidConfig){progress.log('Building signed AAB for release...','info');try{// Create AAB configuration
15
- const tempConfigPath=await createAABConfig(androidConfig);// Call the AAB builder
16
- await(0,_renameAndroidProject.buildAndroidAAB)(tempConfigPath);// Clean up temporary config
17
- if(_fs.default.existsSync(tempConfigPath)){_fs.default.unlinkSync(tempConfigPath);}progress.log('Signed AAB build completed successfully!','success');}catch(error){throw new Error('Error building signed AAB: '+error.message);}}async function buildAndroidApp(){// Initialize androidConfig outside try block to ensure it's available in catch
18
- let androidConfig=null;try{// Initialize configuration
11
+ for(const pattern of excludePatterns){const checkCommand=`find ${destPath} -name "${pattern}" | wc -l`;const count=parseInt((0,_utils.runCommand)(checkCommand).trim(),10);if(count>0){progress.log(`Warning: Found ${count} instances of excluded file ${pattern}`,'warning');(0,_utils.runCommand)(`find ${destPath} -name "${pattern}" -delete`);}}progress.log('Build assets copied with optimization (excluded route-manifest JSON files)','success');}else{progress.log('Running without build optimization...','info');const exclusions=excludePatterns.map(pattern=>`-not -name "${pattern}"`).join(' ');const copyCommand=`find ${sourcePath} -type f ${exclusions} -exec cp --parents {} ${destPath} \\;`;progress.log(`Executing copy command with exclusions...`,'info');(0,_utils.runCommand)(copyCommand);progress.log('Build assets copied successfully!','success');}}catch(error){throw new Error('Error copying build assets: '+error.message);}}async function installApp(ADB_PATH,androidConfig,buildOptimisation,buildType='debug'){progress.log('Building and installing app...','info');try{const buildCommand=`cd ${pwd}/androidProject && ./gradlew generateWebViewConfig -PconfigPath=${configPath} -PbuildOptimisation=${buildOptimisation} && ./gradlew clean installDebug --quiet --console=rich && ${ADB_PATH} shell monkey -p ${ANDROID_PACKAGE}${buildType==='debug'?'.debug':''} 1`;await(0,_utils.runInteractiveCommand)('sh',['-c',buildCommand],{'BUILD SUCCESSFUL':''});progress.log('Installation completed successfully!','success');}catch(error){throw new Error('Error installing app: '+error.message);}}async function createAABConfig(androidConfig){// Create AAB configuration based on WebView config with defaults applied
12
+ const aabConfig={// Apply defaults here in buildAppAndroid.js
13
+ projectPath:androidConfig.projectPath||DEFAULT_PROJECT_PATH,deploymentPath:DEFAULT_DEPLOYMENT_PATH,oldProjectName:DEFAULT_OLD_PROJECT_NAME,overwriteExisting:androidConfig.overwriteExisting!==undefined?androidConfig.overwriteExisting:DEFAULT_OVERWRITE_EXISTING,// Required field - use configured value or derive from other config
14
+ newProjectName:androidConfig.newProjectName||androidConfig.appName||androidConfig.packageName?.split('.').pop()||"catalystapp",// AAB specific settings
15
+ createSignedAAB:true,outputPath:androidConfig.outputPath||`${process.env.PWD}/build-output`};// Add keystore configuration if available
16
+ if(androidConfig.keystoreConfig){aabConfig.keystoreConfig=androidConfig.keystoreConfig;}else if(androidConfig.keystore){// Map old keystore format to new format
17
+ aabConfig.keystoreConfig={keyAlias:androidConfig.keystore.alias||"release",storePassword:androidConfig.keystore.storePassword,keyPassword:androidConfig.keystore.keyPassword,validityYears:25,organizationInfo:{companyName:androidConfig.keystore.organizationName||"YourCompany",city:androidConfig.keystore.city||"YourCity",state:androidConfig.keystore.state||"YourState",countryCode:androidConfig.keystore.countryCode||"US"}};}// Log the configuration being used
18
+ progress.log(`AAB Configuration:`,'info');progress.log(` Project Path: ${aabConfig.projectPath} ${aabConfig.projectPath===DEFAULT_PROJECT_PATH?'(default)':'(configured)'}`,'info');progress.log(` Deployment Path: ${aabConfig.deploymentPath} ${aabConfig.deploymentPath===DEFAULT_DEPLOYMENT_PATH?'(default)':'(configured)'}`,'info');progress.log(` Old Project Name: ${aabConfig.oldProjectName} ${aabConfig.oldProjectName===DEFAULT_OLD_PROJECT_NAME?'(default)':'(configured)'}`,'info');progress.log(` New Project Name: ${aabConfig.newProjectName}`,'info');progress.log(` Overwrite Existing: ${aabConfig.overwriteExisting} ${aabConfig.overwriteExisting===DEFAULT_OVERWRITE_EXISTING?'(default)':'(configured)'}`,'info');progress.log(` Output Path: ${aabConfig.outputPath}`,'info');return aabConfig;}async function buildSignedAAB(androidConfig){progress.log('Building signed AAB for release...','info');try{// Create AAB configuration directly without temporary file
19
+ const aabConfig=await createAABConfig(androidConfig);// Call the AAB builder directly with config object
20
+ await(0,_renameAndroidProject.buildAndroidAAB)(aabConfig);progress.log('Signed AAB build completed successfully!','success');}catch(error){throw new Error('Error building signed AAB: '+error.message);}}async function buildAndroidApp(){// Initialize androidConfig outside try block to ensure it's available in catch
21
+ let androidConfig=null;try{await(0,_setupServer.setupServer)(configPath);// Initialize configuration
19
22
  progress.start('config');const{WEBVIEW_CONFIG}=await initializeConfig();androidConfig=WEBVIEW_CONFIG.android;const buildType=androidConfig.buildType||'debug';const buildOptimisation=!!androidConfig.buildOptimisation||false;progress.complete('config');// Validate tools and get paths
20
23
  progress.start('tools');const{ANDROID_SDK,ADB_PATH,EMULATOR_PATH}=await validateAndroidTools(androidConfig);progress.complete('tools');// Handle emulator for debug builds only
21
24
  if(buildType!=='release'){progress.start('emulator');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');}progress.complete('emulator');}else{progress.log('Skipping emulator setup for release build','info');}// Copy build assets
22
25
  progress.start('copyAssets');await copyBuildAssets(androidConfig,buildOptimisation);progress.log(`Build optimization: ${buildOptimisation?'Enabled':'Disabled'}`,'info');progress.complete('copyAssets');// Build based on type
23
26
  if(buildType==='release'){// Build signed AAB for release
24
27
  progress.start('aab');await buildSignedAAB(androidConfig);progress.complete('aab');}else{// Install debug app for development
25
- progress.start('build');await installApp(ADB_PATH,androidConfig,buildOptimisation);progress.complete('build');}// Print build summary
28
+ progress.start('build');await installApp(ADB_PATH,androidConfig,buildOptimisation,buildType);progress.complete('build');}// Print build summary
26
29
  const summaryItems=['Build completed successfully:',{text:`Build Type: ${buildType}`,indent:1,prefix:'├─ ',color:'gray'},{text:`SDK Path: ${androidConfig.sdkPath}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Build Optimization: ${buildOptimisation?'Enabled':'Disabled'}`,indent:1,prefix:'├─ ',color:'gray'}];if(buildType==='release'){summaryItems.push({text:`Output: Signed AAB generated in build-output/`,indent:1,prefix:'└─ ',color:'green'});}else{summaryItems.push({text:`Emulator: ${androidConfig.emulatorName}`,indent:1,prefix:'└─ ',color:'gray'});}progress.printTreeContent('Build Summary',summaryItems);process.exit(0);}catch(error){if(progress.currentStep){progress.fail(progress.currentStep.id,error.message);const troubleshootingItems=['Build failed. Please try the following steps:',{text:'Check if Android SDK is properly configured',indent:1,prefix:'├─ ',color:'yellow'},{text:'Verify build assets exist in the source directory',indent:1,prefix:'├─ ',color:'yellow'}];// Use androidConfig safely with null checks
27
30
  const buildType=androidConfig?.buildType||'debug';if(buildType==='release'){troubleshootingItems.push({text:'Verify keystore configuration for release builds',indent:1,prefix:'├─ ',color:'yellow'},{text:'Check that keystore passwords are properly set',indent:1,prefix:'├─ ',color:'yellow'});}else{troubleshootingItems.push({text:'Verify that the emulator exists and is working',indent:1,prefix:'├─ ',color:'yellow'});}troubleshootingItems.push({text:'Run "npm run setupEmulator:android" to reconfigure Android settings',indent:1,prefix:'└─ ',color:'yellow'},'\nVerify Configuration:');// Add configuration details only if androidConfig is available
28
31
  if(androidConfig){troubleshootingItems.push({text:`Build Type: ${buildType}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Android SDK Path: ${androidConfig.sdkPath||'Not configured'}`,indent:1,prefix:'├─ ',color:'gray'});if(buildType!=='release'){troubleshootingItems.push({text:`Selected Emulator: ${androidConfig.emulatorName||'Not configured'}`,indent:1,prefix:'└─ ',color:'gray'});}else{troubleshootingItems.push({text:`Output Path: ${androidConfig.outputPath||'build-output/'}`,indent:1,prefix:'└─ ',color:'gray'});}}else{troubleshootingItems.push({text:'Configuration could not be loaded',indent:1,prefix:'├─ ',color:'red'},{text:'Check if config/config.json exists and has valid Android configuration',indent:1,prefix:'└─ ',color:'red'});}progress.printTreeContent('Troubleshooting Guide',troubleshootingItems);}process.exit(1);}}// Execute the main build process
@@ -1,4 +1,4 @@
1
- "use strict";const{exec,execSync}=require("child_process");const fs=require("fs");const path=require("path");const TerminalProgress=require("./TerminalProgress.js").default;const pwd=`${process.cwd()}/node_modules/catalyst-core/dist/native`;const{WEBVIEW_CONFIG}=require(`${process.env.PWD}/config/config.json`);// Configuration constants
1
+ "use strict";const{exec,execSync}=require("child_process");const fs=require("fs");const path=require("path");const{setupServer}=require("./setupServer.js");const TerminalProgress=require("./TerminalProgress.js").default;const pwd=`${process.cwd()}/node_modules/catalyst-core/dist/native`;const{WEBVIEW_CONFIG}=require(`${process.env.PWD}/config/config.json`);// Configuration constants
2
2
  const iosConfig=WEBVIEW_CONFIG.ios;const url=`http://${getLocalIPAddress()}:${WEBVIEW_CONFIG.port}`;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;// Define build steps for progress tracking
3
3
  const steps={config:'Generating Required Configuration for build',launchSimulator:'Launch iOS Simulator',clean:'Clean Build Artifacts',build:'Build IOS Project',findApp:'Locate Built Application',install:'Install Application',launch:'Launch Application'};// Configure progress display
4
4
  const progressConfig={titlePaddingTop:2,titlePaddingBottom:1,stepPaddingLeft:4,stepSpacing:1,errorPaddingLeft:6,bottomMargin:2};const progress=new TerminalProgress(steps,"Catalyst iOS Build",progressConfig);async function generateConfigConstants(){progress.start('config');try{const configOutputPath=path.join(PROJECT_DIR,PROJECT_NAME,"ConfigConstants.swift");const configDir=path.dirname(configOutputPath);if(!fs.existsSync(configDir)){fs.mkdirSync(configDir,{recursive:true});}// Initialize base config with required URL
@@ -37,8 +37,11 @@ const destinationWithRuntime=`${destination},OS=${bootedInfo.version}`;console.l
37
37
  SWIFT_DEBUG_LOG=1 \
38
38
  build`;;return runCommand(buildCommand,{maxBuffer:1024*1024*10});}async function launchIOSSimulator(simulatorName){progress.start("launchSimulator");try{progress.log("Launching iOS Simulator...");// Get all simulators including both available and booted ones
39
39
  const allSimulatorInfo=execSync("xcrun simctl list devices -j").toString();const simulatorsJson=JSON.parse(allSimulatorInfo);// Search through all runtimes and their devices
40
- let foundSimulator=null;let foundSimulatorId=null;let isBooted=false;// Iterate through all runtimes and their devices
41
- Object.values(simulatorsJson.devices).forEach(devices=>{devices.forEach(device=>{if(device.name===simulatorName){foundSimulator=device;foundSimulatorId=device.udid;isBooted=device.state==="Booted";}});});if(!foundSimulator){console.log(`Configured simulator "${simulatorName}" not found.`);return;}if(!isBooted){console.log(`Booting simulator: ${simulatorName}`);runCommand(`xcrun simctl boot ${foundSimulatorId}`);}else{console.log(`Simulator ${simulatorName} is already booted`);}// Open Simulator.app and focus
42
- progress.log("Opening Simulator.app...");runCommand("open -a Simulator");// Give the simulator a moment to open/focus
40
+ let foundSimulator=null;let foundSimulatorId=null;let isBooted=false;let foundDevices=[];// Iterate through all runtimes and their devices
41
+ Object.entries(simulatorsJson.devices).forEach(([runtime,devices])=>{devices.forEach(device=>{if(device.name===simulatorName){foundDevices.push({name:device.name,state:device.state,udid:device.udid,runtime:runtime});foundSimulator=device;foundSimulatorId=device.udid;isBooted=device.state==="Booted";}});});if(!foundSimulator){console.log(`Configured simulator "${simulatorName}" not found.`);return;}if(!isBooted){progress.log(`Attempting to boot simulator: ${simulatorName} (${foundSimulatorId})`,'warning');// Check if the simulator is in a valid state to be booted
42
+ if(foundSimulator.state==="Shutdown"){progress.log(`Simulator is shutdown, attempting to boot...`,'info');try{await runCommand(`xcrun simctl boot ${foundSimulatorId}`);progress.log(`Successfully booted simulator`,'success');}catch(bootError){progress.log(`Boot failed: ${bootError.message}`,'error');// Try to find an alternative booted simulator
43
+ progress.log(`Looking for any booted simulator as fallback...`,'info');const fallbackUUID=await getBootedSimulatorUUID(simulatorName);if(fallbackUUID){progress.log(`Found fallback booted simulator: ${fallbackUUID}`,'success');foundSimulatorId=fallbackUUID;}else{throw new Error(`Failed to boot simulator and no fallback available: ${bootError.message}`);}}}else{progress.log(`Simulator state is '${foundSimulator.state}', not 'Shutdown'. Checking if we can use it...`,'warning');if(foundSimulator.state==="Booted"){progress.log(`Wait, simulator is actually booted! Using it.`,'success');}else{throw new Error(`Simulator is in unexpected state: ${foundSimulator.state}`);}}}else{progress.log(`Simulator ${simulatorName} is already booted`,'success');}// Open Simulator.app and focus
44
+ progress.log("Opening Simulator.app...");await runCommand("open -a Simulator");// Give the simulator a moment to open/focus
43
45
  await new Promise(resolve=>setTimeout(resolve,1000));// Activate the Simulator.app window to bring it to front
44
- runCommand("osascript -e 'tell application \"Simulator\" to activate'");console.log("iOS Simulator launched successfully.");progress.complete("launchSimulator");}catch(error){console.error("Failed to launch iOS Simulator. Error:",error.message);process.exit(1);}}async function main(){try{const originalDir=process.cwd();progress.log('Starting build process from: '+originalDir,'info');await generateConfigConstants();progress.log('Changing directory to: '+PROJECT_DIR,'info');process.chdir(PROJECT_DIR);await launchIOSSimulator(IPHONE_MODEL);await cleanBuildArtifacts();await buildXcodeProject();const APP_PATH=await findAppPath();progress.log('Found app at: '+APP_PATH,'success');await installAndLaunchApp(APP_PATH);process.chdir(originalDir);progress.printTreeContent('Build Summary',['Build completed successfully:',{text:`App Path: ${APP_PATH}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Simulator: ${IPHONE_MODEL}`,indent:1,prefix:'├─ ',color:'gray'},{text:`URL: ${url}`,indent:1,prefix:'└─ ',color:'gray'}]);}catch(error){progress.log('Build failed: '+error.message,'error');process.exit(1);}}main();
46
+ await runCommand("osascript -e 'tell application \"Simulator\" to activate'");progress.log("iOS Simulator launched successfully.",'success');progress.complete("launchSimulator");}catch(error){progress.fail("launchSimulator",error.message);// Show detailed troubleshooting info
47
+ progress.printTreeContent('Simulator Troubleshooting',['iOS Simulator failed to launch. Common solutions:',{text:'Delete and recreate the simulator in Xcode',indent:1,prefix:'├─ ',color:'yellow'},{text:'Reset simulator content: Device > Erase All Content and Settings',indent:1,prefix:'├─ ',color:'yellow'},{text:'Check available simulators: xcrun simctl list devices',indent:1,prefix:'├─ ',color:'yellow'},{text:'Restart Xcode and Simulator app',indent:1,prefix:'└─ ',color:'yellow'},'','Error Details:',{text:`Simulator: ${simulatorName}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Error: ${error.message}`,indent:1,prefix:'└─ ',color:'red'}]);console.error("Failed to launch iOS Simulator. Error:",error.message);process.exit(1);}}async function main(){try{await setupServer(`${process.env.PWD}/config/config.json`);const originalDir=process.cwd();progress.log('Starting build process from: '+originalDir,'info');await generateConfigConstants();progress.log('Changing directory to: '+PROJECT_DIR,'info');process.chdir(PROJECT_DIR);await launchIOSSimulator(IPHONE_MODEL);await cleanBuildArtifacts();await buildXcodeProject();const APP_PATH=await findAppPath();progress.log('Found app at: '+APP_PATH,'success');await installAndLaunchApp(APP_PATH);process.chdir(originalDir);progress.printTreeContent('Build Summary',['Build completed successfully:',{text:`App Path: ${APP_PATH}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Simulator: ${IPHONE_MODEL}`,indent:1,prefix:'├─ ',color:'gray'},{text:`URL: ${url}`,indent:1,prefix:'└─ ',color:'gray'}]);}catch(error){progress.log('Build failed: '+error.message,'error');process.exit(1);}process.exit(1);}main();
@@ -23,13 +23,13 @@ try{(0,_utils.runCommand)(`chmod +x ./gradlew`);}catch(chmodError){progress.log(
23
23
  progress.log('Cleaning previous builds...','info');try{(0,_utils.runCommand)('./gradlew clean');progress.log('Clean completed successfully','success');}catch(cleanError){progress.log(`Warning: Clean failed: ${cleanError.message}`,'warning');// Continue anyway as this might not be critical
24
24
  }// Build signed AAB with PKCS12-compatible parameters
25
25
  progress.log('Building signed AAB...','info');// For PKCS12 keystores, use store password for both store and key
26
- const effectiveKeyPassword=keystoreConfig.storePassword;const bundleCommand=['./gradlew bundleRelease',`-Pandroid.injected.signing.store.file="${keystorePath}"`,`-Pandroid.injected.signing.store.password="${keystoreConfig.storePassword}"`,`-Pandroid.injected.signing.key.alias="${keystoreConfig.keyAlias}"`,`-Pandroid.injected.signing.key.password="${effectiveKeyPassword}"`].join(' ');progress.log(`Executing: ${bundleCommand}`,'info');(0,_utils.runCommand)(bundleCommand);progress.log('Bundle build completed','success');// Find the generated AAB file
26
+ const effectiveKeyPassword=keystoreConfig.storePassword;const bundleCommand=['./gradlew bundleRelease --quiet --console=rich',`-Pandroid.injected.signing.store.file="${keystorePath}"`,`-Pandroid.injected.signing.store.password="${keystoreConfig.storePassword}"`,`-Pandroid.injected.signing.key.alias="${keystoreConfig.keyAlias}"`,`-Pandroid.injected.signing.key.password="${effectiveKeyPassword}"`].join(' ');progress.log(`Executing: ${bundleCommand}`,'info');(0,_utils.runCommand)(bundleCommand);progress.log('Bundle build completed','success');// Find the generated AAB file
27
27
  const aabPath=_path.default.join(newProjectPath,'app','build','outputs','bundle','release','app-release.aab');progress.log(`Looking for AAB file at: ${aabPath}`,'info');if(!_fs.default.existsSync(aabPath)){// Try to find AAB files in the build output directory
28
28
  const bundleDir=_path.default.join(newProjectPath,'app','build','outputs','bundle');progress.log(`AAB not found at expected location. Checking bundle directory: ${bundleDir}`,'warning');if(_fs.default.existsSync(bundleDir)){try{const findAabCommand=`find "${bundleDir}" -name "*.aab" -type f`;const foundAabs=(0,_utils.runCommand)(findAabCommand);if(foundAabs.trim()){progress.log(`Found AAB files: ${foundAabs.trim()}`,'info');}else{progress.log('No AAB files found in bundle directory','warning');}}catch(findError){progress.log(`Error searching for AAB files: ${findError.message}`,'warning');}}throw new Error('Signed AAB file was not generated successfully');}// Get file size
29
29
  const stats=_fs.default.statSync(aabPath);const fileSizeMB=(stats.size/(1024*1024)).toFixed(2);progress.log(`Signed AAB created successfully!`,'success');progress.log(`AAB location: ${aabPath}`,'info');progress.log(`AAB size: ${fileSizeMB} MB`,'info');// Optionally copy AAB to a more accessible location
30
30
  const outputDir=_path.default.join(newProjectPath,'release');if(!_fs.default.existsSync(outputDir)){_fs.default.mkdirSync(outputDir,{recursive:true});}const outputAabPath=_path.default.join(outputDir,`${androidConfig.newProjectName}-release.aab`);_fs.default.copyFileSync(aabPath,outputAabPath);progress.log(`AAB copied to: ${outputAabPath}`,'success');return{aabPath,outputAabPath,fileSize:fileSizeMB};}finally{// Restore original working directory
31
- process.chdir(originalCwd);}}catch(error){throw new Error(`Failed to create signed AAB: ${error.message}`);}}async function initializeConfig(configPath){if(!configPath){throw new Error('Config path is required');}if(!_fs.default.existsSync(configPath)){throw new Error(`Config file not found at: ${configPath}`);}progress.log(`Reading config from: ${configPath}`,'info');const configFile=_fs.default.readFileSync(configPath,'utf8');const config=JSON.parse(configFile);if(!config.android){throw new Error('Android configuration missing in config file');}const{android}=config;// Validate required fields
32
- if(!android.oldProjectName){throw new Error('oldProjectName is required in android config');}if(!android.newProjectName){throw new Error('newProjectName is required in android config');}if(!android.projectPath){throw new Error('projectPath is required in android config');}progress.log(`Old project name: ${android.oldProjectName}`,'info');progress.log(`New project name: ${android.newProjectName}`,'info');progress.log(`Project path: ${android.projectPath}`,'info');if(android.outputPath){progress.log(`Custom output path: ${android.outputPath}`,'info');}else{const parentDir=_path.default.dirname(android.projectPath);const defaultOutputPath=_path.default.join(parentDir,'build-output');progress.log(`Default output path: ${defaultOutputPath}`,'info');}if(android.createSignedAAB){progress.log(`Signed AAB creation: Enabled`,'info');if(android.keystoreConfig){// Check for placeholder passwords
31
+ process.chdir(originalCwd);}}catch(error){throw new Error(`Failed to create signed AAB: ${error.message}`);}}async function initializeConfig(configPath){if(!configPath){throw new Error('Config path is required');}if(!_fs.default.existsSync(configPath)){throw new Error(`Config file not found at: ${configPath}`);}progress.log(`Reading config from: ${configPath}`,'info');const configFile=_fs.default.readFileSync(configPath,'utf8');const config=JSON.parse(configFile);if(!config.android){throw new Error('Android configuration missing in config file');}const{android}=config;// Validate required fields (only newProjectName is truly required)
32
+ if(!android.newProjectName){throw new Error('newProjectName is required in android config');}progress.log(`Project path: ${android.projectPath}`,'info');progress.log(`Old project name: ${android.oldProjectName}`,'info');progress.log(`New project name: ${android.newProjectName}`,'info');if(android.outputPath){progress.log(`Custom output path: ${android.outputPath}`,'info');}else{const parentDir=_path.default.dirname(android.projectPath);const defaultOutputPath=_path.default.join(parentDir,'build-output');progress.log(`Default output path: ${defaultOutputPath}`,'info');}if(android.createSignedAAB){progress.log(`Signed AAB creation: Enabled`,'info');if(android.keystoreConfig){// Check for placeholder passwords
33
33
  if(android.keystoreConfig.storePassword==='your_store_password'||android.keystoreConfig.keyPassword==='your_key_password'||android.keystoreConfig.storePassword==='your_password'||android.keystoreConfig.keyPassword==='your_password'){progress.log(`Warning: Keystore contains placeholder passwords. Please update them.`,'warning');}else{progress.log(`Keystore configuration: Valid`,'success');}}else{progress.log(`Warning: Keystore configuration missing for AAB creation`,'warning');}}else{progress.log(`Signed AAB creation: Disabled (set createSignedAAB: true to enable)`,'info');}return{android,config};}async function diagnoseAndroidProject(projectPath){progress.log('Diagnosing Android project structure...','info');if(!_fs.default.existsSync(projectPath)){throw new Error(`Project path does not exist: ${projectPath}`);}// Check for common Android project patterns (supporting both .gradle and .gradle.kts)
34
34
  const patterns=[{files:['build.gradle','build.gradle.kts'],description:'Root build gradle',type:'either'},{files:['settings.gradle','settings.gradle.kts'],description:'Settings gradle',type:'either'},{files:['gradlew'],description:'Gradle wrapper script',type:'single'},{files:['gradle.properties'],description:'Gradle properties',type:'single'},{files:['app/build.gradle','app/build.gradle.kts'],description:'App module build gradle',type:'either'},{files:['app/src/main/AndroidManifest.xml'],description:'Android manifest',type:'single'},{files:['app/src/main/java'],description:'Java source directory',type:'single'},{files:['app/src/main/res'],description:'Android resources',type:'single'}];const diagnostics={foundFiles:[],missingFiles:[],projectType:'unknown',usesKotlinDSL:false};for(const pattern of patterns){let found=false;let foundFile=null;if(pattern.type==='either'){// Check if either variant exists
35
35
  for(const file of pattern.files){const fullPath=_path.default.join(projectPath,file);if(_fs.default.existsSync(fullPath)){found=true;foundFile=file;if(file.endsWith('.kts')){diagnostics.usesKotlinDSL=true;}break;}}}else{// Check single file
@@ -58,8 +58,11 @@ const findFilesCommand=`find "${newProjectPath}" -type f -name "*${oldProjectNam
58
58
  const fileTypes=['*.gradle','*.gradle.kts',// Added Kotlin DSL gradle files
59
59
  '*.xml','*.json','*.properties','*.java','*.kt','*.kts',// Added general Kotlin script files
60
60
  '*.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
61
- 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(configPath){let androidConfig;let projectPaths;let backupPath;let tempDeploymentPath;try{// Initialize configuration
62
- progress.start('config');const{android}=await initializeConfig(configPath);androidConfig=android;progress.complete('config');// Validate project structure
61
+ 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
62
+ progress.start('config');if(typeof configPathOrConfig==='string'){const{android}=await initializeConfig(configPathOrConfig);androidConfig=android;}else{// Direct config object passed (from buildAppAndroid.js)
63
+ // All defaults should already be applied in buildAppAndroid.js
64
+ androidConfig=configPathOrConfig;// Validate required fields
65
+ if(!androidConfig.newProjectName){throw new Error('newProjectName is required in android config');}if(!androidConfig.projectPath){throw new Error('projectPath is required in android config');}if(!androidConfig.oldProjectName){throw new Error('oldProjectName is required in android config');}progress.log(`Using configuration from buildAppAndroid.js`,'info');progress.log(`Project path: ${androidConfig.projectPath}`,'info');progress.log(`Old project name: ${androidConfig.oldProjectName}`,'info');progress.log(`New project name: ${androidConfig.newProjectName}`,'info');progress.log(`Deployment path: ${androidConfig.deploymentPath||'Not specified'}`,'info');progress.log(`Overwrite existing: ${androidConfig.overwriteExisting}`,'info');}progress.complete('config');// Validate project structure
63
66
  progress.start('validation');projectPaths=await validateProjectStructure(androidConfig);progress.complete('validation');// Create backup if requested
64
67
  progress.start('backup');backupPath=await createBackup(projectPaths,androidConfig);progress.complete('backup');// Create temporary deployment project copy
65
68
  progress.start('createTempDeployment');tempDeploymentPath=await createTempDeploymentProject(projectPaths,androidConfig);progress.complete('createTempDeployment');// Update file contents in temp deployment
@@ -69,8 +72,8 @@ let aabResult=null;if(keystorePath&&androidConfig.createSignedAAB){progress.star
69
72
  if(aabResult){progress.start('finalizeAAB');aabResult=await finalizeAABAndCleanup(projectPaths,androidConfig,aabResult,tempDeploymentPath);progress.complete('finalizeAAB');}}else if(!androidConfig.createSignedAAB){progress.log('Signed AAB creation skipped (not enabled in config)','info');// Clean up temp deployment since we're not building AAB
70
73
  await cleanupTempDeployment(tempDeploymentPath);}else if(!keystorePath){progress.log('Signed AAB creation skipped (no keystore available)','warning');// Clean up temp deployment since we're not building AAB
71
74
  await cleanupTempDeployment(tempDeploymentPath);}// Print completion summary
72
- progress.printTreeContent('Build Summary',['Android AAB build completed successfully:',{text:`Original project: ${projectPaths.oldProjectPath}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Project name: ${androidConfig.oldProjectName} → ${androidConfig.newProjectName}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Backup created: ${backupPath?'Yes':'No'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Keystore: ${keystorePath?'Created/Verified':'Skipped'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Signed AAB: ${aabResult?'Created':'Skipped'}`,indent:1,prefix:'└─ ',color:'gray'},'','Output Files:',{text:`Original preserved at: ${projectPaths.oldProjectPath}`,indent:1,prefix:'├─ ',color:'green'},...(backupPath?[{text:`Backup: ${backupPath}`,indent:1,prefix:'├─ ',color:'gray'}]:[]),...(aabResult?[{text:`AAB file: ${aabResult.finalAabPath}`,indent:1,prefix:'├─ ',color:'green'},{text:`AAB size: ${aabResult.fileSize} MB`,indent:1,prefix:'└─ ',color:'green'}]:[{text:`No AAB created`,indent:1,prefix:'└─ ',color:'gray'}])]);process.exit(0);}catch(error){// Clean up temp deployment on error
73
- if(tempDeploymentPath){await cleanupTempDeployment(tempDeploymentPath);}if(progress.currentStep){progress.fail(progress.currentStep.id,error.message);progress.printTreeContent('Troubleshooting Guide',['Build operation failed. Please check the following:',{text:'Verify config file exists and contains required android configuration',indent:1,prefix:'├─ ',color:'yellow'},{text:'Ensure project path exists and is accessible',indent:1,prefix:'├─ ',color:'yellow'},{text:'Check file/directory permissions',indent:1,prefix:'├─ ',color:'yellow'},{text:'Verify no processes are using the project directory',indent:1,prefix:'├─ ',color:'yellow'},{text:'Ensure sufficient disk space for project copy',indent:1,prefix:'├─ ',color:'yellow'},{text:'Update keystore passwords (do not use "your_store_password")',indent:1,prefix:'└─ ',color:'yellow'},'\nConfiguration Details:',{text:`Config path: ${configPath}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Old project name: ${androidConfig?.oldProjectName||'Not loaded'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`New project name: ${androidConfig?.newProjectName||'Not loaded'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Project path: ${androidConfig?.projectPath||'Not loaded'}`,indent:1,prefix:'└─ ',color:'gray'},'\nFor keystore issues, ensure your config has:',{text:'Real passwords (not placeholder values)',indent:1,prefix:'├─ ',color:'cyan'},{text:'Valid organization information',indent:1,prefix:'├─ ',color:'cyan'},{text:'Consistent store and key passwords for PKCS12',indent:1,prefix:'└─ ',color:'cyan'}]);}process.exit(1);}}// Export functions for use as a module - using the same export pattern as buildAppAndroid.js
75
+ progress.printTreeContent('Build Summary',['Android AAB build completed successfully:',{text:`Original project: ${projectPaths.oldProjectPath}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Project name: ${androidConfig.oldProjectName} → ${androidConfig.newProjectName}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Deployment path: ${androidConfig.deploymentPath||'Not specified'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Overwrite existing: ${androidConfig.overwriteExisting}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Backup created: ${backupPath?'Yes':'No'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Keystore: ${keystorePath?'Created/Verified':'Skipped'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Signed AAB: ${aabResult?'Created':'Skipped'}`,indent:1,prefix:'└─ ',color:'gray'},'','Output Files:',{text:`Original preserved at: ${projectPaths.oldProjectPath}`,indent:1,prefix:'├─ ',color:'green'},...(backupPath?[{text:`Backup: ${backupPath}`,indent:1,prefix:'├─ ',color:'gray'}]:[]),...(aabResult?[{text:`AAB file: ${aabResult.finalAabPath}`,indent:1,prefix:'├─ ',color:'green'},{text:`AAB size: ${aabResult.fileSize} MB`,indent:1,prefix:'└─ ',color:'green'}]:[{text:`No AAB created`,indent:1,prefix:'└─ ',color:'gray'}])]);process.exit(0);}catch(error){// Clean up temp deployment on error
76
+ if(tempDeploymentPath){await cleanupTempDeployment(tempDeploymentPath);}if(progress.currentStep){progress.fail(progress.currentStep.id,error.message);progress.printTreeContent('Troubleshooting Guide',['Build operation failed. Please check the following:',{text:'Verify config file exists and contains required android configuration',indent:1,prefix:'├─ ',color:'yellow'},{text:'Ensure project path exists and is accessible',indent:1,prefix:'├─ ',color:'yellow'},{text:'Check file/directory permissions',indent:1,prefix:'├─ ',color:'yellow'},{text:'Verify no processes are using the project directory',indent:1,prefix:'├─ ',color:'yellow'},{text:'Ensure sufficient disk space for project copy',indent:1,prefix:'├─ ',color:'yellow'},{text:'Update keystore passwords (do not use "your_store_password")',indent:1,prefix:'└─ ',color:'yellow'},'\nConfiguration Details:',{text:`Config: ${typeof configPathOrConfig==='string'?configPathOrConfig:'Direct object from buildAppAndroid.js'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Project path: ${androidConfig?.projectPath||'Not loaded'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Deployment path: ${androidConfig?.deploymentPath||'Not loaded'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Old project name: ${androidConfig?.oldProjectName||'Not loaded'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`New project name: ${androidConfig?.newProjectName||'Not loaded'}`,indent:1,prefix:'├─ ',color:'gray'},{text:`Overwrite existing: ${androidConfig?.overwriteExisting!==undefined?androidConfig.overwriteExisting:'Not loaded'}`,indent:1,prefix:'└─ ',color:'gray'},'\nFor keystore issues, ensure your config has:',{text:'Real passwords (not placeholder values)',indent:1,prefix:'├─ ',color:'cyan'},{text:'Valid organization information',indent:1,prefix:'├─ ',color:'cyan'},{text:'Consistent store and key passwords for PKCS12',indent:1,prefix:'└─ ',color:'cyan'}]);}process.exit(1);}}// Export functions for use as a module - using the same export pattern as buildAppAndroid.js
74
77
  // This allows the functions to be imported with ES6 import syntax from other modules
75
78
  exports.buildAndroidAAB=buildAndroidAAB;exports.createAndroidDeployment=buildAndroidAAB;exports.renameAndroidProject=buildAndroidAAB;// Execute if run directly
76
79
  if(require.main===module){const configPath=process.argv[2];if(!configPath){console.error('Usage: node renameAndroidProject.js <config-path>');process.exit(1);}buildAndroidAAB(configPath).catch(error=>{console.error('Script failed:',error.message);process.exit(1);});}
@@ -0,0 +1,27 @@
1
+ "use strict";const{exec,execSync}=require("child_process");const fs=require("fs");/**
2
+ * Get local IP address on macOS using ifconfig
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
+ * Check if server is running on given port
5
+ */async function isServerRunning(port){return new Promise(resolve=>{const command=`lsof -i :${port}`;exec(command,(error,stdout)=>{if(error){resolve(false);return;}resolve(stdout.includes('LISTEN'));});});}/**
6
+ * Start server in background using npm start
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
8
+ console.log('Server started in background');}}/**
9
+ * Recursively replace localhost and different IPs with current local IP
10
+ */function replaceIPInObject(obj,localIP,path=''){let updated=false;for(const[key,value]of Object.entries(obj)){const currentPath=path?`${path}.${key}`:key;if(typeof value==='string'){let newValue=value;let shouldUpdate=false;// Replace localhost with local IP
11
+ if(value.includes('localhost')){newValue=value.replace(/localhost/g,localIP);shouldUpdate=true;}// Replace different IP addresses in URLs
12
+ const urlPattern=/http:\/\/(\d+\.\d+\.\d+\.\d+)/g;const matches=[...value.matchAll(urlPattern)];for(const match of matches){if(match[1]!==localIP){newValue=newValue.replace(match[1],localIP);shouldUpdate=true;}}// Replace standalone IP addresses that are not localhost
13
+ const ipPattern=/^(\d+\.\d+\.\d+\.\d+)$/;const ipMatch=value.match(ipPattern);if(ipMatch&&ipMatch[1]!==localIP){newValue=localIP;shouldUpdate=true;}if(shouldUpdate&&newValue!==value){console.log(`Updating ${currentPath}: ${value} -> ${newValue}`);obj[key]=newValue;updated=true;}}else if(typeof value==='object'&&value!==null){// Recursively process nested objects
14
+ const nestedUpdated=replaceIPInObject(value,localIP,currentPath);updated=updated||nestedUpdated;}}return updated;}/**
15
+ * Update config.json with current local IP address
16
+ */function updateConfigWithLocalIP(configPath,localIP){try{const config=JSON.parse(fs.readFileSync(configPath,'utf8'));// Recursively update all IP addresses in the config
17
+ const updated=replaceIPInObject(config,localIP);// Write back if updated
18
+ if(updated){fs.writeFileSync(configPath,JSON.stringify(config,null,2),'utf8');console.log('Configuration updated with current IP address');}else{console.log('No IP addresses needed updating in configuration');}return config;}catch(error){console.error('Error updating config:',error.message);throw error;}}/**
19
+ * Main function: Setup server with IP validation and background startup
20
+ * @param {string} configPath - Path to config.json file
21
+ * @returns {Promise<{serverURL: string, localIP: string, port: number}>}
22
+ */async function setupServer(configPath){console.log('Setting up server...');// Get local IP
23
+ const localIP=getLocalIPAddress();console.log(`Local IP: ${localIP}`);// Update config with current IP
24
+ const config=updateConfigWithLocalIP(configPath,localIP);// Get port from config
25
+ const port=config.WEBVIEW_CONFIG?.port||3005;const serverURL=`http://${localIP}:${port}`;// Check if server is running
26
+ const running=await isServerRunning(port);if(!running){console.log(`Server not running on ${serverURL}, starting...`);startServerBackground();// Give it a moment to start
27
+ await new Promise(resolve=>setTimeout(resolve,3000));}else{console.log(`Server already running on ${serverURL}`);}console.log(`Server setup complete: ${serverURL}`);return{serverURL,localIP,port};}module.exports={setupServer};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catalyst-core",
3
- "version": "0.0.3-canary.5",
3
+ "version": "0.0.3-canary.6",
4
4
  "main": "index.js",
5
5
  "description": "Web framework that provides great performance out of the box",
6
6
  "bin": {
@@ -1,35 +0,0 @@
1
- {
2
- "verbose": true,
3
- "android": {
4
- "projectPath": "/Users/mayankmahavar/catalyst-core/src/native/androidProject",
5
- "oldProjectName": "myapplication",
6
- "newProjectName": "mycatalystapp",
7
- "deploymentPath": "./deployment",
8
- "applicationName": "My Catalyst App",
9
- "preserveKeystore": false,
10
- "sdkPath": "/Users/mayankmahavar/Android/",
11
- "emulatorName": "testMediumPhone",
12
- "createSignedAAB" : true,
13
- "overwriteExisting": true,
14
- "productionConfig": {
15
- "webAppUrl": "https://yourwebapp.com",
16
- "apiBaseUrl": "https://api.yourdomain.com/",
17
- "allowCleartextTraffic": false,
18
- "port": 3005
19
- },
20
- "keystoreConfig": {
21
- "keyAlias": "release",
22
- "storePassword": "MySecurePassword123!",
23
- "keyPassword": "MySecurePassword123!",
24
- "validityYears": 25,
25
- "organizationInfo": {
26
- "companyName": "Your Company Name",
27
- "department": "Development",
28
- "organization": "Your Organization",
29
- "city": "Your City",
30
- "state": "Your State",
31
- "countryCode": "US"
32
- }
33
- }
34
- }
35
- }