catalyst-core 0.1.0-beta.1 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/changelog.md +5 -0
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +2 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +3 -3
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/FridaDetector.kt +16 -5
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +7 -3
- package/dist/native/androidSetup.js +1 -1
- package/dist/native/bridge/useBaseHook.js +4 -4
- package/dist/native/bridge/utils/NativeBridge.js +5 -5
- package/dist/native/buildAppAndroid.js +5 -7
- package/dist/native/buildAppIos.js +6 -7
- package/dist/native/renameAndroidProject.js +3 -3
- package/dist/native/setupServer.js +2 -3
- package/dist/native/utils.js +4 -1
- package/dist/scripts/devServe.js +1 -1
- package/dist/scripts/registerAliases.js +6 -2
- package/dist/scripts/scriptUtils.js +2 -1
- package/dist/scripts/serve.js +2 -1
- package/dist/scripts/start.js +4 -1
- package/dist/server/renderer/extract.js +2 -2
- package/dist/server/utils/validator.js +1 -1
- package/dist/webpack/base.babel.js +2 -1
- package/dist/webpack/development.client.babel.js +1 -1
- package/mcp_v2/lib/helpers.js +30 -12
- package/mcp_v2/mcp.js +4 -3
- package/mcp_v2/setup.js +5 -3
- package/mcp_v2/tools/build.js +1 -1
- package/mcp_v2/tools/config.js +16 -4
- package/mcp_v2/tools/conversion.js +11 -11
- package/mcp_v2/tools/knowledge.js +16 -3
- package/mcp_v2/tools/tasks.js +13 -4
- package/package.json +7 -14
- package/.release-meta.json +0 -4
- package/license +0 -10
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
Catalyst offers a comprehensive suite of features designed for modern web development. It includes isomorphic rendering for optimal performance, an extendable server with full-stack capabilities, and configurable state management. The framework employs smart prefetching of data and chunks, allows easy configuration of global styles and layouts, and provides SEO optimization at both global and page levels.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Catalyst also supports universal apps for Android and iOS. See the [Universal App guide](https://catalyst.1mg.com/public_docs/content/Guides%20and%20Tutorials/First%20Universal%20App/first-universal-app) to get started.
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
package/changelog.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.0-beta.2] - 2026-05-06
|
|
4
|
+
|
|
5
|
+
- Moved Catalyst into a monorepo structure with `catalyst-core`, `create-catalyst-app`, the Catalyst docs app, and the Catalyst core test app managed from one repository.
|
|
6
|
+
- Replaced the internal-package pre-release flow with a local release sandbox that scaffolds a real app using current-branch CCA and a locally packed current-branch `catalyst-core`.
|
|
7
|
+
|
|
3
8
|
## [0.1.0-beta.1] - 2026-04-15
|
|
4
9
|
|
|
5
10
|
- Promoted `0.1.0-canary.7` to the first proper beta release after stabilization.
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
android:usesCleartextTraffic="true"
|
|
25
25
|
android:networkSecurityConfig="@xml/network_security_config"
|
|
26
26
|
tools:targetApi="31">
|
|
27
|
+
<!-- nosemgrep: java.android.security.exported_activity.exported_activity - Launcher activity must be exported so Android can start the app from the launcher. -->
|
|
27
28
|
<activity
|
|
28
29
|
android:name="io.yourname.androidproject.SplashActivity"
|
|
29
30
|
android:exported="true"
|
|
@@ -51,4 +52,4 @@
|
|
|
51
52
|
</provider>
|
|
52
53
|
</application>
|
|
53
54
|
|
|
54
|
-
</manifest>
|
|
55
|
+
</manifest>
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt
CHANGED
|
@@ -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("
|
|
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
|
|
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.
|
|
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
|
-
|
|
72
|
-
val address =
|
|
73
|
-
|
|
74
|
-
|
|
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.
|
|
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
|
-
|
|
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}...`);//
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
16
|
-
window.NativeBridge[command](data);return true;}else{console.error(`Android bridge method '${command}' not found`);return false;}}catch(error){console.error(
|
|
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(
|
|
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(
|
|
27
|
-
if(this.isAndroid){return this._executeAndroidCommand(command,data);}else if(this.isIOS){return this._executeIOSCommand(command,data);}return false;}catch(error){console.error(
|
|
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.
|
|
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
|
|
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);}
|
|
90
|
-
|
|
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.
|
|
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{
|
|
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)=>{
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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");}}/**
|
package/dist/native/utils.js
CHANGED
|
@@ -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{
|
|
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
|
package/dist/scripts/devServe.js
CHANGED
|
@@ -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,"../../");
|
|
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{
|
|
2
|
-
|
|
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
|
|
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;
|
package/dist/scripts/serve.js
CHANGED
|
@@ -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,"../../")
|
|
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();
|
package/dist/scripts/start.js
CHANGED
|
@@ -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){
|
|
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(
|
|
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(
|
|
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)=>{
|
|
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.
|
|
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
|
package/mcp_v2/lib/helpers.js
CHANGED
|
@@ -38,8 +38,8 @@ function versionOlderThan(a, b) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Walk up the directory tree looking for a package.json that depends on
|
|
42
|
-
* Returns { dir, pkg, catalystVersion, installedVersion, versionMeta } or null.
|
|
41
|
+
* Walk up the directory tree looking for a package.json that depends on Catalyst.
|
|
42
|
+
* Returns { dir, pkg, catalystPackageName, catalystVersion, installedVersion, versionMeta } or null.
|
|
43
43
|
*/
|
|
44
44
|
function findCatalystRoot() {
|
|
45
45
|
// Walk up from __dirname first (mcp always lives inside the project at .catalyst/mcp/),
|
|
@@ -55,7 +55,8 @@ function findCatalystRoot() {
|
|
|
55
55
|
try {
|
|
56
56
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
|
|
57
57
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
|
|
58
|
-
|
|
58
|
+
const declaredRef = deps["catalyst-core"]
|
|
59
|
+
if (declaredRef) {
|
|
59
60
|
const nmPath = path.join(dir, "node_modules", "catalyst-core")
|
|
60
61
|
const installed = fs.existsSync(nmPath)
|
|
61
62
|
// Read the actual installed version from node_modules
|
|
@@ -70,11 +71,11 @@ function findCatalystRoot() {
|
|
|
70
71
|
/* ignore */
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
|
-
const declaredRef = deps["catalyst-core"]
|
|
74
74
|
const isGithubRef = declaredRef.startsWith("github:") || declaredRef.includes("#")
|
|
75
75
|
return {
|
|
76
76
|
dir,
|
|
77
77
|
pkg,
|
|
78
|
+
catalystPackageName: "catalyst-core",
|
|
78
79
|
catalystVersion: declaredRef, // what package.json says (may be github ref)
|
|
79
80
|
installedVersion, // what's actually in node_modules e.g. "0.0.3-canary.3"
|
|
80
81
|
notInstalled: !installed,
|
|
@@ -100,21 +101,33 @@ function findCatalystRoot() {
|
|
|
100
101
|
* File-system helpers scoped to a project root.
|
|
101
102
|
*/
|
|
102
103
|
function makeProjectHelpers(root) {
|
|
104
|
+
function safeProjectPath(rel) {
|
|
105
|
+
if (typeof rel !== "string" || path.isAbsolute(rel)) return null
|
|
106
|
+
const normalized = path.normalize(rel)
|
|
107
|
+
if (normalized === ".." || normalized.startsWith(`..${path.sep}`)) return null
|
|
108
|
+
return `${root}${path.sep}${normalized}`
|
|
109
|
+
}
|
|
110
|
+
|
|
103
111
|
function fileExists(rel) {
|
|
104
|
-
|
|
112
|
+
const filePath = safeProjectPath(rel)
|
|
113
|
+
return Boolean(filePath && fs.existsSync(filePath))
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
function readJson(rel) {
|
|
117
|
+
const filePath = safeProjectPath(rel)
|
|
118
|
+
if (!filePath) return null
|
|
108
119
|
try {
|
|
109
|
-
return JSON.parse(fs.readFileSync(
|
|
120
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"))
|
|
110
121
|
} catch {
|
|
111
122
|
return null
|
|
112
123
|
}
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
function readText(rel) {
|
|
127
|
+
const filePath = safeProjectPath(rel)
|
|
128
|
+
if (!filePath) return null
|
|
116
129
|
try {
|
|
117
|
-
return fs.readFileSync(
|
|
130
|
+
return fs.readFileSync(filePath, "utf8")
|
|
118
131
|
} catch {
|
|
119
132
|
return null
|
|
120
133
|
}
|
|
@@ -124,7 +137,6 @@ function makeProjectHelpers(root) {
|
|
|
124
137
|
* Walk src/**\/*.{js,jsx,ts,tsx} and return relative paths that match pattern.
|
|
125
138
|
*/
|
|
126
139
|
function grepSrc(pattern) {
|
|
127
|
-
const re = new RegExp(pattern)
|
|
128
140
|
const matches = []
|
|
129
141
|
function walk(dir) {
|
|
130
142
|
let entries
|
|
@@ -135,12 +147,13 @@ function makeProjectHelpers(root) {
|
|
|
135
147
|
}
|
|
136
148
|
for (const e of entries) {
|
|
137
149
|
if (e.name === "node_modules" || e.name === ".git") continue
|
|
138
|
-
const full = path.
|
|
150
|
+
const full = `${dir}${path.sep}${e.name}`
|
|
139
151
|
if (e.isDirectory()) {
|
|
140
152
|
walk(full)
|
|
141
153
|
} else if (/\.(js|jsx|ts|tsx)$/.test(e.name)) {
|
|
142
154
|
try {
|
|
143
|
-
|
|
155
|
+
const content = fs.readFileSync(full, "utf8")
|
|
156
|
+
if (pattern.test(content)) {
|
|
144
157
|
matches.push(path.relative(root, full))
|
|
145
158
|
}
|
|
146
159
|
} catch {
|
|
@@ -149,11 +162,16 @@ function makeProjectHelpers(root) {
|
|
|
149
162
|
}
|
|
150
163
|
}
|
|
151
164
|
}
|
|
152
|
-
walk(path.
|
|
165
|
+
walk(`${root}${path.sep}src`)
|
|
153
166
|
return matches
|
|
154
167
|
}
|
|
155
168
|
|
|
156
169
|
return { fileExists, readJson, readText, grepSrc }
|
|
157
170
|
}
|
|
158
171
|
|
|
159
|
-
module.exports = {
|
|
172
|
+
module.exports = {
|
|
173
|
+
findCatalystRoot,
|
|
174
|
+
makeProjectHelpers,
|
|
175
|
+
versionOlderThan,
|
|
176
|
+
parseVersion,
|
|
177
|
+
}
|
package/mcp_v2/mcp.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Startup validation → module init → JSON-RPC loop over stdio.
|
|
6
6
|
*
|
|
7
7
|
* Hard fails if:
|
|
8
|
-
* - No
|
|
8
|
+
* - No Catalyst package in package.json (not a catalyst project)
|
|
9
9
|
* - context.db missing (setup.js not run)
|
|
10
10
|
*
|
|
11
11
|
* Tool routing: LLM reads tool name + description to route.
|
|
@@ -39,7 +39,8 @@ if (!projectInfo) {
|
|
|
39
39
|
jsonrpc: "2.0",
|
|
40
40
|
error: {
|
|
41
41
|
code: -32000,
|
|
42
|
-
message:
|
|
42
|
+
message:
|
|
43
|
+
"Not a Catalyst project. No catalyst-core or catalyst-core-internal dependency found in package.json.",
|
|
43
44
|
},
|
|
44
45
|
id: null,
|
|
45
46
|
}) + "\n"
|
|
@@ -52,7 +53,7 @@ if (projectInfo.notInstalled) {
|
|
|
52
53
|
jsonrpc: "2.0",
|
|
53
54
|
error: {
|
|
54
55
|
code: -32000,
|
|
55
|
-
message:
|
|
56
|
+
message: `${projectInfo.catalystPackageName} is listed in package.json (${projectInfo.catalystVersion}) but not installed in node_modules. Run: npm install`,
|
|
56
57
|
},
|
|
57
58
|
id: null,
|
|
58
59
|
}) + "\n"
|
package/mcp_v2/setup.js
CHANGED
|
@@ -33,7 +33,7 @@ function findCatalystRoot() {
|
|
|
33
33
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
|
|
34
34
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
|
|
35
35
|
if (deps["catalyst-core"]) {
|
|
36
|
-
return { dir, pkg, version: deps["catalyst-core"] }
|
|
36
|
+
return { dir, pkg, catalystPackageName: "catalyst-core", version: deps["catalyst-core"] }
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
dir = path.dirname(dir)
|
|
@@ -119,7 +119,9 @@ function seedKnowledgeBase(db, projectInfo) {
|
|
|
119
119
|
package_name: projectInfo.pkg.name || "unknown",
|
|
120
120
|
catalyst_version: projectInfo.version,
|
|
121
121
|
})
|
|
122
|
-
console.log(
|
|
122
|
+
console.log(
|
|
123
|
+
` ✓ Project context stored (${projectInfo.pkg.name}, ${projectInfo.catalystPackageName}@${projectInfo.version})`
|
|
124
|
+
)
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
// ── 5. sync_catalyst_docs (initial run) ───────────────────────────────────────
|
|
@@ -237,7 +239,7 @@ async function main() {
|
|
|
237
239
|
process.exit(1)
|
|
238
240
|
}
|
|
239
241
|
console.log(`✓ Catalyst project: ${projectInfo.pkg.name || projectInfo.dir}`)
|
|
240
|
-
console.log(`
|
|
242
|
+
console.log(` ${projectInfo.catalystPackageName}@${projectInfo.version}`)
|
|
241
243
|
|
|
242
244
|
// 2. Init DB
|
|
243
245
|
console.log("\nInitializing context.db...")
|
package/mcp_v2/tools/build.js
CHANGED
|
@@ -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(
|
|
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
|
)
|
package/mcp_v2/tools/config.js
CHANGED
|
@@ -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) =>
|
|
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
|
-
|
|
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
|
-
|
|
116
|
+
/useEffect.*fetch|useEffect.*axios|useEffect.*\.get\(|useEffect.*\.post\(/
|
|
117
117
|
)
|
|
118
|
-
const fetcherFiles = grepSrc(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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) {
|
package/mcp_v2/tools/tasks.js
CHANGED
|
@@ -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 :
|
|
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.
|
|
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.
|
|
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.
|
|
475
|
+
const dir = `${projectRoot}${path.sep}.mcp_tasks`
|
|
467
476
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
468
477
|
|
|
469
478
|
const mdPath = getMdPath(projectRoot, slug)
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catalyst-core",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
4
|
-
"main": "index.js",
|
|
3
|
+
"version": "0.1.0-beta.3",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
5
|
"description": "Web framework that provides great performance out of the box",
|
|
6
6
|
"bin": {
|
|
7
7
|
"catalyst": "bin/catalyst.js"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/tata1mg/catalyst-core.git"
|
|
11
|
+
"url": "https://github.com/tata1mg/catalyst-core.git",
|
|
12
|
+
"directory": "packages/catalyst-core"
|
|
12
13
|
},
|
|
13
14
|
"_moduleAliases": {
|
|
14
15
|
"@catalyst/template": ".",
|
|
@@ -37,10 +38,10 @@
|
|
|
37
38
|
},
|
|
38
39
|
"scripts": {
|
|
39
40
|
"lint": "eslint .",
|
|
40
|
-
"lint-staged": "lint-staged",
|
|
41
41
|
"prettify": "prettier . --write",
|
|
42
42
|
"prepare": "babel src --out-dir ./dist --ignore '**/.build/**' && mkdir -p dist/native && cp -r src/native/androidProject src/native/build.swift src/native/assets dist/native/ && mkdir -p dist/native/iosnativeWebView && tar -C src/native/iosnativeWebView --exclude='.build' --exclude='.package-config-hash' -cf - . | tar -C dist/native/iosnativeWebView -xf -",
|
|
43
|
-
"prepublishOnly": "npm
|
|
43
|
+
"prepublishOnly": "npm run prepare",
|
|
44
|
+
"test:fixture": "sh ../../scripts/test-catalyst-core.sh"
|
|
44
45
|
},
|
|
45
46
|
"license": "MIT",
|
|
46
47
|
"dependencies": {
|
|
@@ -96,7 +97,7 @@
|
|
|
96
97
|
"webpack": "^5.88.2",
|
|
97
98
|
"webpack-bundle-analyzer": "^4.9.1",
|
|
98
99
|
"webpack-cli": "^5.1.4",
|
|
99
|
-
"webpack-dev-server": "^
|
|
100
|
+
"webpack-dev-server": "^5.2.4",
|
|
100
101
|
"webpack-merge": "^5.9.0",
|
|
101
102
|
"webpack-node-externals": "^3.0.0",
|
|
102
103
|
"winston": "^3.11.0",
|
|
@@ -113,14 +114,6 @@
|
|
|
113
114
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
114
115
|
"eslint-plugin-risxss": "^2.1.0",
|
|
115
116
|
"eslint-plugin-security": "^3.0.0",
|
|
116
|
-
"husky": "^9.0.11",
|
|
117
|
-
"lint-staged": "^15.2.2",
|
|
118
117
|
"prettier": "^3.2.5"
|
|
119
|
-
},
|
|
120
|
-
"lint-staged": {
|
|
121
|
-
"*.{js,jsx}": [
|
|
122
|
-
"eslint .",
|
|
123
|
-
"prettier . --write"
|
|
124
|
-
]
|
|
125
118
|
}
|
|
126
119
|
}
|
package/.release-meta.json
DELETED
package/license
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 1mg Healthcare Solutions Private Limited.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
-
|
|
7
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
-
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
10
|
-
|