catalyst-core 0.0.3-canary.7 โ†’ 0.0.3-canary.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.md CHANGED
@@ -1,44 +1,58 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-canary.8] - 2025-01-06
4
+
5
+ - **App icon configuration**
6
+ - **Splash screen integration**
7
+ - **Url Whitelisting**
3
8
 
4
9
  ## [0.0.3-canary.7] - 2025-01-06
5
10
 
6
11
  ### ๐Ÿš€ Features
7
- - **Native API Integration**: Enhanced native module integration for better performance
8
- - **Automated IP Resolution**: Intelligent network detection eliminates manual IP configuration
9
- - **Universal Build System**: Streamlined one-command builds for both Android and iOS platforms
10
- - **Enhanced CLI Interface**: Improved command-line tools for better developer experience
11
12
 
12
- ### ๐Ÿ› Bug Fixes
13
- - Fixed IP address detection issues on development environments
14
- - Resolved build configuration conflicts between platforms
15
- - Improved error handling and recovery mechanisms
13
+ - **Native API Integration**: Enhanced native module integration for better performance
14
+ - **Automated IP Resolution**: Intelligent network detection eliminates manual IP configuration
15
+ - **Universal Build System**: Streamlined one-command builds for both Android and iOS platforms
16
+ - **Enhanced CLI Interface**: Improved command-line tools for better developer experience
17
+
18
+ ### ๐Ÿ› Bug Fixes
19
+
20
+ - Fixed IP address detection issues on development environments
21
+ - Resolved build configuration conflicts between platforms
22
+ - Improved error handling and recovery mechanisms
16
23
 
17
24
  ### ๐Ÿ”ง Improvements
18
- - **Performance**: Optimized native module loading and execution
19
- - **Developer Experience**: Enhanced CLI with better error messages and debugging info
20
- - **Configuration**: Dynamic config updates without requiring restarts
21
- - **Build Process**: Faster and more reliable build pipeline
25
+
26
+ - **Performance**: Optimized native module loading and execution
27
+ - **Developer Experience**: Enhanced CLI with better error messages and debugging info
28
+ - **Configuration**: Dynamic config updates without requiring restarts
29
+ - **Build Process**: Faster and more reliable build pipeline
22
30
 
23
31
  ### ๐Ÿงช Testing & Validation
24
- - โœ… **Android Build**: Verified APK generation, installation, and runtime behavior
25
- - โœ… **iOS Build**: Tested build process and device compatibility
26
- - โœ… **IP Detection**: Validated automatic network resolution across platforms
27
- - โœ… **Native Modules**: Confirmed API integration and performance benchmarks
28
- - โœ… **CLI Operations**: Tested enhanced command-line workflows and error handling
32
+
33
+ - โœ… **Android Build**: Verified APK generation, installation, and runtime behavior
34
+ - โœ… **iOS Build**: Tested build process and device compatibility
35
+ - โœ… **IP Detection**: Validated automatic network resolution across platforms
36
+ - โœ… **Native Modules**: Confirmed API integration and performance benchmarks
37
+ - โœ… **CLI Operations**: Tested enhanced command-line workflows and error handling
29
38
 
30
39
  ### ๐Ÿ”„ Breaking Changes
40
+
31
41
  **None** - All changes maintain backward compatibility while enhancing existing functionality
32
42
 
33
43
  ### ๐Ÿ“ Technical Details
34
- - Improved native API bridge for better cross-platform communication
35
- - Enhanced error reporting with detailed stack traces and suggestions
36
- - Optimized build configuration for faster development cycles
37
- - Added comprehensive logging for debugging and monitoring
44
+
45
+ - Improved native API bridge for better cross-platform communication
46
+ - Enhanced error reporting with detailed stack traces and suggestions
47
+ - Optimized build configuration for faster development cycles
48
+ - Added comprehensive logging for debugging and monitoring
38
49
 
39
50
  ### ๐Ÿ”— Migration Notes
40
- - No migration steps required
41
- - Existing projects will automatically benefit from improvements
42
- - Optional: Update CLI usage to leverage new enhanced features
51
+
52
+ - No migration steps required
53
+ - Existing projects will automatically benefit from improvements
54
+ - Optional: Update CLI usage to leverage new enhanced features
55
+
43
56
  ## Version
44
- - Target version: 0.0.3-canary.7
57
+
58
+ - Target version: 0.0.3-canary.7
@@ -1,3 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
1
2
  <project version="4">
2
3
  <component name="ExternalStorageConfigurationManager" enabled="true" />
3
4
  <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK" />
@@ -1,6 +1,14 @@
1
1
  import java.net.NetworkInterface
2
2
  import java.io.File
3
3
  import java.util.Properties
4
+ import org.json.JSONObject
5
+ import org.json.JSONArray
6
+
7
+ buildscript {
8
+ dependencies {
9
+ classpath("org.json:json:20231013")
10
+ }
11
+ }
4
12
 
5
13
  val configPath: String? by project.properties
6
14
  val keystorePassword: String? by project.properties // Changed from keyStorePassword to keystorePassword
@@ -133,6 +141,7 @@ dependencies {
133
141
  androidTestImplementation(libs.androidx.espresso.core)
134
142
  implementation("androidx.webkit:webkit:1.12.1")
135
143
  implementation("org.json:json:20231013")
144
+ implementation("androidx.core:core-splashscreen:1.0.1")
136
145
 
137
146
  // Ktor Server dependencies for FrameworkServer (~200KB total)
138
147
  implementation("io.ktor:ktor-server-core:2.3.7")
@@ -196,12 +205,15 @@ tasks.register("generateWebViewConfig") {
196
205
  }
197
206
 
198
207
  val configContent = configJsonFile.readText()
199
-
200
- // Extract WEBVIEW_CONFIG section
201
- val webviewConfigRegex = """"WEBVIEW_CONFIG"\s*:\s*\{([^}]*)\}""".toRegex()
202
- val webviewConfigMatch = webviewConfigRegex.find(configContent)
203
- val webviewConfigContent = webviewConfigMatch?.groupValues?.get(1)
204
-
208
+
209
+ val jsonObject = JSONObject(configContent)
210
+
211
+ if (!jsonObject.has("WEBVIEW_CONFIG")) {
212
+ throw GradleException("WEBVIEW_CONFIG not found in config file")
213
+ }
214
+
215
+ val webviewConfig = jsonObject.getJSONObject("WEBVIEW_CONFIG")
216
+
205
217
  val properties = Properties()
206
218
 
207
219
  // Set different IP based on build type
@@ -215,30 +227,36 @@ tasks.register("generateWebViewConfig") {
215
227
  properties.setProperty("buildOptimisation", "false")
216
228
  }
217
229
 
218
- // Parse top-level properties
219
- val topLevelRegex = """"([^"]+)"\s*:\s*"([^"]+)"""".toRegex()
220
- webviewConfigContent?.let {
221
- topLevelRegex.findAll(it).forEach { matchResult ->
222
- val (key, value) = matchResult.destructured
223
- if (key != "android") {
224
- properties.setProperty(key, value)
230
+ fun extractProperties(jsonObj: org.json.JSONObject, prefix: String = "") {
231
+ try {
232
+ val keys = jsonObj.keys()
233
+
234
+ while (keys.hasNext()) {
235
+ val key = keys.next()
236
+ val value = jsonObj.opt(key)
237
+ val fullKey = if (prefix.isEmpty()) key else "$prefix.$key"
238
+
239
+ when (value) {
240
+ is org.json.JSONObject -> {
241
+ extractProperties(value, fullKey)
242
+ }
243
+ is org.json.JSONArray -> {
244
+ val arrayValues = (0 until value.length()).map { i ->
245
+ value.opt(i).toString()
246
+ }.joinToString(",")
247
+ properties.setProperty(fullKey, arrayValues)
248
+ }
249
+ else -> {
250
+ properties.setProperty(fullKey, value.toString())
251
+ }
252
+ }
225
253
  }
254
+ } catch (e: Exception) {
255
+ throw RuntimeException("Failed to extract properties from JSON at prefix: $prefix", e)
226
256
  }
227
257
  }
228
258
 
229
- // Parse android object specifically
230
- val androidRegex = """"android"\s*:\s*\{([^}]*)\}""".toRegex()
231
- val androidMatch = androidRegex.find(webviewConfigContent ?: "")
232
- val androidContent = androidMatch?.groupValues?.get(1)
233
-
234
- // Parse android object properties
235
- val androidPropsRegex = """"([^"]+)"\s*:\s*"([^"]+)"""".toRegex()
236
- androidContent?.let {
237
- androidPropsRegex.findAll(it).forEach { matchResult ->
238
- val (key, value) = matchResult.destructured
239
- properties.setProperty("android.$key", value)
240
- }
241
- }
259
+ extractProperties(webviewConfig)
242
260
 
243
261
  // Set production-specific properties for release builds
244
262
  if (gradle.startParameter.taskNames.any { it.contains("Release") || it.contains("release") }) {
@@ -2,6 +2,7 @@ package io.yourname.androidproject
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.content.Intent
5
6
  import android.graphics.Bitmap
6
7
  import android.net.Uri
7
8
  import android.os.Bundle
@@ -11,6 +12,8 @@ import android.webkit.*
11
12
  import android.widget.ProgressBar
12
13
  import androidx.webkit.WebViewAssetLoader
13
14
  import io.yourname.androidproject.WebCacheManager
15
+ import io.yourname.androidproject.isUrlAllowed
16
+ import io.yourname.androidproject.isExternalDomain
14
17
  import kotlinx.coroutines.*
15
18
  import java.util.Properties
16
19
 
@@ -34,6 +37,7 @@ class CustomWebView(
34
37
  private var isInitialPageLoaded: Boolean = false
35
38
  private var buildOptimisation: Boolean = false // Added property for build optimization
36
39
  private lateinit var assetLoader: WebViewAssetLoader
40
+ private var allowedUrls: List<String> = emptyList()
37
41
 
38
42
  // Counters for asset loading statistics
39
43
  private var assetLoadAttempts = 0
@@ -56,6 +60,12 @@ class CustomWebView(
56
60
  // Parse buildOptimisation property
57
61
  buildOptimisation = properties.getProperty("buildOptimisation", "false").toBoolean()
58
62
 
63
+ // Load allowed URLs from properties
64
+ allowedUrls = properties.getProperty("accessControl.allowedUrls", "")
65
+ .split(",")
66
+ .map { it.trim() }
67
+ .filter { it.isNotEmpty() }
68
+
59
69
  // Set initial flags based on buildOptimisation
60
70
  if (buildOptimisation) {
61
71
  isInitialApiCalled = false
@@ -71,6 +81,7 @@ class CustomWebView(
71
81
  Log.d(TAG, "Cache Pattern: $cachePatterns")
72
82
  Log.d(TAG, "API Base URL: $apiBaseUrl")
73
83
  Log.d(TAG, "Build Optimisation: $buildOptimisation")
84
+ Log.d(TAG, "Allowed URLs: $allowedUrls")
74
85
  Log.d(TAG, "Initial API Called: $isInitialApiCalled")
75
86
  Log.d(TAG, "Initial Page Loaded: $isInitialPageLoaded")
76
87
  }
@@ -198,6 +209,22 @@ class CustomWebView(
198
209
  }
199
210
  }
200
211
 
212
+
213
+
214
+ private fun openInInAppBrowser(url: String) {
215
+ try {
216
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
217
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
218
+ context.startActivity(intent)
219
+ if (BuildConfig.DEBUG) {
220
+ Log.d(TAG, "๐ŸŒ Opening external URL in in-app browser: $url")
221
+ }
222
+ } catch (e: Exception) {
223
+ Log.e(TAG, "โŒ Failed to open URL in in-app browser: $url", e)
224
+ }
225
+ }
226
+
227
+
201
228
  private fun isApiCall(url: String): Boolean {
202
229
  // Check if URL is an API call based on your API base URL
203
230
  return apiBaseUrl.isNotEmpty() && url.startsWith(apiBaseUrl)
@@ -290,6 +317,25 @@ class CustomWebView(
290
317
  webView.webViewClient = object : WebViewClient() {
291
318
  override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
292
319
  request?.url?.let { url ->
320
+ val urlString = url.toString()
321
+
322
+ // Check if URL is an external domain
323
+ if (url.scheme in listOf("http", "https") && isExternalDomain(urlString, allowedUrls)) {
324
+ if (BuildConfig.DEBUG) {
325
+ Log.d(TAG, "๐ŸŒ External domain detected, opening in in-app browser: $urlString")
326
+ }
327
+ openInInAppBrowser(urlString)
328
+ return true
329
+ }
330
+
331
+ // Check if URL is allowed for internal navigation
332
+ if (!isUrlAllowed(urlString, allowedUrls)) {
333
+ if (BuildConfig.DEBUG) {
334
+ Log.w(TAG, "๐Ÿšซ URL blocked by access control: $urlString")
335
+ }
336
+ return true
337
+ }
338
+
293
339
  // Let WebView handle loading non-API HTTP/HTTPS URLs
294
340
  if (url.scheme in listOf("http", "https")) {
295
341
  return false
@@ -307,6 +353,14 @@ class CustomWebView(
307
353
  Log.d(TAG, "๐Ÿ”„ Intercepting request for: $url on thread: ${Thread.currentThread().name}")
308
354
  }
309
355
 
356
+ if (!isUrlAllowed(url, allowedUrls)) {
357
+ if (BuildConfig.DEBUG) {
358
+ Log.w(TAG, "๐Ÿšซ Network request blocked by access control: $url")
359
+ }
360
+ // Return an empty response to block the request
361
+ return WebResourceResponse("text/plain", "utf-8", null)
362
+ }
363
+
310
364
  // Handle the initial route request - intercept first request regardless of host
311
365
  if (!isInitialApiCalled && request.method == "GET") {
312
366
  isInitialApiCalled = true
@@ -10,6 +10,7 @@ import io.yourname.androidproject.NativeBridge
10
10
  import kotlinx.coroutines.CoroutineScope
11
11
  import kotlinx.coroutines.MainScope
12
12
  import kotlinx.coroutines.cancelChildren
13
+ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
13
14
 
14
15
  class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
15
16
 
@@ -20,6 +21,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
20
21
  private lateinit var properties: Properties
21
22
  private var isHardwareAccelerationEnabled = false
22
23
  private var currentUrl: String = ""
24
+ private var splashStartTime: Long = 0
23
25
 
24
26
  private fun enableHardwareAcceleration() {
25
27
  if (!isHardwareAccelerationEnabled) {
@@ -45,6 +47,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
45
47
  }
46
48
 
47
49
  override fun onCreate(savedInstanceState: Bundle?) {
50
+ val splashScreen = installSplashScreen()
48
51
  super.onCreate(savedInstanceState)
49
52
  if (BuildConfig.DEBUG) {
50
53
  Log.d(TAG, "๐Ÿ“ฑ onCreate started on thread: ${Thread.currentThread().name}")
@@ -63,6 +66,10 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
63
66
  properties.setProperty("buildOptimisation", (!BuildConfig.DEBUG).toString())
64
67
  }
65
68
 
69
+ // Configure splash screen
70
+ splashStartTime = System.currentTimeMillis()
71
+ configureSplashScreen(splashScreen)
72
+
66
73
  // Setup UI
67
74
  binding = ActivityMainBinding.inflate(layoutInflater)
68
75
  supportActionBar?.hide()
@@ -159,4 +166,21 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
159
166
  customWebView.destroy()
160
167
  super.onDestroy()
161
168
  }
169
+
170
+ private fun configureSplashScreen(splashScreen: androidx.core.splashscreen.SplashScreen) {
171
+ val durationProperty = properties.getProperty("splashScreen.duration")
172
+
173
+ splashScreen.setKeepOnScreenCondition {
174
+ val webViewLoaded = ::customWebView.isInitialized &&
175
+ customWebView.getWebView().progress >= 100
176
+
177
+ if (durationProperty != null) {
178
+ val duration = durationProperty.toLong()
179
+ val timeElapsed = System.currentTimeMillis() - splashStartTime >= duration
180
+ !timeElapsed
181
+ } else {
182
+ !webViewLoaded
183
+ }
184
+ }
185
+ }
162
186
  }
@@ -1,14 +1,14 @@
1
1
  "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _NativeInterfaces=require("./constants/NativeInterfaces.js");class WebBridge{constructor(){this.handlers=new Map();this.initialized=false;this._logInitialization();}/**
2
2
  * Log initialization info
3
- */_logInitialization(){console.log("๐ŸŒ‰ WebBridge initialized");console.log("๐ŸŒ‰ Available callback interfaces:",_NativeInterfaces.ALL_CALLBACKS.length);if(process.env.NODE_ENV==='development'){(0,_NativeInterfaces.debugInterfaces)();}}/**
3
+ */_logInitialization(){console.log("๐ŸŒ‰ WebBridge initialized");console.log("๐ŸŒ‰ Available callback interfaces:",_NativeInterfaces.ALL_CALLBACKS.length);if(process.env.NODE_ENV==="development"){(0,_NativeInterfaces.debugInterfaces)();}}/**
4
4
  * Static method to initialize WebBridge on window object
5
- */static init=()=>{if(typeof window==='undefined'){console.error("๐ŸŒ‰ WebBridge cannot be initialized outside the browser!");return null;}if(window.WebBridge){console.warn("๐ŸŒ‰ WebBridge already initialized!");return window.WebBridge;}const bridge=new WebBridge();window.WebBridge=bridge;bridge.initialized=true;console.log("๐ŸŒ‰ WebBridge created and attached to window");return bridge;};/**
5
+ */static init=()=>{if(typeof window==="undefined"){console.error("๐ŸŒ‰ WebBridge cannot be initialized outside the browser!");return null;}if(window.WebBridge){console.warn("๐ŸŒ‰ WebBridge already initialized!");return window.WebBridge;}const bridge=new WebBridge();window.WebBridge=bridge;bridge.initialized=true;console.log("๐ŸŒ‰ WebBridge created and attached to window");return bridge;};/**
6
6
  * Main callback handler called by native platforms
7
7
  * @param {string} interfaceName - The callback interface name
8
8
  * @param {any} data - Data from native platform
9
- */callback=(interfaceName,data)=>{console.log(`๐ŸŒ‰ WebBridge callback: ${interfaceName}`,data?{data}:'');// Validate interface
9
+ */callback=(interfaceName,data)=>{console.log(`๐ŸŒ‰ WebBridge callback: ${interfaceName}`,data?{data}:"");// Validate interface
10
10
  if(!(0,_NativeInterfaces.isValidCallback)(interfaceName)){console.error(`๐ŸŒ‰ Invalid callback interface: ${interfaceName}`);console.log("๐ŸŒ‰ Available callbacks:",_NativeInterfaces.ALL_CALLBACKS);return;}if(!this.handlers.has(interfaceName)){console.warn(`๐ŸŒ‰ No handler registered for interface: ${interfaceName}`);return;}try{const handler=this.handlers.get(interfaceName);handler(data);}catch(error){console.error(`๐ŸŒ‰ Error executing callback for ${interfaceName}:`,error);// In development, provide more helpful error info
11
- if(process.env.NODE_ENV==='development'){console.error('๐ŸŒ‰ Handler details:',{interfaceName,data,handlerType:typeof this.handlers.get(interfaceName)});}}};/**
11
+ if(process.env.NODE_ENV==="development"){console.error("๐ŸŒ‰ Handler details:",{interfaceName,data,handlerType:typeof this.handlers.get(interfaceName)});}}};/**
12
12
  * Register a callback handler for a native interface
13
13
  * @param {string} interfaceName - The callback interface name (from NATIVE_CALLBACKS)
14
14
  * @param {function} callback - The callback function to execute
@@ -39,4 +39,4 @@ if(this.handlers.has(interfaceName)){console.warn(`๐ŸŒ‰ Interface ${interfaceNam
39
39
  * Test callback functionality (for debugging)
40
40
  * @param {string} interfaceName - The callback interface to test
41
41
  * @param {any} testData - Test data to pass
42
- */testCallback=(interfaceName,testData='test')=>{if(process.env.NODE_ENV!=='development'){console.warn('๐ŸŒ‰ testCallback is only available in development mode');return;}console.log(`๐ŸŒ‰ Testing callback: ${interfaceName}`);this.callback(interfaceName,testData);};}var _default=exports.default=WebBridge;
42
+ */testCallback=(interfaceName,testData="test")=>{if(process.env.NODE_ENV!=="development"){console.warn("๐ŸŒ‰ testCallback is only available in development mode");return;}console.log(`๐ŸŒ‰ Testing callback: ${interfaceName}`);this.callback(interfaceName,testData);};}var _default=exports.default=WebBridge;
@@ -2,21 +2,21 @@
2
2
  // Centralized definitions for all native interface communications
3
3
  // Command Interfaces: Web โ†’ Native (outgoing commands)
4
4
  const NATIVE_COMMANDS=exports.NATIVE_COMMANDS={// Camera commands
5
- OPEN_CAMERA:'openCamera',REQUEST_CAMERA_PERMISSION:'requestCameraPermission',// File commands
6
- PICK_FILE:'pickFile',OPEN_FILE_WITH_INTENT:'openFileWithIntent',// Haptic feedback commands
7
- REQUEST_HAPTIC_FEEDBACK:'requestHapticFeedback'};// Callback Interfaces: Native โ†’ Web (incoming events/callbacks)
5
+ OPEN_CAMERA:"openCamera",REQUEST_CAMERA_PERMISSION:"requestCameraPermission",// File commands
6
+ PICK_FILE:"pickFile",OPEN_FILE_WITH_INTENT:"openFileWithIntent",// Haptic feedback commands
7
+ REQUEST_HAPTIC_FEEDBACK:"requestHapticFeedback"};// Callback Interfaces: Native โ†’ Web (incoming events/callbacks)
8
8
  const NATIVE_CALLBACKS=exports.NATIVE_CALLBACKS={// Camera callbacks
9
- CAMERA_PERMISSION_STATUS:'CAMERA_PERMISSION_STATUS',ON_CAMERA_CAPTURE:'ON_CAMERA_CAPTURE',ON_CAMERA_ERROR:'ON_CAMERA_ERROR',// File picker callbacks
10
- ON_FILE_PICKED:'ON_FILE_PICKED',ON_FILE_PICK_ERROR:'ON_FILE_PICK_ERROR',ON_FILE_PICK_CANCELLED:'ON_FILE_PICK_CANCELLED',ON_FILE_PICK_STATE_UPDATE:'ON_FILE_PICK_STATE_UPDATE',// Intent callbacks
11
- ON_INTENT_SUCCESS:'ON_INTENT_SUCCESS',ON_INTENT_ERROR:'ON_INTENT_ERROR',ON_INTENT_CANCELLED:'ON_INTENT_CANCELLED',// Haptic feedback callbacks
12
- HAPTIC_FEEDBACK:'HAPTIC_FEEDBACK'};// Interface Categories for easier management
9
+ CAMERA_PERMISSION_STATUS:"CAMERA_PERMISSION_STATUS",ON_CAMERA_CAPTURE:"ON_CAMERA_CAPTURE",ON_CAMERA_ERROR:"ON_CAMERA_ERROR",// File picker callbacks
10
+ ON_FILE_PICKED:"ON_FILE_PICKED",ON_FILE_PICK_ERROR:"ON_FILE_PICK_ERROR",ON_FILE_PICK_CANCELLED:"ON_FILE_PICK_CANCELLED",ON_FILE_PICK_STATE_UPDATE:"ON_FILE_PICK_STATE_UPDATE",// Intent callbacks
11
+ ON_INTENT_SUCCESS:"ON_INTENT_SUCCESS",ON_INTENT_ERROR:"ON_INTENT_ERROR",ON_INTENT_CANCELLED:"ON_INTENT_CANCELLED",// Haptic feedback callbacks
12
+ HAPTIC_FEEDBACK:"HAPTIC_FEEDBACK"};// Interface Categories for easier management
13
13
  const INTERFACE_CATEGORIES=exports.INTERFACE_CATEGORIES={CAMERA:{commands:[NATIVE_COMMANDS.OPEN_CAMERA,NATIVE_COMMANDS.REQUEST_CAMERA_PERMISSION],callbacks:[NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,NATIVE_CALLBACKS.ON_CAMERA_ERROR]},FILE:{commands:[NATIVE_COMMANDS.PICK_FILE,NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_FILE_PICKED,NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE]},INTENT:{commands:[NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_INTENT_SUCCESS,NATIVE_CALLBACKS.ON_INTENT_ERROR,NATIVE_CALLBACKS.ON_INTENT_CANCELLED]},HAPTIC:{commands:[NATIVE_COMMANDS.REQUEST_HAPTIC_FEEDBACK],callbacks:[NATIVE_CALLBACKS.HAPTIC_FEEDBACK]}};// All available interfaces (for validation)
14
14
  const ALL_COMMANDS=exports.ALL_COMMANDS=Object.values(NATIVE_COMMANDS);const ALL_CALLBACKS=exports.ALL_CALLBACKS=Object.values(NATIVE_CALLBACKS);const ALL_INTERFACES=exports.ALL_INTERFACES=[...ALL_COMMANDS,...ALL_CALLBACKS];// Permission status constants
15
- const PERMISSION_STATUS=exports.PERMISSION_STATUS={GRANTED:'GRANTED',DENIED:'DENIED',NOT_DETERMINED:'NOT_DETERMINED',RESTRICTED:'RESTRICTED'};// Haptic feedback types
16
- const HAPTIC_FEEDBACK_TYPES=exports.HAPTIC_FEEDBACK_TYPES={LIGHT:'light',MEDIUM:'medium',HEAVY:'heavy',SUCCESS:'success',WARNING:'warning',ERROR:'error'};// File picker state constants
17
- const FILE_PICKER_STATES=exports.FILE_PICKER_STATES={OPENING:'opening',PROCESSING:'processing',IDLE:null};// Intent processing states
18
- const INTENT_STATES=exports.INTENT_STATES={PROCESSING:'processing',DOWNLOADING:'downloading',OPENING:'opening',IDLE:null};// Response status constants
19
- const RESPONSE_STATUS=exports.RESPONSE_STATUS={SUCCESS:'SUCCESS',ERROR:'ERROR',CANCELLED:'CANCELLED'};// Utility functions for interface validation
15
+ const PERMISSION_STATUS=exports.PERMISSION_STATUS={GRANTED:"GRANTED",DENIED:"DENIED",NOT_DETERMINED:"NOT_DETERMINED",RESTRICTED:"RESTRICTED"};// Haptic feedback types
16
+ const HAPTIC_FEEDBACK_TYPES=exports.HAPTIC_FEEDBACK_TYPES={LIGHT:"light",MEDIUM:"medium",HEAVY:"heavy",SUCCESS:"success",WARNING:"warning",ERROR:"error"};// File picker state constants
17
+ const FILE_PICKER_STATES=exports.FILE_PICKER_STATES={OPENING:"opening",PROCESSING:"processing",IDLE:null};// Intent processing states
18
+ const INTENT_STATES=exports.INTENT_STATES={PROCESSING:"processing",DOWNLOADING:"downloading",OPENING:"opening",IDLE:null};// Response status constants
19
+ const RESPONSE_STATUS=exports.RESPONSE_STATUS={SUCCESS:"SUCCESS",ERROR:"ERROR",CANCELLED:"CANCELLED"};// Utility functions for interface validation
20
20
  const isValidCommand=command=>ALL_COMMANDS.includes(command);exports.isValidCommand=isValidCommand;const isValidCallback=callback=>ALL_CALLBACKS.includes(callback);exports.isValidCallback=isValidCallback;const isValidInterface=interfaceName=>ALL_INTERFACES.includes(interfaceName);// Get interfaces by category
21
21
  exports.isValidInterface=isValidInterface;const getInterfacesByCategory=category=>{const categoryData=INTERFACE_CATEGORIES[category.toUpperCase()];if(!categoryData){throw new Error(`Invalid category: ${category}`);}return categoryData;};// Debug helper to list all interfaces
22
- exports.getInterfacesByCategory=getInterfacesByCategory;const debugInterfaces=()=>{console.group('๐ŸŒ‰ Native Bridge Interfaces');console.log('Commands:',ALL_COMMANDS);console.log('Callbacks:',ALL_CALLBACKS);console.log('Categories:',Object.keys(INTERFACE_CATEGORIES));console.groupEnd();};exports.debugInterfaces=debugInterfaces;var _default=exports.default={NATIVE_COMMANDS,NATIVE_CALLBACKS,INTERFACE_CATEGORIES,ALL_COMMANDS,ALL_CALLBACKS,ALL_INTERFACES,PERMISSION_STATUS,HAPTIC_FEEDBACK_TYPES,FILE_PICKER_STATES,INTENT_STATES,RESPONSE_STATUS,isValidCommand,isValidCallback,isValidInterface,getInterfacesByCategory,debugInterfaces};
22
+ exports.getInterfacesByCategory=getInterfacesByCategory;const debugInterfaces=()=>{console.group("๐ŸŒ‰ Native Bridge Interfaces");console.log("Commands:",ALL_COMMANDS);console.log("Callbacks:",ALL_CALLBACKS);console.log("Categories:",Object.keys(INTERFACE_CATEGORIES));console.groupEnd();};exports.debugInterfaces=debugInterfaces;var _default=exports.default={NATIVE_COMMANDS,NATIVE_CALLBACKS,INTERFACE_CATEGORIES,ALL_COMMANDS,ALL_CALLBACKS,ALL_INTERFACES,PERMISSION_STATUS,HAPTIC_FEEDBACK_TYPES,FILE_PICKER_STATES,INTENT_STATES,RESPONSE_STATUS,isValidCommand,isValidCallback,isValidInterface,getInterfacesByCategory,debugInterfaces};
@@ -2,16 +2,16 @@
2
2
  * Standardized Error System for Catalyst Framework
3
3
  * Translates native errors into web developer-friendly messages
4
4
  */// Error categories for logical grouping
5
- const ERROR_CATEGORIES=exports.ERROR_CATEGORIES={PERMISSION:'PERMISSION',NETWORK:'NETWORK',FILE_SYSTEM:'FILE_SYSTEM',USER_ACTION:'USER_ACTION',VALIDATION:'VALIDATION',SYSTEM:'SYSTEM',UNKNOWN:'UNKNOWN'};// Standardized error codes
5
+ const ERROR_CATEGORIES=exports.ERROR_CATEGORIES={PERMISSION:"PERMISSION",NETWORK:"NETWORK",FILE_SYSTEM:"FILE_SYSTEM",USER_ACTION:"USER_ACTION",VALIDATION:"VALIDATION",SYSTEM:"SYSTEM",UNKNOWN:"UNKNOWN"};// Standardized error codes
6
6
  const ERROR_CODES=exports.ERROR_CODES={// Permission errors
7
- PERMISSION_DENIED:'PERMISSION_DENIED',PERMISSION_REQUIRED:'PERMISSION_REQUIRED',// Network errors
8
- NETWORK_UNAVAILABLE:'NETWORK_UNAVAILABLE',DOWNLOAD_FAILED:'DOWNLOAD_FAILED',// File system errors
9
- FILE_NOT_FOUND:'FILE_NOT_FOUND',FILE_TOO_LARGE:'FILE_TOO_LARGE',STORAGE_FULL:'STORAGE_FULL',FILE_CORRUPTED:'FILE_CORRUPTED',// User action errors
10
- OPERATION_CANCELLED:'OPERATION_CANCELLED',NO_FILE_SELECTED:'NO_FILE_SELECTED',// Validation errors
11
- INVALID_FILE_TYPE:'INVALID_FILE_TYPE',INVALID_PARAMETERS:'INVALID_PARAMETERS',// System errors
12
- BRIDGE_UNAVAILABLE:'BRIDGE_UNAVAILABLE',FEATURE_UNSUPPORTED:'FEATURE_UNSUPPORTED',INTERNAL_ERROR:'INTERNAL_ERROR',// Camera specific errors
13
- CAMERA_UNAVAILABLE:'CAMERA_UNAVAILABLE',CAMERA_IN_USE:'CAMERA_IN_USE'};// Error definitions with user-friendly messages and recovery actions
14
- const ERROR_DEFINITIONS=exports.ERROR_DEFINITIONS={[ERROR_CODES.PERMISSION_DENIED]:{category:ERROR_CATEGORIES.PERMISSION,defaultMessage:'Permission denied',defaultDetails:'Required permission was not granted by the user',recoverable:true,suggestedAction:'Grant permission in device settings and try again'},[ERROR_CODES.PERMISSION_REQUIRED]:{category:ERROR_CATEGORIES.PERMISSION,defaultMessage:'Permission required',defaultDetails:'This operation requires additional permissions',recoverable:true,suggestedAction:'Grant the required permission to continue'},[ERROR_CODES.NETWORK_UNAVAILABLE]:{category:ERROR_CATEGORIES.NETWORK,defaultMessage:'No internet connection',defaultDetails:'Network connection is required for this operation',recoverable:true,suggestedAction:'Check your internet connection and try again'},[ERROR_CODES.FILE_NOT_FOUND]:{category:ERROR_CATEGORIES.FILE_SYSTEM,defaultMessage:'File not found',defaultDetails:'The requested file could not be located',recoverable:false,suggestedAction:'The file may have been moved or deleted'},[ERROR_CODES.FILE_TOO_LARGE]:{category:ERROR_CATEGORIES.VALIDATION,defaultMessage:'File too large',defaultDetails:'File exceeds the maximum size limit',recoverable:true,suggestedAction:'Choose a smaller file or compress the current file'},[ERROR_CODES.STORAGE_FULL]:{category:ERROR_CATEGORIES.FILE_SYSTEM,defaultMessage:'Storage full',defaultDetails:'Device storage is full and cannot save the file',recoverable:true,suggestedAction:'Free up some storage space and try again'},[ERROR_CODES.OPERATION_CANCELLED]:{category:ERROR_CATEGORIES.USER_ACTION,defaultMessage:'Operation cancelled',defaultDetails:'The operation was cancelled by the user',recoverable:true,suggestedAction:'Try again if you want to complete the operation'},[ERROR_CODES.INVALID_FILE_TYPE]:{category:ERROR_CATEGORIES.VALIDATION,defaultMessage:'Invalid file type',defaultDetails:'The selected file type is not supported',recoverable:true,suggestedAction:'Choose a file with a supported format'},[ERROR_CODES.BRIDGE_UNAVAILABLE]:{category:ERROR_CATEGORIES.SYSTEM,defaultMessage:'Native feature unavailable',defaultDetails:'The native bridge is not available or not initialized',recoverable:false,suggestedAction:'Use web fallback if available'},[ERROR_CODES.FEATURE_UNSUPPORTED]:{category:ERROR_CATEGORIES.SYSTEM,defaultMessage:'Feature not supported',defaultDetails:'This feature is not supported on the current platform',recoverable:false,suggestedAction:'Try using an alternative method or device'},[ERROR_CODES.CAMERA_UNAVAILABLE]:{category:ERROR_CATEGORIES.SYSTEM,defaultMessage:'Camera unavailable',defaultDetails:'Camera hardware is not available or accessible',recoverable:false,suggestedAction:'Check if your device has a camera and try again'},[ERROR_CODES.CAMERA_IN_USE]:{category:ERROR_CATEGORIES.SYSTEM,defaultMessage:'Camera in use',defaultDetails:'Camera is being used by another application',recoverable:true,suggestedAction:'Close other camera apps and try again'},[ERROR_CODES.INTERNAL_ERROR]:{category:ERROR_CATEGORIES.UNKNOWN,defaultMessage:'An unexpected error occurred',defaultDetails:'An internal error occurred while processing the request',recoverable:true,suggestedAction:'Try again or contact support if the problem persists'}};/**
7
+ PERMISSION_DENIED:"PERMISSION_DENIED",PERMISSION_REQUIRED:"PERMISSION_REQUIRED",// Network errors
8
+ NETWORK_UNAVAILABLE:"NETWORK_UNAVAILABLE",DOWNLOAD_FAILED:"DOWNLOAD_FAILED",// File system errors
9
+ FILE_NOT_FOUND:"FILE_NOT_FOUND",FILE_TOO_LARGE:"FILE_TOO_LARGE",STORAGE_FULL:"STORAGE_FULL",FILE_CORRUPTED:"FILE_CORRUPTED",// User action errors
10
+ OPERATION_CANCELLED:"OPERATION_CANCELLED",NO_FILE_SELECTED:"NO_FILE_SELECTED",// Validation errors
11
+ INVALID_FILE_TYPE:"INVALID_FILE_TYPE",INVALID_PARAMETERS:"INVALID_PARAMETERS",// System errors
12
+ BRIDGE_UNAVAILABLE:"BRIDGE_UNAVAILABLE",FEATURE_UNSUPPORTED:"FEATURE_UNSUPPORTED",INTERNAL_ERROR:"INTERNAL_ERROR",// Camera specific errors
13
+ CAMERA_UNAVAILABLE:"CAMERA_UNAVAILABLE",CAMERA_IN_USE:"CAMERA_IN_USE"};// Error definitions with user-friendly messages and recovery actions
14
+ const ERROR_DEFINITIONS=exports.ERROR_DEFINITIONS={[ERROR_CODES.PERMISSION_DENIED]:{category:ERROR_CATEGORIES.PERMISSION,defaultMessage:"Permission denied",defaultDetails:"Required permission was not granted by the user",recoverable:true,suggestedAction:"Grant permission in device settings and try again"},[ERROR_CODES.PERMISSION_REQUIRED]:{category:ERROR_CATEGORIES.PERMISSION,defaultMessage:"Permission required",defaultDetails:"This operation requires additional permissions",recoverable:true,suggestedAction:"Grant the required permission to continue"},[ERROR_CODES.NETWORK_UNAVAILABLE]:{category:ERROR_CATEGORIES.NETWORK,defaultMessage:"No internet connection",defaultDetails:"Network connection is required for this operation",recoverable:true,suggestedAction:"Check your internet connection and try again"},[ERROR_CODES.FILE_NOT_FOUND]:{category:ERROR_CATEGORIES.FILE_SYSTEM,defaultMessage:"File not found",defaultDetails:"The requested file could not be located",recoverable:false,suggestedAction:"The file may have been moved or deleted"},[ERROR_CODES.FILE_TOO_LARGE]:{category:ERROR_CATEGORIES.VALIDATION,defaultMessage:"File too large",defaultDetails:"File exceeds the maximum size limit",recoverable:true,suggestedAction:"Choose a smaller file or compress the current file"},[ERROR_CODES.STORAGE_FULL]:{category:ERROR_CATEGORIES.FILE_SYSTEM,defaultMessage:"Storage full",defaultDetails:"Device storage is full and cannot save the file",recoverable:true,suggestedAction:"Free up some storage space and try again"},[ERROR_CODES.OPERATION_CANCELLED]:{category:ERROR_CATEGORIES.USER_ACTION,defaultMessage:"Operation cancelled",defaultDetails:"The operation was cancelled by the user",recoverable:true,suggestedAction:"Try again if you want to complete the operation"},[ERROR_CODES.INVALID_FILE_TYPE]:{category:ERROR_CATEGORIES.VALIDATION,defaultMessage:"Invalid file type",defaultDetails:"The selected file type is not supported",recoverable:true,suggestedAction:"Choose a file with a supported format"},[ERROR_CODES.BRIDGE_UNAVAILABLE]:{category:ERROR_CATEGORIES.SYSTEM,defaultMessage:"Native feature unavailable",defaultDetails:"The native bridge is not available or not initialized",recoverable:false,suggestedAction:"Use web fallback if available"},[ERROR_CODES.FEATURE_UNSUPPORTED]:{category:ERROR_CATEGORIES.SYSTEM,defaultMessage:"Feature not supported",defaultDetails:"This feature is not supported on the current platform",recoverable:false,suggestedAction:"Try using an alternative method or device"},[ERROR_CODES.CAMERA_UNAVAILABLE]:{category:ERROR_CATEGORIES.SYSTEM,defaultMessage:"Camera unavailable",defaultDetails:"Camera hardware is not available or accessible",recoverable:false,suggestedAction:"Check if your device has a camera and try again"},[ERROR_CODES.CAMERA_IN_USE]:{category:ERROR_CATEGORIES.SYSTEM,defaultMessage:"Camera in use",defaultDetails:"Camera is being used by another application",recoverable:true,suggestedAction:"Close other camera apps and try again"},[ERROR_CODES.INTERNAL_ERROR]:{category:ERROR_CATEGORIES.UNKNOWN,defaultMessage:"An unexpected error occurred",defaultDetails:"An internal error occurred while processing the request",recoverable:true,suggestedAction:"Try again or contact support if the problem persists"}};/**
15
15
  * Create a standardized error object
16
16
  * @param {string} code - Error code from ERROR_CODES
17
17
  * @param {string} userMessage - Custom user-friendly message (optional)
@@ -23,17 +23,17 @@ const ERROR_DEFINITIONS=exports.ERROR_DEFINITIONS={[ERROR_CODES.PERMISSION_DENIE
23
23
  * @param {any} nativeError - Native error from Android/iOS
24
24
  * @returns {Object} Standardized error object
25
25
  */exports.createStandardError=createStandardError;const translateError=nativeError=>{if(!nativeError){return createStandardError(ERROR_CODES.INTERNAL_ERROR,null,nativeError);}const errorString=nativeError.toString().toLowerCase();// Permission errors
26
- if(errorString.includes('permission')&&errorString.includes('denied')){if(errorString.includes('camera')){return createStandardError(ERROR_CODES.PERMISSION_DENIED,'Camera permission denied',nativeError);}if(errorString.includes('storage')||errorString.includes('external_storage')){return createStandardError(ERROR_CODES.PERMISSION_DENIED,'Storage permission denied',nativeError);}return createStandardError(ERROR_CODES.PERMISSION_DENIED,null,nativeError);}// File system errors
27
- if(errorString.includes('filenotfound')||errorString.includes('file not found')){return createStandardError(ERROR_CODES.FILE_NOT_FOUND,null,nativeError);}if(errorString.includes('storage')&&errorString.includes('full')){return createStandardError(ERROR_CODES.STORAGE_FULL,null,nativeError);}// Network errors
28
- if(errorString.includes('network')||errorString.includes('connection')||errorString.includes('internet')){return createStandardError(ERROR_CODES.NETWORK_UNAVAILABLE,null,nativeError);}// User cancellation
29
- if(errorString.includes('cancel')||errorString.includes('abort')){return createStandardError(ERROR_CODES.OPERATION_CANCELLED,null,nativeError);}// Camera specific errors
30
- if(errorString.includes('camera')){if(errorString.includes('unavailable')||errorString.includes('not available')){return createStandardError(ERROR_CODES.CAMERA_UNAVAILABLE,null,nativeError);}if(errorString.includes('in use')||errorString.includes('busy')){return createStandardError(ERROR_CODES.CAMERA_IN_USE,null,nativeError);}}// Activity/Intent errors (Android)
31
- if(errorString.includes('activitynotfound')){return createStandardError(ERROR_CODES.FEATURE_UNSUPPORTED,'No app available to handle this request',nativeError);}// iOS specific error codes
32
- if(typeof nativeError==='object'&&nativeError.code&&nativeError.domain){// Camera errors (iOS)
33
- if(nativeError.domain==='AVCaptureSessionErrorDomain'){if(nativeError.code===-11814){return createStandardError(ERROR_CODES.PERMISSION_DENIED,'Camera access denied',nativeError);}}// Network errors (iOS)
34
- if(nativeError.domain==='NSURLErrorDomain'){if(nativeError.code===-1009){return createStandardError(ERROR_CODES.NETWORK_UNAVAILABLE,null,nativeError);}}// File system errors (iOS)
35
- if(nativeError.domain==='NSCocoaErrorDomain'){if(nativeError.code===260){return createStandardError(ERROR_CODES.FILE_NOT_FOUND,null,nativeError);}}}// Default to internal error
26
+ if(errorString.includes("permission")&&errorString.includes("denied")){if(errorString.includes("camera")){return createStandardError(ERROR_CODES.PERMISSION_DENIED,"Camera permission denied",nativeError);}if(errorString.includes("storage")||errorString.includes("external_storage")){return createStandardError(ERROR_CODES.PERMISSION_DENIED,"Storage permission denied",nativeError);}return createStandardError(ERROR_CODES.PERMISSION_DENIED,null,nativeError);}// File system errors
27
+ if(errorString.includes("filenotfound")||errorString.includes("file not found")){return createStandardError(ERROR_CODES.FILE_NOT_FOUND,null,nativeError);}if(errorString.includes("storage")&&errorString.includes("full")){return createStandardError(ERROR_CODES.STORAGE_FULL,null,nativeError);}// Network errors
28
+ if(errorString.includes("network")||errorString.includes("connection")||errorString.includes("internet")){return createStandardError(ERROR_CODES.NETWORK_UNAVAILABLE,null,nativeError);}// User cancellation
29
+ if(errorString.includes("cancel")||errorString.includes("abort")){return createStandardError(ERROR_CODES.OPERATION_CANCELLED,null,nativeError);}// Camera specific errors
30
+ if(errorString.includes("camera")){if(errorString.includes("unavailable")||errorString.includes("not available")){return createStandardError(ERROR_CODES.CAMERA_UNAVAILABLE,null,nativeError);}if(errorString.includes("in use")||errorString.includes("busy")){return createStandardError(ERROR_CODES.CAMERA_IN_USE,null,nativeError);}}// Activity/Intent errors (Android)
31
+ if(errorString.includes("activitynotfound")){return createStandardError(ERROR_CODES.FEATURE_UNSUPPORTED,"No app available to handle this request",nativeError);}// iOS specific error codes
32
+ if(typeof nativeError==="object"&&nativeError.code&&nativeError.domain){// Camera errors (iOS)
33
+ if(nativeError.domain==="AVCaptureSessionErrorDomain"){if(nativeError.code===-11814){return createStandardError(ERROR_CODES.PERMISSION_DENIED,"Camera access denied",nativeError);}}// Network errors (iOS)
34
+ if(nativeError.domain==="NSURLErrorDomain"){if(nativeError.code===-1009){return createStandardError(ERROR_CODES.NETWORK_UNAVAILABLE,null,nativeError);}}// File system errors (iOS)
35
+ if(nativeError.domain==="NSCocoaErrorDomain"){if(nativeError.code===260){return createStandardError(ERROR_CODES.FILE_NOT_FOUND,null,nativeError);}}}// Default to internal error
36
36
  return createStandardError(ERROR_CODES.INTERNAL_ERROR,null,nativeError);};/**
37
37
  * Check if development mode is enabled
38
38
  * @returns {boolean} True if in development mode
39
- */exports.translateError=translateError;const isDevelopment=()=>{return typeof process!=='undefined'&&process.env&&process.env.NODE_ENV==='development';};exports.isDevelopment=isDevelopment;
39
+ */exports.translateError=translateError;const isDevelopment=()=>{return typeof process!=="undefined"&&process.env&&process.env.NODE_ENV==="development";};exports.isDevelopment=isDevelopment;
@@ -2,17 +2,17 @@
2
2
  * React hook for camera functionality using standardized interface
3
3
  * Handles camera permissions, photo capture, and error states
4
4
  */const useCamera=()=>{// Use standardized base hook
5
- const base=(0,_useBaseHook.useBaseHook)('useCamera');// Camera-specific state
5
+ const base=(0,_useBaseHook.useBaseHook)("useCamera");// Camera-specific state
6
6
  const[permission,setPermission]=(0,_react.useState)(null);// Server-side rendering safety
7
7
  if(typeof window==="undefined"){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,execute:()=>{},clear:()=>{},clearError:()=>{},permission:null,// Legacy aliases
8
8
  photo:null,takePhoto:()=>{},isLoading:false,clearPhoto:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{// Register callback handlers
9
- window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,data=>{try{const result=typeof data==='string'?JSON.parse(data):data;console.log("๐Ÿ“ท Camera capture result:",result);// Handle new tri-transport format or legacy format
9
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,data=>{try{const result=typeof data==="string"?JSON.parse(data):data;console.log("๐Ÿ“ท Camera capture result:",result);// Handle new tri-transport format or legacy format
10
10
  const photoData=result.fileSrc?{// New tri-transport format
11
11
  fileSrc:result.fileSrc,fileName:result.fileName,size:result.size,mimeType:result.mimeType,transport:result.transport,source:result.source}:{// Legacy format (fallback)
12
- fileSrc:result.imageUrl,fileName:'camera_photo.jpg',size:0,mimeType:'image/jpeg',transport:'LEGACY',source:'camera'};base.setDataAndComplete(photoData);// Update progress with transport info if available
12
+ fileSrc:result.imageUrl,fileName:"camera_photo.jpg",size:0,mimeType:"image/jpeg",transport:"LEGACY",source:"camera"};base.setDataAndComplete(photoData);// Update progress with transport info if available
13
13
  if(photoData.transport){base.updateProgress({transport:photoData.transport,bytesTotal:photoData.size||null});}console.log("๐Ÿ“ท Photo captured successfully via transport:",photoData.transport);}catch(parseError){console.error("๐Ÿ“ท Error parsing camera capture data:",parseError);base.handleNativeError("Failed to process captured photo");}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,data=>{setPermission(data);console.log("๐Ÿ“ท Camera permission status:",data);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_ERROR,data=>{console.error("๐Ÿ“ท Camera error:",data);base.handleNativeError(data);});return()=>{// Cleanup: unregister all handlers
14
14
  window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_ERROR);};},[base.setDataAndComplete,base.handleNativeError,base.updateProgress]);const takePhoto=()=>{console.log("๐Ÿ“ท Camera open requested");base.executeOperation(()=>{// Update progress to show capturing state
15
- base.updateProgress({state:'capturing',phase:'requesting',message:'Opening camera...'});_NativeBridge.default.camera.open();},'camera capture');};// Standardized execute function (new interface)
15
+ base.updateProgress({state:"capturing",phase:"requesting",message:"Opening camera..."});_NativeBridge.default.camera.open();},"camera capture");};// Standardized execute function (new interface)
16
16
  const execute=takePhoto;return{// Standardized interface
17
17
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,execute,clear:base.clear,clearError:base.clearError,// Camera-specific extras
18
18
  permission,// Legacy aliases for backward compatibility
@@ -20,28 +20,28 @@ photo:base.data,takePhoto,isLoading:base.loading,clearPhoto:base.clear};};/**
20
20
  * React hook for intent handling using standardized interface
21
21
  * Manages file opening operations with external apps
22
22
  */exports.useCamera=useCamera;const useIntent=()=>{// Use standardized base hook
23
- const base=(0,_useBaseHook.useBaseHook)('useIntent');// Server-side rendering safety
23
+ const base=(0,_useBaseHook.useBaseHook)("useIntent");// Server-side rendering safety
24
24
  if(typeof window==="undefined"){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,execute:()=>{},clear:()=>{},clearError:()=>{},// Legacy aliases
25
25
  isLoading:false,processingState:null,openFile:()=>{},success:null,reset:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{// Register callback handlers
26
26
  window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS,data=>{console.log("๐Ÿ“„ Intent completed successfully:",data);base.setDataAndComplete({result:data,success:true});});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR,data=>{console.error("๐Ÿ“„ Intent error:",data);base.handleNativeError(data);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_CANCELLED,data=>{console.log("๐Ÿ“„ Intent cancelled:",data);base.setLoading(false);base.resetProgress();// Keep data as is when cancelled
27
27
  });return()=>{// Cleanup: unregister all handlers
28
28
  window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_CANCELLED);};},[base.setDataAndComplete,base.handleNativeError,base.setLoading,base.resetProgress]);const openFile=(fileUrl,mimeType=null)=>{if(!fileUrl){base.handleNativeError("File URL is required");return;}console.log("๐Ÿ“„ File open with intent requested:",{fileUrl,mimeType});base.executeOperation(()=>{// Update progress to show opening state
29
- base.updateProgress({state:'opening_file',phase:'processing',message:'Opening file with external app...'});_NativeBridge.default.file.openWithIntent(fileUrl,mimeType);},'intent file open');};// Standardized execute function (new interface)
29
+ base.updateProgress({state:"opening_file",phase:"processing",message:"Opening file with external app..."});_NativeBridge.default.file.openWithIntent(fileUrl,mimeType);},"intent file open");};// Standardized execute function (new interface)
30
30
  const execute=openFile;return{// Standardized interface
31
31
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,execute,clear:base.clear,clearError:base.clearError,// Legacy aliases for backward compatibility
32
32
  isLoading:base.loading,processingState:base.progress?.phase||null,openFile,success:base.data?.success||null,reset:base.clear};};/**
33
33
  * React hook for file picker functionality
34
34
  * Manages file selection operations using standardized interface
35
35
  */exports.useIntent=useIntent;const useFilePicker=()=>{// Use standardized base hook
36
- const base=(0,_useBaseHook.useBaseHook)('useFilePicker');// Server-side rendering safety
36
+ const base=(0,_useBaseHook.useBaseHook)("useFilePicker");// Server-side rendering safety
37
37
  if(typeof window==="undefined"){return{data:null,loading:false,progress:null,error:null,isWeb:true,isNative:false,execute:()=>{},clear:()=>{},clearError:()=>{},// Legacy aliases for backward compatibility
38
38
  selectedFile:null,pickFile:()=>{},isLoading:false,processingState:null,clearFile:()=>{}};}if(!window.WebBridge){throw new Error("WebBridge is not initialized. Call WebBridge.init() first.");}(0,_react.useEffect)(()=>{// Register callback handlers
39
- window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICKED,data=>{try{const fileData=typeof data==='string'?JSON.parse(data):data;console.log("๐Ÿ“ File picked:",fileData);base.setDataAndComplete(fileData);// Update progress with transport info if available
39
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICKED,data=>{try{const fileData=typeof data==="string"?JSON.parse(data):data;console.log("๐Ÿ“ File picked:",fileData);base.setDataAndComplete(fileData);// Update progress with transport info if available
40
40
  if(fileData.transport){base.updateProgress({transport:fileData.transport,bytesTotal:fileData.size||null});}}catch(parseError){console.error("๐Ÿ“ Error parsing file data:",parseError);base.handleNativeError("Error processing selected file");}});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,data=>{console.error("๐Ÿ“ File pick error:",data);base.handleNativeError(data);});window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,data=>{console.log("๐Ÿ“ File pick cancelled:",data);base.setLoading(false);base.resetProgress();// Keep data as is when cancelled
41
41
  });// File picker state updates
42
- window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE,data=>{try{const stateData=typeof data==='string'?JSON.parse(data):data;console.log("๐Ÿ“ File picker state:",stateData.state);// Map file picker states to standardized progress states
43
- const progressState=stateData.state==='opening'?'opening':stateData.state==='processing'?'processing':'starting';base.updateProgress({state:progressState,phase:stateData.state,message:`File picker: ${stateData.state}...`});if(stateData.state){base.setLoading(true);}}catch(parseError){console.error("๐Ÿ“ Error parsing state data:",parseError);}});return()=>{// Cleanup: unregister all handlers
44
- window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICKED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_ERROR);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE);};},[base.setDataAndComplete,base.handleNativeError,base.setLoading,base.resetProgress,base.updateProgress]);const pickFile=(mimeType=null)=>{const finalMimeType=mimeType||"*/*";console.log("๐Ÿ“ Picking file with MIME type:",finalMimeType);base.executeOperation(()=>{_NativeBridge.default.file.pick(finalMimeType);},'file pick');};// Standardized execute function (new interface)
42
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE,data=>{try{const stateData=typeof data==="string"?JSON.parse(data):data;console.log("๐Ÿ“ File picker state:",stateData.state);// Map file picker states to standardized progress states
43
+ const progressState=stateData.state==="opening"?"opening":stateData.state==="processing"?"processing":"starting";base.updateProgress({state:progressState,phase:stateData.state,message:`File picker: ${stateData.state}...`});if(stateData.state){base.setLoading(true);}}catch(parseError){console.error("๐Ÿ“ Error parsing state data:",parseError);}});return()=>{// Cleanup: unregister all handlers
44
+ window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICKED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_ERROR);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE);};},[base.setDataAndComplete,base.handleNativeError,base.setLoading,base.resetProgress,base.updateProgress]);const pickFile=(mimeType=null)=>{const finalMimeType=mimeType||"*/*";console.log("๐Ÿ“ Picking file with MIME type:",finalMimeType);base.executeOperation(()=>{_NativeBridge.default.file.pick(finalMimeType);},"file pick");};// Standardized execute function (new interface)
45
45
  const execute=pickFile;return{// Standardized interface
46
46
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,execute,clear:base.clear,clearError:base.clearError,// Legacy aliases for backward compatibility
47
47
  selectedFile:base.data,pickFile,isLoading:base.loading,processingState:base.progress?.phase||null,clearFile:base.clear};};/**
@@ -59,13 +59,13 @@ const handlePermissionStatus=data=>{window.WebBridge.unregister(_NativeInterface
59
59
  const handleHapticResponse=data=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.HAPTIC_FEEDBACK);if(data===_NativeInterfaces.RESPONSE_STATUS.SUCCESS){resolve(data);}else{reject(new Error(`Haptic feedback failed: ${data}`));}};window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.HAPTIC_FEEDBACK,handleHapticResponse);_NativeBridge.default.haptic.feedback(feedbackType);console.log("๐Ÿ“ณ Haptic feedback requested:",feedbackType);}catch(error){reject(error);}});};/**
60
60
  * React hook for haptic feedback
61
61
  * Provides a function to trigger haptic feedback
62
- */exports.requestHapticFeedback=requestHapticFeedback;const useHapticFeedback=()=>{const base=(0,_useBaseHook.useBaseHook)('useHapticFeedback');const[capabilities,setCapabilities]=(0,_react.useState)({isSupported:false,availableTypes:['light','medium','heavy'],platform:'unknown'});// Haptic types
63
- const HAPTIC_TYPES={LIGHT:'light',MEDIUM:'medium',HEAVY:'heavy',SUCCESS:'success',WARNING:'warning',ERROR:'error',SELECTION:'selection',IMPACT:'impact'};(0,_react.useEffect)(()=>{// Initialize capabilities
64
- const userAgent=typeof navigator!=='undefined'?navigator.userAgent.toLowerCase():'';let platform='unknown';let isSupported=false;let availableTypes=[];if(base.isNative){isSupported=true;if(userAgent.includes('android')){platform='android';availableTypes=[HAPTIC_TYPES.LIGHT,HAPTIC_TYPES.MEDIUM,HAPTIC_TYPES.HEAVY];}else if(userAgent.includes('iphone')||userAgent.includes('ipad')){platform='ios';availableTypes=Object.values(HAPTIC_TYPES);}}else{platform='web';isSupported=typeof navigator!=='undefined'&&'vibrate'in navigator;if(isSupported){availableTypes=[HAPTIC_TYPES.LIGHT,HAPTIC_TYPES.MEDIUM,HAPTIC_TYPES.HEAVY];}}setCapabilities({isSupported,availableTypes,platform});},[base.isNative]);// Main execute function for haptic superhook
65
- const executeHaptic=(type='light',options={})=>{if(!capabilities.isSupported){const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.FEATURE_UNSUPPORTED,"Haptic feedback not supported",null,"Device does not support haptic feedback");base.handleNativeError(error);return;}return handleHapticTrigger(type,options);};// Haptic trigger handler
66
- const handleHapticTrigger=async(type,options={})=>{try{base.setLoading(true);base.updateProgress({state:'active',phase:'triggering',message:`Triggering ${type} haptic feedback...`});let success=false;if(base.isNative){// Use existing native implementation
62
+ */exports.requestHapticFeedback=requestHapticFeedback;const useHapticFeedback=()=>{const base=(0,_useBaseHook.useBaseHook)("useHapticFeedback");const[capabilities,setCapabilities]=(0,_react.useState)({isSupported:false,availableTypes:["light","medium","heavy"],platform:"unknown"});// Haptic types
63
+ const HAPTIC_TYPES={LIGHT:"light",MEDIUM:"medium",HEAVY:"heavy",SUCCESS:"success",WARNING:"warning",ERROR:"error",SELECTION:"selection",IMPACT:"impact"};(0,_react.useEffect)(()=>{// Initialize capabilities
64
+ const userAgent=typeof navigator!=="undefined"?navigator.userAgent.toLowerCase():"";let platform="unknown";let isSupported=false;let availableTypes=[];if(base.isNative){isSupported=true;if(userAgent.includes("android")){platform="android";availableTypes=[HAPTIC_TYPES.LIGHT,HAPTIC_TYPES.MEDIUM,HAPTIC_TYPES.HEAVY];}else if(userAgent.includes("iphone")||userAgent.includes("ipad")){platform="ios";availableTypes=Object.values(HAPTIC_TYPES);}}else{platform="web";isSupported=typeof navigator!=="undefined"&&"vibrate"in navigator;if(isSupported){availableTypes=[HAPTIC_TYPES.LIGHT,HAPTIC_TYPES.MEDIUM,HAPTIC_TYPES.HEAVY];}}setCapabilities({isSupported,availableTypes,platform});},[base.isNative]);// Main execute function for haptic superhook
65
+ const executeHaptic=(type="light",options={})=>{if(!capabilities.isSupported){const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.FEATURE_UNSUPPORTED,"Haptic feedback not supported",null,"Device does not support haptic feedback");base.handleNativeError(error);return;}return handleHapticTrigger(type,options);};// Haptic trigger handler
66
+ const handleHapticTrigger=async(type,options={})=>{try{base.setLoading(true);base.updateProgress({state:"active",phase:"triggering",message:`Triggering ${type} haptic feedback...`});let success=false;if(base.isNative){// Use existing native implementation
67
67
  success=await requestHapticFeedback(type);}else{// Web fallback
68
- success=handleWebHaptic(type,options);}const hapticData={lastType:type,lastOptions:options,timestamp:new Date().toISOString(),success:success,capabilities:capabilities,lastOperation:'trigger',operationSuccess:success};base.setDataAndComplete(hapticData);return success;}catch(error){console.error("๐Ÿ“ณ Haptic feedback failed:",error);base.handleNativeError(error);return false;}};// Web haptic fallback
68
+ success=handleWebHaptic(type,options);}const hapticData={lastType:type,lastOptions:options,timestamp:new Date().toISOString(),success:success,capabilities:capabilities,lastOperation:"trigger",operationSuccess:success};base.setDataAndComplete(hapticData);return success;}catch(error){console.error("๐Ÿ“ณ Haptic feedback failed:",error);base.handleNativeError(error);return false;}};// Web haptic fallback
69
69
  const handleWebHaptic=(type,options={})=>{if(!navigator.vibrate){return false;}const vibrationPatterns={[HAPTIC_TYPES.LIGHT]:[50],[HAPTIC_TYPES.MEDIUM]:[100],[HAPTIC_TYPES.HEAVY]:[200],[HAPTIC_TYPES.SUCCESS]:[100,50,100],[HAPTIC_TYPES.WARNING]:[200,100,200],[HAPTIC_TYPES.ERROR]:[300,100,300,100,300],[HAPTIC_TYPES.SELECTION]:[25],[HAPTIC_TYPES.IMPACT]:[150]};const pattern=vibrationPatterns[type]||[100];navigator.vibrate(pattern);return true;};// Return standardized superhook interface
70
70
  return{// Standard interface (from useBaseHook)
71
71
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,clear:base.clear,clearError:base.clearError,// Main execute function
@@ -2,26 +2,26 @@
2
2
  * Standardized Camera Superhook
3
3
  * Comprehensive camera functionality with integrated permission management
4
4
  * Follows standardized interface with execute as primary function
5
- *
5
+ *
6
6
  * @param {Object} options - Configuration options
7
7
  * @returns {Object} Standardized camera superhook interface
8
- */const useCamera=(options={})=>{const base=(0,_useBaseHook.useBaseHook)('useCamera',options);const[permission,setPermission]=(0,_react.useState)({status:'not_determined',canRequest:true,lastChecked:null});// Camera operations
9
- const CAMERA_OPERATIONS={TAKE_PHOTO:'takePhoto',REQUEST_PERMISSION:'requestPermission',CHECK_PERMISSION:'checkPermission'};// Camera-specific progress phases
10
- const CAMERA_PHASES={REQUESTING_PERMISSION:'requesting_permission',CHECKING_PERMISSION:'checking_permission',OPENING_CAMERA:'opening_camera',CAPTURING:'capturing',PROCESSING:'processing',COMPLETE:'complete'};(0,_react.useEffect)(()=>{// Server-side rendering safety
8
+ */const useCamera=(options={})=>{const base=(0,_useBaseHook.useBaseHook)("useCamera",options);const[permission,setPermission]=(0,_react.useState)({status:"not_determined",canRequest:true,lastChecked:null});// Camera operations
9
+ const CAMERA_OPERATIONS={TAKE_PHOTO:"takePhoto",REQUEST_PERMISSION:"requestPermission",CHECK_PERMISSION:"checkPermission"};// Camera-specific progress phases
10
+ const CAMERA_PHASES={REQUESTING_PERMISSION:"requesting_permission",CHECKING_PERMISSION:"checking_permission",OPENING_CAMERA:"opening_camera",CAPTURING:"capturing",PROCESSING:"processing",COMPLETE:"complete"};(0,_react.useEffect)(()=>{// Server-side rendering safety
11
11
  if(typeof window==="undefined")return;if(!window.WebBridge){console.warn("WebBridge not initialized. Camera functionality unavailable.");return;}// Camera capture success handler
12
- const handleCameraCapture=data=>{try{const result=typeof data==='string'?JSON.parse(data):data;base.updateProgress({phase:CAMERA_PHASES.PROCESSING,message:'Processing captured photo...'});// Handle new tri-transport format or legacy format
12
+ const handleCameraCapture=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;base.updateProgress({phase:CAMERA_PHASES.PROCESSING,message:"Processing captured photo..."});// Handle new tri-transport format or legacy format
13
13
  const photoData=result.fileSrc?{// New tri-transport format
14
14
  fileSrc:result.fileSrc,fileName:result.fileName,size:result.size,mimeType:result.mimeType,transport:result.transport,source:result.source,filePath:result.filePath}:{// Legacy format (fallback)
15
- fileSrc:result.imageUrl,fileName:result.fileName||'camera_photo.jpg',size:result.size||0,mimeType:result.mimeType||'image/jpeg',transport:'LEGACY',source:'camera',filePath:result.filePath||null};// Create comprehensive data object with photo and permission info
15
+ fileSrc:result.imageUrl,fileName:result.fileName||"camera_photo.jpg",size:result.size||0,mimeType:result.mimeType||"image/jpeg",transport:"LEGACY",source:"camera",filePath:result.filePath||null};// Create comprehensive data object with photo and permission info
16
16
  const cameraData={...photoData,permission:permission,lastOperation:CAMERA_OPERATIONS.TAKE_PHOTO,operationSuccess:true,timestamp:new Date().toISOString()};base.setDataAndComplete(cameraData);console.log("๐Ÿ“ท Photo captured successfully via transport:",photoData.transport);}catch(parseError){console.error("๐Ÿ“ท Error parsing camera capture data:",parseError);const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.INTERNAL_ERROR,"Failed to process captured photo",parseError);base.handleNativeError(error);}};// Camera permission status handler
17
- const handlePermissionStatus=data=>{const permissionStatus=typeof data==='string'?data:data.status||data;const newPermission={status:permissionStatus.toLowerCase(),canRequest:permissionStatus!=='permanently_denied',lastChecked:new Date().toISOString()};setPermission(newPermission);console.log("๐Ÿ“ท Camera permission status:",newPermission);// Update data with permission info
18
- const permissionData={permission:newPermission,lastOperation:CAMERA_OPERATIONS.REQUEST_PERMISSION,operationSuccess:permissionStatus==='GRANTED',timestamp:new Date().toISOString()};if(permissionStatus==='GRANTED'){base.updateProgress({phase:CAMERA_PHASES.OPENING_CAMERA,message:'Camera permission granted, ready to capture...'});base.setDataAndComplete(permissionData);}else if(permissionStatus==='DENIED'){const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.PERMISSION_DENIED,"Camera permission denied",null,"Camera permission is required to take photos");base.handleNativeError(error);}};// Camera error handler
17
+ const handlePermissionStatus=data=>{const permissionStatus=typeof data==="string"?data:data.status||data;const newPermission={status:permissionStatus.toLowerCase(),canRequest:permissionStatus!=="permanently_denied",lastChecked:new Date().toISOString()};setPermission(newPermission);console.log("๐Ÿ“ท Camera permission status:",newPermission);// Update data with permission info
18
+ const permissionData={permission:newPermission,lastOperation:CAMERA_OPERATIONS.REQUEST_PERMISSION,operationSuccess:permissionStatus==="GRANTED",timestamp:new Date().toISOString()};if(permissionStatus==="GRANTED"){base.updateProgress({phase:CAMERA_PHASES.OPENING_CAMERA,message:"Camera permission granted, ready to capture..."});base.setDataAndComplete(permissionData);}else if(permissionStatus==="DENIED"){const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.PERMISSION_DENIED,"Camera permission denied",null,"Camera permission is required to take photos");base.handleNativeError(error);}};// Camera error handler
19
19
  const handleCameraError=data=>{console.error("๐Ÿ“ท Camera error:",data);base.handleNativeError(data);};// Register event listeners
20
20
  window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,handleCameraCapture);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,handlePermissionStatus);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_ERROR,handleCameraError);// Cleanup function
21
21
  return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_CAPTURE);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_CAMERA_ERROR);};},[base]);// Main execute function for camera superhook
22
- const executeCamera=(operation,...args)=>{if(base.isWeb){console.warn(`Camera ${operation} requires web fallback implementation (isWeb: true)`);return;}switch(operation){case CAMERA_OPERATIONS.TAKE_PHOTO:return handleTakePhoto(args[0]||{});case CAMERA_OPERATIONS.REQUEST_PERMISSION:return handleRequestPermission();case CAMERA_OPERATIONS.CHECK_PERMISSION:return handleCheckPermission();default:const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.INVALID_PARAMETERS,`Unknown camera operation: ${operation}`,null,`Valid operations: ${Object.values(CAMERA_OPERATIONS).join(', ')}`);base.handleNativeError(error);}};// Camera operation handlers
23
- const handleTakePhoto=(cameraOptions={})=>{base.executeOperation(()=>{base.updateProgress({phase:CAMERA_PHASES.REQUESTING_PERMISSION,message:'Requesting camera permission...'});_NativeBridge.default.camera.open(cameraOptions);console.log("๐Ÿ“ท Camera open requested with options:",cameraOptions);},'photo capture');};const handleRequestPermission=()=>{base.executeOperation(()=>{base.updateProgress({phase:CAMERA_PHASES.REQUESTING_PERMISSION,message:'Requesting camera permission...'});if(_NativeBridge.default.camera.requestPermission){_NativeBridge.default.camera.requestPermission();}else{// Fallback method
24
- _NativeBridge.default.camera.checkPermission();}console.log("๐Ÿ“ท Camera permission requested");},'permission request');};const handleCheckPermission=()=>{base.executeOperation(()=>{base.updateProgress({phase:CAMERA_PHASES.CHECKING_PERMISSION,message:'Checking camera permission...'});_NativeBridge.default.camera.checkPermission();console.log("๐Ÿ“ท Camera permission check requested");},'permission check');};// Return standardized superhook interface
22
+ const executeCamera=(operation,...args)=>{if(base.isWeb){console.warn(`Camera ${operation} requires web fallback implementation (isWeb: true)`);return;}switch(operation){case CAMERA_OPERATIONS.TAKE_PHOTO:return handleTakePhoto(args[0]||{});case CAMERA_OPERATIONS.REQUEST_PERMISSION:return handleRequestPermission();case CAMERA_OPERATIONS.CHECK_PERMISSION:return handleCheckPermission();default:const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.INVALID_PARAMETERS,`Unknown camera operation: ${operation}`,null,`Valid operations: ${Object.values(CAMERA_OPERATIONS).join(", ")}`);base.handleNativeError(error);}};// Camera operation handlers
23
+ const handleTakePhoto=(cameraOptions={})=>{base.executeOperation(()=>{base.updateProgress({phase:CAMERA_PHASES.REQUESTING_PERMISSION,message:"Requesting camera permission..."});_NativeBridge.default.camera.open(cameraOptions);console.log("๐Ÿ“ท Camera open requested with options:",cameraOptions);},"photo capture");};const handleRequestPermission=()=>{base.executeOperation(()=>{base.updateProgress({phase:CAMERA_PHASES.REQUESTING_PERMISSION,message:"Requesting camera permission..."});if(_NativeBridge.default.camera.requestPermission){_NativeBridge.default.camera.requestPermission();}else{// Fallback method
24
+ _NativeBridge.default.camera.checkPermission();}console.log("๐Ÿ“ท Camera permission requested");},"permission request");};const handleCheckPermission=()=>{base.executeOperation(()=>{base.updateProgress({phase:CAMERA_PHASES.CHECKING_PERMISSION,message:"Checking camera permission..."});_NativeBridge.default.camera.checkPermission();console.log("๐Ÿ“ท Camera permission check requested");},"permission check");};// Return standardized superhook interface
25
25
  return{// Standard interface (from useBaseHook)
26
26
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,clear:base.clear,clearError:base.clearError,// Main execute function
27
27
  execute:executeCamera,// Semantic aliases for execute
@@ -30,20 +30,20 @@ permission:permission,// Legacy compatibility
30
30
  photo:base.data,clearPhoto:base.clear};};/**
31
31
  * Standardized File Picker Hook
32
32
  * Provides consistent interface with error standardization and environment detection
33
- *
33
+ *
34
34
  * @param {Object} options - Configuration options
35
35
  * @returns {Object} Standardized file picker interface
36
- */exports.useCamera=useCamera;const useFilePicker=(options={})=>{const base=(0,_useBaseHook.useBaseHook)('useFilePicker',options);// File picker specific progress phases
37
- const FILE_PICKER_PHASES={OPENING:'opening',PROCESSING:'processing',ROUTING:'routing',COMPLETE:'complete'};(0,_react.useEffect)(()=>{// Server-side rendering safety
36
+ */exports.useCamera=useCamera;const useFilePicker=(options={})=>{const base=(0,_useBaseHook.useBaseHook)("useFilePicker",options);// File picker specific progress phases
37
+ const FILE_PICKER_PHASES={OPENING:"opening",PROCESSING:"processing",ROUTING:"routing",COMPLETE:"complete"};(0,_react.useEffect)(()=>{// Server-side rendering safety
38
38
  if(typeof window==="undefined")return;if(!window.WebBridge){console.warn("WebBridge not initialized. File picker functionality unavailable.");return;}// File picked success handler
39
- const handleFilePicked=data=>{try{const result=typeof data==='string'?JSON.parse(data):data;console.log("๐Ÿ“ File picked result:",result);base.updateProgress({phase:FILE_PICKER_PHASES.PROCESSING,message:'Processing selected file...'});// Handle tri-transport format
39
+ const handleFilePicked=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;console.log("๐Ÿ“ File picked result:",result);base.updateProgress({phase:FILE_PICKER_PHASES.PROCESSING,message:"Processing selected file..."});// Handle tri-transport format
40
40
  const fileData={fileSrc:result.fileSrc,fileName:result.fileName,size:result.size,mimeType:result.mimeType,transport:result.transport,filePath:result.filePath};base.setDataAndComplete(fileData);console.log("๐Ÿ“ File processed successfully via transport:",fileData.transport);}catch(parseError){console.error("๐Ÿ“ Error parsing file picker data:",parseError);const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.INTERNAL_ERROR,"Failed to process selected file",parseError);base.handleNativeError(error);}};// File pick error handler
41
41
  const handleFilePickError=data=>{console.error("๐Ÿ“ File pick error:",data);base.handleNativeError(data);};// File pick cancelled handler
42
42
  const handleFilePickCancelled=data=>{console.log("๐Ÿ“ File pick cancelled:",data);const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.OPERATION_CANCELLED,"File selection was cancelled",data);base.handleNativeError(error);};// File picker state updates
43
- const handleFilePickStateUpdate=data=>{try{const stateData=typeof data==='string'?JSON.parse(data):data;console.log("๐Ÿ“ File picker state:",stateData.state);let phase=FILE_PICKER_PHASES.PROCESSING;let message='Processing...';switch(stateData.state){case'opening':phase=FILE_PICKER_PHASES.OPENING;message='Opening file picker...';break;case'processing':phase=FILE_PICKER_PHASES.PROCESSING;message='Processing selected file...';break;case'routing':phase=FILE_PICKER_PHASES.ROUTING;message='Determining transport method...';break;default:message=stateData.state;}base.updateProgress({phase,message});}catch(parseError){console.error("๐Ÿ“ Error parsing file picker state:",parseError);}};// Register event listeners
43
+ const handleFilePickStateUpdate=data=>{try{const stateData=typeof data==="string"?JSON.parse(data):data;console.log("๐Ÿ“ File picker state:",stateData.state);let phase=FILE_PICKER_PHASES.PROCESSING;let message="Processing...";switch(stateData.state){case"opening":phase=FILE_PICKER_PHASES.OPENING;message="Opening file picker...";break;case"processing":phase=FILE_PICKER_PHASES.PROCESSING;message="Processing selected file...";break;case"routing":phase=FILE_PICKER_PHASES.ROUTING;message="Determining transport method...";break;default:message=stateData.state;}base.updateProgress({phase,message});}catch(parseError){console.error("๐Ÿ“ Error parsing file picker state:",parseError);}};// Register event listeners
44
44
  window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICKED,handleFilePicked);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,handleFilePickError);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,handleFilePickCancelled);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE,handleFilePickStateUpdate);// Cleanup function
45
45
  return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICKED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_ERROR);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED);window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE);};},[base]);// Pick file function
46
- const pickFile=(mimeType=null)=>{base.executeOperation(()=>{const finalMimeType=mimeType||"*/*";base.updateProgress({phase:FILE_PICKER_PHASES.OPENING,message:`Opening file picker${mimeType?` for ${mimeType}`:''}...`});_NativeBridge.default.file.pick(finalMimeType);console.log("๐Ÿ“ File picker opened with MIME type:",finalMimeType);},'file selection');};// Return standardized interface
46
+ const pickFile=(mimeType=null)=>{base.executeOperation(()=>{const finalMimeType=mimeType||"*/*";base.updateProgress({phase:FILE_PICKER_PHASES.OPENING,message:`Opening file picker${mimeType?` for ${mimeType}`:""}...`});_NativeBridge.default.file.pick(finalMimeType);console.log("๐Ÿ“ File picker opened with MIME type:",finalMimeType);},"file selection");};// Return standardized interface
47
47
  return{// Standard interface
48
48
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,// Actions
49
49
  execute:pickFile,// Standard execute function
@@ -56,21 +56,21 @@ clearFile:base.clear// Legacy alias
56
56
  };};/**
57
57
  * Standardized Intent Hook
58
58
  * Provides consistent interface with error standardization and environment detection
59
- *
59
+ *
60
60
  * @param {Object} options - Configuration options
61
61
  * @returns {Object} Standardized intent interface
62
- */exports.useFilePicker=useFilePicker;const useIntent=(options={})=>{const base=(0,_useBaseHook.useBaseHook)('useIntent',options);// Intent specific progress phases
63
- const INTENT_PHASES={PREPARING:'preparing',DOWNLOADING:'downloading',OPENING:'opening',COMPLETE:'complete'};(0,_react.useEffect)(()=>{// Server-side rendering safety
62
+ */exports.useFilePicker=useFilePicker;const useIntent=(options={})=>{const base=(0,_useBaseHook.useBaseHook)("useIntent",options);// Intent specific progress phases
63
+ const INTENT_PHASES={PREPARING:"preparing",DOWNLOADING:"downloading",OPENING:"opening",COMPLETE:"complete"};(0,_react.useEffect)(()=>{// Server-side rendering safety
64
64
  if(typeof window==="undefined")return;if(!window.WebBridge){console.warn("WebBridge not initialized. Intent functionality unavailable.");return;}// Intent success handler
65
- const handleIntentSuccess=data=>{try{const result=typeof data==='string'?JSON.parse(data):data;console.log("๐Ÿ”— Intent success result:",result);const intentData={url:result.url,mimeType:result.mimeType,success:result.success||true,message:result.message||'File opened successfully'};base.setDataAndComplete(intentData);console.log("๐Ÿ”— Intent completed successfully");}catch(parseError){console.error("๐Ÿ”— Error parsing intent result:",parseError);const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.INTERNAL_ERROR,"Failed to process intent result",parseError);base.handleNativeError(error);}};// Intent error handler
65
+ const handleIntentSuccess=data=>{try{const result=typeof data==="string"?JSON.parse(data):data;console.log("๐Ÿ”— Intent success result:",result);const intentData={url:result.url,mimeType:result.mimeType,success:result.success||true,message:result.message||"File opened successfully"};base.setDataAndComplete(intentData);console.log("๐Ÿ”— Intent completed successfully");}catch(parseError){console.error("๐Ÿ”— Error parsing intent result:",parseError);const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.INTERNAL_ERROR,"Failed to process intent result",parseError);base.handleNativeError(error);}};// Intent error handler
66
66
  const handleIntentError=data=>{console.error("๐Ÿ”— Intent error:",data);base.handleNativeError(data);};// Intent state updates (if available)
67
- const handleIntentStateUpdate=data=>{try{const stateData=typeof data==='string'?JSON.parse(data):data;console.log("๐Ÿ”— Intent state:",stateData.state);let phase=INTENT_PHASES.PREPARING;let message='Processing...';switch(stateData.state){case'preparing':phase=INTENT_PHASES.PREPARING;message='Preparing to open file...';break;case'downloading':phase=INTENT_PHASES.DOWNLOADING;message='Downloading file...';break;case'opening':phase=INTENT_PHASES.OPENING;message='Opening with external app...';break;default:message=stateData.state;}base.updateProgress({phase,message,percentage:stateData.percentage||null});}catch(parseError){console.error("๐Ÿ”— Error parsing intent state:",parseError);}};// Register event listeners (some callbacks might not exist in legacy intent)
68
- window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS||'ON_INTENT_SUCCESS',handleIntentSuccess);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR||'ON_INTENT_ERROR',handleIntentError);// Optional state updates (might not be implemented in current intent system)
67
+ const handleIntentStateUpdate=data=>{try{const stateData=typeof data==="string"?JSON.parse(data):data;console.log("๐Ÿ”— Intent state:",stateData.state);let phase=INTENT_PHASES.PREPARING;let message="Processing...";switch(stateData.state){case"preparing":phase=INTENT_PHASES.PREPARING;message="Preparing to open file...";break;case"downloading":phase=INTENT_PHASES.DOWNLOADING;message="Downloading file...";break;case"opening":phase=INTENT_PHASES.OPENING;message="Opening with external app...";break;default:message=stateData.state;}base.updateProgress({phase,message,percentage:stateData.percentage||null});}catch(parseError){console.error("๐Ÿ”— Error parsing intent state:",parseError);}};// Register event listeners (some callbacks might not exist in legacy intent)
68
+ window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS||"ON_INTENT_SUCCESS",handleIntentSuccess);window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR||"ON_INTENT_ERROR",handleIntentError);// Optional state updates (might not be implemented in current intent system)
69
69
  if(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_STATE_UPDATE){window.WebBridge.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_STATE_UPDATE,handleIntentStateUpdate);}// Cleanup function
70
- return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS||'ON_INTENT_SUCCESS');window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR||'ON_INTENT_ERROR');if(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_STATE_UPDATE){window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_STATE_UPDATE);}};},[base]);// Open file function
70
+ return()=>{window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_SUCCESS||"ON_INTENT_SUCCESS");window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_ERROR||"ON_INTENT_ERROR");if(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_STATE_UPDATE){window.WebBridge.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_INTENT_STATE_UPDATE);}};},[base]);// Open file function
71
71
  const openFile=(url,mimeType=null)=>{if(!url){const error=(0,_errors.createStandardError)(_errors.ERROR_CODES.INVALID_PARAMETERS,"URL is required for opening files",null,"Please provide a valid URL to open");base.handleNativeError(error);return;}base.executeOperation(()=>{const finalMimeType=mimeType||"*/*";base.updateProgress({phase:INTENT_PHASES.PREPARING,message:`Preparing to open ${url}...`});// Check if intent system has specific methods
72
72
  if(_NativeBridge.default.intent&&_NativeBridge.default.intent.openFile){_NativeBridge.default.intent.openFile(url,finalMimeType);}else{// Fallback to generic method if available
73
- _NativeBridge.default.openFile(url,finalMimeType);}console.log("๐Ÿ”— Intent to open file:",url,"with MIME type:",finalMimeType);},'file opening');};// Return standardized interface
73
+ _NativeBridge.default.openFile(url,finalMimeType);}console.log("๐Ÿ”— Intent to open file:",url,"with MIME type:",finalMimeType);},"file opening");};// Return standardized interface
74
74
  return{// Standard interface
75
75
  data:base.data,loading:base.loading,progress:base.progress,error:base.error,isWeb:base.isWeb,isNative:base.isNative,// Actions
76
76
  execute:openFile,// Standard execute function
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useEnvironmentInfo=exports.useBaseHook=exports.PROGRESS_STATES=void 0;var _react=require("react");var _NativeBridge=_interopRequireDefault(require("./utils/NativeBridge.js"));var _errors=require("./errors");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}/**
2
2
  * Base hook utility that provides standardized interface for all Catalyst hooks
3
3
  * Handles environment detection, error standardization, and common state management
4
- *
4
+ *
5
5
  * @param {string} hookName - Name of the hook for debugging purposes
6
6
  * @param {Object} options - Configuration options
7
7
  * @returns {Object} Base hook interface with common functionality
@@ -9,7 +9,7 @@
9
9
  const isNative=(0,_react.useCallback)(()=>{// Server-side rendering safety
10
10
  if(typeof window==="undefined")return false;// Check for WebBridge and verify it's functional
11
11
  return!!(window.WebBridge&&_NativeBridge.default.isAvailable());},[]);const isWeb=(0,_react.useCallback)(()=>{return!isNative();},[isNative]);// Common state management
12
- const[data,setData]=(0,_react.useState)(null);const[loading,setLoading]=(0,_react.useState)(false);const[error,setError]=(0,_react.useState)(null);const[progress,setProgress]=(0,_react.useState)({state:'idle',// 'idle' | 'opening' | 'processing' | 'routing' | 'complete' | 'error'
12
+ const[data,setData]=(0,_react.useState)(null);const[loading,setLoading]=(0,_react.useState)(false);const[error,setError]=(0,_react.useState)(null);const[progress,setProgress]=(0,_react.useState)({state:"idle",// 'idle' | 'opening' | 'processing' | 'routing' | 'complete' | 'error'
13
13
  percentage:null,// 0-100 or null
14
14
  message:null,// Human readable message or null
15
15
  phase:null,// Operation specific phase or null
@@ -17,11 +17,11 @@ transport:null,// Transport method being used
17
17
  bytesLoaded:null,// Bytes processed
18
18
  bytesTotal:null// Total bytes
19
19
  });// Progress management utilities
20
- 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
20
+ 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
21
21
  const handleNativeError=(0,_react.useCallback)(nativeError=>{const standardError=(0,_errors.translateError)(nativeError);setError(standardError);setLoading(false);errorProgress();// Development logging
22
- 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
22
+ 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
23
23
  const setDataAndComplete=(0,_react.useCallback)(newData=>{setData(newData);setLoading(false);completeProgress();setError(null);if((0,_errors.isDevelopment)()){console.log(`โœ… ${hookName} Success:`,newData);}},[hookName,completeProgress]);const clear=(0,_react.useCallback)(()=>{setData(null);setError(null);resetProgress();if((0,_errors.isDevelopment)()){console.log(`๐Ÿ—‘๏ธ ${hookName} Cleared`);}},[hookName,resetProgress]);// Operation wrapper that handles common patterns
24
- const executeOperation=(0,_react.useCallback)((operationCallback,operationName='operation')=>{try{if(isWeb()){console.warn(`${hookName} requires web fallback implementation (isWeb: true)`);return;}if(!_NativeBridge.default.isAvailable()){throw new Error("Native bridge not available");}setLoading(true);setError(null);startProgress('starting',`Starting ${operationName}...`);if((0,_errors.isDevelopment)()){console.log(`๐Ÿš€ ${hookName} ${operationName} started`);}// Execute the actual operation
24
+ const executeOperation=(0,_react.useCallback)((operationCallback,operationName="operation")=>{try{if(isWeb()){console.warn(`${hookName} requires web fallback implementation (isWeb: true)`);return;}if(!_NativeBridge.default.isAvailable()){throw new Error("Native bridge not available");}setLoading(true);setError(null);startProgress("starting",`Starting ${operationName}...`);if((0,_errors.isDevelopment)()){console.log(`๐Ÿš€ ${hookName} ${operationName} started`);}// Execute the actual operation
25
25
  operationCallback();}catch(err){handleNativeError(err);console.error(`โŒ ${hookName} ${operationName} failed:`,err);}},[hookName,isWeb,startProgress,handleNativeError]);// Environment flags (computed values, not functions)
26
26
  const environmentFlags={isWeb:isWeb(),isNative:isNative()};// Return standardized interface
27
27
  return{// Data state
@@ -32,13 +32,13 @@ error,// Environment detection
32
32
  clear,clearError,// Internal utilities for specific hooks
33
33
  setData,setLoading,setError,setProgress,updateProgress,resetProgress,startProgress,completeProgress,errorProgress,setDataAndComplete,handleNativeError,executeOperation};};/**
34
34
  * Hook for development debugging and testing
35
- * @param {string} hookName
35
+ * @param {string} hookName
36
36
  * @returns {Object} Environment information and utilities
37
- */exports.useBaseHook=useBaseHook;const useEnvironmentInfo=hookName=>{const isNativeAvailable=()=>{if(typeof window==="undefined")return false;return!!(window.WebBridge&&_NativeBridge.default.isAvailable());};const getEnvironmentDetails=()=>{const details={hasWindow:typeof window!=="undefined",hasWebBridge:typeof window!=="undefined"&&!!window.WebBridge,nativeBridgeAvailable:false,userAgent:typeof navigator!=="undefined"?navigator.userAgent:'SSR',timestamp:new Date().toISOString()};if(details.hasWebBridge){details.nativeBridgeAvailable=_NativeBridge.default.isAvailable();}return details;};if((0,_errors.isDevelopment)()){console.log(`๐Ÿ” ${hookName} Environment:`,getEnvironmentDetails());}return{isNative:isNativeAvailable(),isWeb:!isNativeAvailable(),environmentDetails:getEnvironmentDetails()};};/**
37
+ */exports.useBaseHook=useBaseHook;const useEnvironmentInfo=hookName=>{const isNativeAvailable=()=>{if(typeof window==="undefined")return false;return!!(window.WebBridge&&_NativeBridge.default.isAvailable());};const getEnvironmentDetails=()=>{const details={hasWindow:typeof window!=="undefined",hasWebBridge:typeof window!=="undefined"&&!!window.WebBridge,nativeBridgeAvailable:false,userAgent:typeof navigator!=="undefined"?navigator.userAgent:"SSR",timestamp:new Date().toISOString()};if(details.hasWebBridge){details.nativeBridgeAvailable=_NativeBridge.default.isAvailable();}return details;};if((0,_errors.isDevelopment)()){console.log(`๐Ÿ” ${hookName} Environment:`,getEnvironmentDetails());}return{isNative:isNativeAvailable(),isWeb:!isNativeAvailable(),environmentDetails:getEnvironmentDetails()};};/**
38
38
  * Progress state constants for consistency across hooks
39
39
  */exports.useEnvironmentInfo=useEnvironmentInfo;const PROGRESS_STATES=exports.PROGRESS_STATES={// Common states
40
- IDLE:'idle',STARTING:'starting',COMPLETE:'complete',ERROR:'error',// File Picker states
41
- OPENING:'opening',PROCESSING:'processing',ROUTING:'routing',// Camera states
42
- REQUESTING:'requesting',CAPTURING:'capturing',// Intent states
43
- DOWNLOADING:'downloading',OPENING_FILE:'opening_file',// Server states
44
- CONNECTING:'connecting',UPLOADING:'uploading',SERVING:'serving'};
40
+ IDLE:"idle",STARTING:"starting",COMPLETE:"complete",ERROR:"error",// File Picker states
41
+ OPENING:"opening",PROCESSING:"processing",ROUTING:"routing",// Camera states
42
+ REQUESTING:"requesting",CAPTURING:"capturing",// Intent states
43
+ DOWNLOADING:"downloading",OPENING_FILE:"opening_file",// Server states
44
+ CONNECTING:"connecting",UPLOADING:"uploading",SERVING:"serving"};
@@ -2,30 +2,30 @@
2
2
  * NativeBridge Utility
3
3
  * Handles communication from Web to Native platforms
4
4
  * Supports both Android (window.NativeBridge) and iOS (webkit.messageHandlers)
5
- */class NativeBridgeUtil{constructor(){this.isAndroid=this._detectAndroid();this.isIOS=this._detectIOS();this.isNativeEnvironment=this.isAndroid||this.isIOS;if(typeof window!=='undefined'){this._logEnvironmentInfo();}}/**
5
+ */class NativeBridgeUtil{constructor(){this.isAndroid=this._detectAndroid();this.isIOS=this._detectIOS();this.isNativeEnvironment=this.isAndroid||this.isIOS;if(typeof window!=="undefined"){this._logEnvironmentInfo();}}/**
6
6
  * Detect if running on Android WebView
7
- */_detectAndroid(){if(typeof window==='undefined')return false;return!!(window.NativeBridge&&typeof window.NativeBridge==='object');}/**
7
+ */_detectAndroid(){if(typeof window==="undefined")return false;return!!(window.NativeBridge&&typeof window.NativeBridge==="object");}/**
8
8
  * Detect if running on iOS WebView
9
- */_detectIOS(){if(typeof window==='undefined')return false;return!!window.webkit?.messageHandlers?.NativeBridge;}/**
9
+ */_detectIOS(){if(typeof window==="undefined")return false;return!!window.webkit?.messageHandlers?.NativeBridge;}/**
10
10
  * Log environment detection info for debugging
11
- */_logEnvironmentInfo(){console.log('๐ŸŒ‰ NativeBridge Environment Detection:',{isAndroid:this.isAndroid,isIOS:this.isIOS,isNativeEnvironment:this.isNativeEnvironment,hasAndroidBridge:!!window.NativeBridge,hasIOSBridge:!!window.webkit?.messageHandlers?.NativeBridge});}/**
11
+ */_logEnvironmentInfo(){console.log("๐ŸŒ‰ NativeBridge Environment Detection:",{isAndroid:this.isAndroid,isIOS:this.isIOS,isNativeEnvironment:this.isNativeEnvironment,hasAndroidBridge:!!window.NativeBridge,hasIOSBridge:!!window.webkit?.messageHandlers?.NativeBridge});}/**
12
12
  * Validate command before execution
13
- */_validateCommand(command){if(!command){throw new Error('Command is required');}if(!(0,_NativeInterfaces.isValidCommand)(command)){throw new Error(`Invalid command: ${command}. Available commands: ${Object.values(_NativeInterfaces.NATIVE_COMMANDS).join(', ')}`);}if(!this.isNativeEnvironment){throw new Error('Native bridge not available. Ensure you are running in a native WebView environment.');}}/**
13
+ */_validateCommand(command){if(!command){throw new Error("Command is required");}if(!(0,_NativeInterfaces.isValidCommand)(command)){throw new Error(`Invalid command: ${command}. Available commands: ${Object.values(_NativeInterfaces.NATIVE_COMMANDS).join(", ")}`);}if(!this.isNativeEnvironment){throw new Error("Native bridge not available. Ensure you are running in a native WebView environment.");}}/**
14
14
  * Execute command on Android platform
15
- */_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
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
16
  window.NativeBridge[command](data);return true;}else{throw new Error(`Android bridge method '${command}' not found`);}}catch(error){console.error(`Error executing Android command '${command}':`,error);throw error;}}/**
17
17
  * Execute command on iOS platform
18
18
  */_executeIOSCommand(command,data){try{// For iOS, we always send a message object
19
19
  const message={command:command,data:data// This can be null/undefined, iOS bridge should handle it
20
- };console.log(`๐ŸŒ‰ Calling iOS method '${command}'`,data?{data}:'(no data)');window.webkit.messageHandlers.NativeBridge.postMessage(message);return true;}catch(error){console.error(`Error executing iOS command '${command}':`,error);throw error;}}/**
20
+ };console.log(`๐ŸŒ‰ Calling iOS method '${command}'`,data?{data}:"(no data)");window.webkit.messageHandlers.NativeBridge.postMessage(message);return true;}catch(error){console.error(`Error executing iOS command '${command}':`,error);throw error;}}/**
21
21
  * Main method to call native commands
22
22
  * @param {string} command - The command to execute (from NATIVE_COMMANDS)
23
23
  * @param {any} data - Optional data to pass to the native side
24
24
  * @returns {boolean} - Success status
25
25
  */call(command,data=null){try{// Validate environment and command
26
- this._validateCommand(command);console.log(`๐ŸŒ‰ Executing native command: ${command}`,data?{data}:'');// Execute on appropriate platform
26
+ this._validateCommand(command);console.log(`๐ŸŒ‰ Executing native command: ${command}`,data?{data}:"");// Execute on appropriate platform
27
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
- if(process.env.NODE_ENV==='development'){console.warn(`๐ŸŒ‰ Development mode: Native command '${command}' failed. This is expected when running in browser.`);}throw error;}}/**
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={/**
31
31
  * Open camera for photo capture
@@ -36,7 +36,7 @@ if(process.env.NODE_ENV==='development'){console.warn(`๐ŸŒ‰ Development mode: Na
36
36
  */file={/**
37
37
  * Open file picker
38
38
  * @param {string} mimeType - MIME type filter (e.g., 'image/*', 'application/pdf')
39
- */pick:(mimeType='*/*')=>this.call(_NativeInterfaces.NATIVE_COMMANDS.PICK_FILE,mimeType),/**
39
+ */pick:(mimeType="*/*")=>this.call(_NativeInterfaces.NATIVE_COMMANDS.PICK_FILE,mimeType),/**
40
40
  * Open file with external app
41
41
  * @param {string} fileUrl - URL of the file to open
42
42
  * @param {string} mimeType - Optional MIME type
@@ -45,11 +45,11 @@ if(process.env.NODE_ENV==='development'){console.warn(`๐ŸŒ‰ Development mode: Na
45
45
  */haptic={/**
46
46
  * Request haptic feedback
47
47
  * @param {string} feedbackType - Type of haptic feedback
48
- */feedback:(feedbackType='light')=>this.call(_NativeInterfaces.NATIVE_COMMANDS.REQUEST_HAPTIC_FEEDBACK,feedbackType)};/**
48
+ */feedback:(feedbackType="light")=>this.call(_NativeInterfaces.NATIVE_COMMANDS.REQUEST_HAPTIC_FEEDBACK,feedbackType)};/**
49
49
  * Get environment info
50
- */getEnvironmentInfo(){return{isAndroid:this.isAndroid,isIOS:this.isIOS,isNativeEnvironment:this.isNativeEnvironment,platform:this.isAndroid?'android':this.isIOS?'ios':'web'};}/**
50
+ */getEnvironmentInfo(){return{isAndroid:this.isAndroid,isIOS:this.isIOS,isNativeEnvironment:this.isNativeEnvironment,platform:this.isAndroid?"android":this.isIOS?"ios":"web"};}/**
51
51
  * Check if native bridge is available
52
52
  */isAvailable(){return this.isNativeEnvironment;}/**
53
53
  * Debug method to test native bridge connectivity
54
- */testConnection(){console.group('๐ŸŒ‰ NativeBridge Connection Test');console.log('Environment:',this.getEnvironmentInfo());console.log('Available commands:',Object.values(_NativeInterfaces.NATIVE_COMMANDS));if(this.isAndroid&&window.NativeBridge){console.log('Android bridge methods:',Object.getOwnPropertyNames(window.NativeBridge));}if(this.isIOS&&window.webkit?.messageHandlers?.NativeBridge){console.log('iOS webkit bridge available');}console.groupEnd();}}// Export singleton instance
54
+ */testConnection(){console.group("๐ŸŒ‰ NativeBridge Connection Test");console.log("Environment:",this.getEnvironmentInfo());console.log("Available commands:",Object.values(_NativeInterfaces.NATIVE_COMMANDS));if(this.isAndroid&&window.NativeBridge){console.log("Android bridge methods:",Object.getOwnPropertyNames(window.NativeBridge));}if(this.isIOS&&window.webkit?.messageHandlers?.NativeBridge){console.log("iOS webkit bridge available");}console.groupEnd();}}// Export singleton instance
55
55
  exports.NativeBridgeUtil=NativeBridgeUtil;const nativeBridge=new NativeBridgeUtil();var _default=exports.default=nativeBridge;// Also export the class for advanced usage
@@ -1,54 +1,76 @@
1
1
  "use strict";var _renameAndroidProject=require("./renameAndroidProject.js");var _setupServer=require("./setupServer.js");var _child_process=require("child_process");var _fs=_interopRequireDefault(require("fs"));var _path=_interopRequireDefault(require("path"));var _utils=require("./utils.js");var _TerminalProgress=_interopRequireDefault(require("./TerminalProgress.js"));// Import the AAB builder
2
- function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const configPath=`${process.env.PWD}/config/config.json`;const pwd=`${process.cwd()}/node_modules/catalyst-core/dist/native`;const ANDROID_PACKAGE="io.yourname.androidproject";// Default values for AAB building
2
+ function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const configPath=`${process.env.PWD}/config/config.json`;const publicPath=`${process.env.PWD}/public`;const pwd=`${process.cwd()}/node_modules/catalyst-core/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
- const DEFAULT_OVERWRITE_EXISTING=true;const steps={config:'Initialize Configuration',tools:'Validate Android Tools',emulator:'Check and Start Emulator',copyAssets:'Copy Build Assets',build:'Build and Install Application',aab:'Build Signed AAB'};const progressConfig={titlePaddingTop:2,titlePaddingBottom:1,stepPaddingLeft:4,stepSpacing:1,errorPaddingLeft:6,bottomMargin:2};const progress=new _TerminalProgress.default(steps,"Catalyst Android Build",progressConfig);async function initializeConfig(){const configFile=_fs.default.readFileSync(configPath,'utf8');const config=JSON.parse(configFile);const{WEBVIEW_CONFIG}=config;if(!WEBVIEW_CONFIG||Object.keys(WEBVIEW_CONFIG).length===0){throw new Error('WebView Config missing in '+configPath);}if(!WEBVIEW_CONFIG.android){throw new Error('Android config missing in WebView Config');}// Log build type information
5
- const buildType=WEBVIEW_CONFIG.android.buildType||'debug';progress.log(`Build Type: ${buildType}`,'info');if(buildType==='release'){progress.log('Release build detected - AAB will be generated','info');}return{WEBVIEW_CONFIG};}function validateAndroidTools(androidConfig){const ANDROID_SDK=androidConfig.sdkPath;const ADB_PATH=`${ANDROID_SDK}/platform-tools/adb`;const EMULATOR_PATH=`${ANDROID_SDK}/emulator/emulator`;progress.log('Validating Android tools...','info');if(!ANDROID_SDK){throw new Error('Android SDK path is not configured');}if(!_fs.default.existsSync(ANDROID_SDK)){throw new Error(`Android SDK path does not exist: ${ANDROID_SDK}`);}if(!_fs.default.existsSync(ADB_PATH)){throw new Error(`ADB not found at: ${ADB_PATH}`);}try{(0,_utils.runCommand)(`${ADB_PATH} version`);progress.log('ADB validation successful','success');}catch(error){throw new Error(`ADB is not working properly: ${error.message}`);}// Skip emulator validation for release builds
6
- const buildType=androidConfig.buildType||'debug';if(buildType!=='release'){if(!_fs.default.existsSync(EMULATOR_PATH)){throw new Error(`Emulator not found at: ${EMULATOR_PATH}`);}try{(0,_utils.runCommand)(`${EMULATOR_PATH} -version`);progress.log('Emulator validation successful','success');}catch(error){throw new Error(`Emulator is not working properly: ${error.message}`);}try{const avdOutput=(0,_utils.runCommand)(`${EMULATOR_PATH} -list-avds`);if(!avdOutput.includes(androidConfig.emulatorName)){throw new Error(`Specified emulator "${androidConfig.emulatorName}" not found in available AVDs`);}progress.log(`Emulator "${androidConfig.emulatorName}" exists`,'success');}catch(error){throw new Error(`Error checking emulator AVD: ${error.message}`);}}else{progress.log('Skipping emulator validation for release build','info');}progress.log('Android tools validation completed successfully!','success');return{ANDROID_SDK,ADB_PATH,EMULATOR_PATH};}async function detectPhysicalDevice(ADB_PATH){try{progress.log('Detecting physical devices...','info');const devices=(0,_utils.runCommand)(`${ADB_PATH} devices -l`);const lines=devices.split('\n').filter(line=>line.trim()&&!line.includes('List of devices'));for(const line of lines){const parts=line.trim().split(/\s+/);if(parts.length>=2&&parts[1]==='device'){const deviceId=parts[0];// Check if it's a physical device (not emulator)
7
- if(!deviceId.startsWith('emulator-')){try{// Double-check it's not an emulator using device properties
4
+ const DEFAULT_OVERWRITE_EXISTING=true;const steps={config:"Initialize Configuration",tools:"Validate Android Tools",emulator:"Check and Start Emulator",copyAssets:"Copy Build Assets",build:"Build and Install Application",aab:"Build Signed AAB"};const progressConfig={titlePaddingTop:2,titlePaddingBottom:1,stepPaddingLeft:4,stepSpacing:1,errorPaddingLeft:6,bottomMargin:2};const progress=new _TerminalProgress.default(steps,"Catalyst Android Build",progressConfig);async function initializeConfig(){const configFile=_fs.default.readFileSync(configPath,"utf8");const config=JSON.parse(configFile);const{WEBVIEW_CONFIG}=config;if(!WEBVIEW_CONFIG||Object.keys(WEBVIEW_CONFIG).length===0){throw new Error("WebView Config missing in "+configPath);}if(!WEBVIEW_CONFIG.android){throw new Error("Android config missing in WebView Config");}// Log build type information
5
+ const buildType=WEBVIEW_CONFIG.android.buildType||"debug";progress.log(`Build Type: ${buildType}`,"info");if(buildType==="release"){progress.log("Release build detected - AAB will be generated","info");}return{WEBVIEW_CONFIG};}function validateAndroidTools(androidConfig){const ANDROID_SDK=androidConfig.sdkPath;const ADB_PATH=`${ANDROID_SDK}/platform-tools/adb`;const EMULATOR_PATH=`${ANDROID_SDK}/emulator/emulator`;progress.log("Validating Android tools...","info");if(!ANDROID_SDK){throw new Error("Android SDK path is not configured");}if(!_fs.default.existsSync(ANDROID_SDK)){throw new Error(`Android SDK path does not exist: ${ANDROID_SDK}`);}if(!_fs.default.existsSync(ADB_PATH)){throw new Error(`ADB not found at: ${ADB_PATH}`);}try{;(0,_utils.runCommand)(`${ADB_PATH} version`);progress.log("ADB validation successful","success");}catch(error){throw new Error(`ADB is not working properly: ${error.message}`);}// Skip emulator validation for release builds
6
+ const buildType=androidConfig.buildType||"debug";if(buildType!=="release"){if(!_fs.default.existsSync(EMULATOR_PATH)){throw new Error(`Emulator not found at: ${EMULATOR_PATH}`);}try{;(0,_utils.runCommand)(`${EMULATOR_PATH} -version`);progress.log("Emulator validation successful","success");}catch(error){throw new Error(`Emulator is not working properly: ${error.message}`);}try{const avdOutput=(0,_utils.runCommand)(`${EMULATOR_PATH} -list-avds`);if(!avdOutput.includes(androidConfig.emulatorName)){throw new Error(`Specified emulator "${androidConfig.emulatorName}" not found in available AVDs`);}progress.log(`Emulator "${androidConfig.emulatorName}" exists`,"success");}catch(error){throw new Error(`Error checking emulator AVD: ${error.message}`);}}else{progress.log("Skipping emulator validation for release build","info");}progress.log("Android tools validation completed successfully!","success");return{ANDROID_SDK,ADB_PATH,EMULATOR_PATH};}async function detectPhysicalDevice(ADB_PATH){try{progress.log("Detecting physical devices...","info");const devices=(0,_utils.runCommand)(`${ADB_PATH} devices -l`);const lines=devices.split("\n").filter(line=>line.trim()&&!line.includes("List of devices"));for(const line of lines){const parts=line.trim().split(/\s+/);if(parts.length>=2&&parts[1]==="device"){const deviceId=parts[0];// Check if it's a physical device (not emulator)
7
+ if(!deviceId.startsWith("emulator-")){try{// Double-check it's not an emulator using device properties
8
8
  const qemuCheck=(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell getprop ro.kernel.qemu`);if(!qemuCheck.trim()){// Empty means physical device
9
- progress.log(`Physical device detected: ${deviceId}`,'success');// Get device model/name for better logging
10
- let deviceModel='Unknown Device';try{const model=(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell getprop ro.product.model`);deviceModel=model.trim()||deviceModel;}catch(e){// Ignore model detection errors
9
+ progress.log(`Physical device detected: ${deviceId}`,"success");// Get device model/name for better logging
10
+ let deviceModel="Unknown Device";try{const model=(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell getprop ro.product.model`);deviceModel=model.trim()||deviceModel;}catch(e){// Ignore model detection errors
11
11
  }return{id:deviceId,model:deviceModel};}}catch(error){// If we can't check properties, skip this device
12
- continue;}}}}progress.log('No physical devices detected','info');return null;}catch(error){progress.log('Error detecting physical devices: '+error.message,'error');return null;}}async function testPhysicalDeviceInstallation(ADB_PATH,deviceId){try{progress.log(`Testing installation capability on device ${deviceId}...`,'info');// Test 1: Basic ADB connection
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
- 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
- try{const usbDebugging=(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell settings get global adb_enabled`);if(usbDebugging.trim()!=='1'){progress.log('USB debugging not enabled on device','warning');return false;}}catch(error){progress.log('Cannot verify USB debugging status','warning');}// Test 4: Check if we can access package manager
16
- try{(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell pm list packages -3 | head -1`);}catch(error){progress.log('Cannot access package manager on device','error');return false;}progress.log(`Device ${deviceId} is ready for installation`,'success');return true;}catch(error){progress.log(`Installation capability test failed: ${error.message}`,'error');return false;}}async function checkEmulator(ADB_PATH){try{const devices=(0,_utils.runCommand)(`${ADB_PATH} devices`);return devices.includes('emulator');}catch(error){progress.log('Error checking emulator status: '+error.message,'error');return false;}}async function handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig){progress.log('Setting up emulator...','info');const emulatorRunning=await checkEmulator(ADB_PATH);if(!emulatorRunning){progress.log('No emulator running, attempting to start one...','info');await startEmulator(EMULATOR_PATH,androidConfig);await new Promise(resolve=>setTimeout(resolve,5000));}else{progress.log('Emulator already running','success');}return{type:'emulator',name:androidConfig.emulatorName};}async function startEmulator(EMULATOR_PATH,androidConfig){progress.log(`Starting emulator: ${androidConfig.emulatorName}...`,'info');return new Promise((resolve,reject)=>{(0,_child_process.exec)(`${EMULATOR_PATH} -avd ${androidConfig.emulatorName} -read-only > /dev/null &`,(error,stdout,stderr)=>{if(error){progress.log('Error starting emulator: '+error.message,'error');reject(error);}else{progress.log('Emulator started successfully','success');resolve();}});});}async function copyBuildAssets(androidConfig,buildOptimisation=false){if(!buildOptimisation)return;progress.log('Copying build assets to Android project...','info');try{// Define source and destination paths
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
- (0,_utils.runCommand)(`mkdir -p ${destPath}`);// Clear existing destination to avoid conflicts
19
- (0,_utils.runCommand)(`rm -rf ${destPath}/*`);// Files to exclude from copying
20
- const excludePatterns=['route-manifest.json.gz','route-manifest.json.br'];if(buildOptimisation){progress.log('Running with build optimization...','info');const excludeParams=excludePatterns.map(pattern=>`--exclude="${pattern}"`).join(' ');const rsyncCommand=`rsync -av ${excludeParams} ${sourcePath} ${destPath}`;progress.log('Executing rsync command with exclusions...','info');(0,_utils.runCommand)(rsyncCommand);// Verify excluded files don't exist in destination
21
- for(const pattern of excludePatterns){const checkCommand=`find ${destPath} -name "${pattern}" | wc -l`;const count=parseInt((0,_utils.runCommand)(checkCommand).trim(),10);if(count>0){progress.log(`Warning: Found ${count} instances of excluded file ${pattern}`,'warning');(0,_utils.runCommand)(`find ${destPath} -name "${pattern}" -delete`);}}progress.log('Build assets copied with optimization (excluded route-manifest JSON files)','success');}else{progress.log('Running without build optimization...','info');const exclusions=excludePatterns.map(pattern=>`-not -name "${pattern}"`).join(' ');const copyCommand=`find ${sourcePath} -type f ${exclusions} -exec cp --parents {} ${destPath} \\;`;progress.log(`Executing copy command with exclusions...`,'info');(0,_utils.runCommand)(copyCommand);progress.log('Build assets copied successfully!','success');}}catch(error){throw new Error('Error copying build assets: '+error.message);}}async function buildApp(ADB_PATH,androidConfig,buildOptimisation,buildType='debug',targetDevice=null){progress.log('Building and installing app...','info');try{// Build command without monkey launch
12
+ continue;}}}}progress.log("No physical devices detected","info");return null;}catch(error){progress.log("Error detecting physical devices: "+error.message,"error");return null;}}async function testPhysicalDeviceInstallation(ADB_PATH,deviceId){try{progress.log(`Testing installation capability on device ${deviceId}...`,"info");// Test 1: Basic ADB connection
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
+ 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
+ try{const usbDebugging=(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell settings get global adb_enabled`);if(usbDebugging.trim()!=="1"){progress.log("USB debugging not enabled on device","warning");return false;}}catch(error){progress.log("Cannot verify USB debugging status","warning");}// Test 4: Check if we can access package manager
16
+ try{;(0,_utils.runCommand)(`${ADB_PATH} -s ${deviceId} shell pm list packages -3 | head -1`);}catch(error){progress.log("Cannot access package manager on device","error");return false;}progress.log(`Device ${deviceId} is ready for installation`,"success");return true;}catch(error){progress.log(`Installation capability test failed: ${error.message}`,"error");return false;}}async function checkEmulator(ADB_PATH){try{const devices=(0,_utils.runCommand)(`${ADB_PATH} devices`);return devices.includes("emulator");}catch(error){progress.log("Error checking emulator status: "+error.message,"error");return false;}}async function handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig){progress.log("Setting up emulator...","info");const emulatorRunning=await checkEmulator(ADB_PATH);if(!emulatorRunning){progress.log("No emulator running, attempting to start one...","info");await startEmulator(EMULATOR_PATH,androidConfig);await new Promise(resolve=>setTimeout(resolve,5000));}else{progress.log("Emulator already running","success");}return{type:"emulator",name:androidConfig.emulatorName};}async function startEmulator(EMULATOR_PATH,androidConfig){progress.log(`Starting emulator: ${androidConfig.emulatorName}...`,"info");return new Promise((resolve,reject)=>{;(0,_child_process.exec)(`${EMULATOR_PATH} -avd ${androidConfig.emulatorName} -read-only > /dev/null &`,(error,stdout,stderr)=>{if(error){progress.log("Error starting emulator: "+error.message,"error");reject(error);}else{progress.log("Emulator started successfully","success");resolve();}});});}async function copyBuildAssets(androidConfig,buildOptimisation=false){if(!buildOptimisation)return;progress.log("Copying build assets to Android project...","info");try{// Define source and destination paths
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
+ ;(0,_utils.runCommand)(`mkdir -p ${destPath}`)// Clear existing destination to avoid conflicts
19
+ ;(0,_utils.runCommand)(`rm -rf ${destPath}/*`);// Files to exclude from copying
20
+ const excludePatterns=["route-manifest.json.gz","route-manifest.json.br"];if(buildOptimisation){progress.log("Running with build optimization...","info");const excludeParams=excludePatterns.map(pattern=>`--exclude="${pattern}"`).join(" ");const rsyncCommand=`rsync -av ${excludeParams} ${sourcePath} ${destPath}`;progress.log("Executing rsync command with exclusions...","info");(0,_utils.runCommand)(rsyncCommand);// Verify excluded files don't exist in destination
21
+ for(const pattern of excludePatterns){const checkCommand=`find ${destPath} -name "${pattern}" | wc -l`;const count=parseInt((0,_utils.runCommand)(checkCommand).trim(),10);if(count>0){progress.log(`Warning: Found ${count} instances of excluded file ${pattern}`,"warning");(0,_utils.runCommand)(`find ${destPath} -name "${pattern}" -delete`);}}progress.log("Build assets copied with optimization (excluded route-manifest JSON files)","success");}else{progress.log("Running without build optimization...","info");const exclusions=excludePatterns.map(pattern=>`-not -name "${pattern}"`).join(" ");const copyCommand=`find ${sourcePath} -type f ${exclusions} -exec cp --parents {} ${destPath} \\;`;progress.log(`Executing copy command with exclusions...`,"info");(0,_utils.runCommand)(copyCommand);progress.log("Build assets copied successfully!","success");}}catch(error){throw new Error("Error copying build assets: "+error.message);}}async function buildApp(ADB_PATH,androidConfig,buildOptimisation,buildType="debug",targetDevice=null){progress.log("Building and installing app...","info");try{// Build command without monkey launch
22
22
  let buildCommand=`cd ${pwd}/androidProject && ./gradlew generateWebViewConfig -PconfigPath=${configPath} -PbuildOptimisation=${buildOptimisation} && ./gradlew clean installDebug --quiet --console=rich`;// Add device-specific install target if physical device
23
- if(targetDevice&&targetDevice.type==='physical'){buildCommand=buildCommand.replace('installDebug',`installDebug -Pandroid.injected.target.device=${targetDevice.id}`);}await(0,_utils.runInteractiveCommand)('sh',['-c',buildCommand],{'BUILD SUCCESSFUL':''});progress.log('App build and installation completed successfully!','success');}catch(error){throw new Error('Error building/installing app: '+error.message);}}async function launchApp(ADB_PATH,buildType='debug',targetDevice=null){if(!targetDevice){progress.log('No target device specified, skipping launch','warning');return;}if(targetDevice.type==='physical'){progress.log(`App installed on physical device: ${targetDevice.model}`,'success');progress.log('Manual launch required - check your device to open the app','info');return;}// Launch on emulator
24
- try{progress.log('Launching app on emulator...','info');const packageName=`${ANDROID_PACKAGE}${buildType==='debug'?'.debug':''}`;const launchCommand=`${ADB_PATH} shell monkey -p ${packageName} 1`;await(0,_utils.runInteractiveCommand)('sh',['-c',launchCommand],{});progress.log('App launched successfully on emulator!','success');}catch(error){progress.log(`Warning: Could not auto-launch app: ${error.message}`,'warning');progress.log('App was installed successfully, but auto-launch failed','info');}}// Legacy function for backward compatibility
25
- async function installApp(ADB_PATH,androidConfig,buildOptimisation,buildType='debug'){await buildApp(ADB_PATH,androidConfig,buildOptimisation,buildType);await launchApp(ADB_PATH,buildType,{type:'emulator'});}async function createAABConfig(androidConfig){// Create AAB configuration based on WebView config with defaults applied
23
+ if(targetDevice&&targetDevice.type==="physical"){buildCommand=buildCommand.replace("installDebug",`installDebug -Pandroid.injected.target.device=${targetDevice.id}`);}await(0,_utils.runInteractiveCommand)("sh",["-c",buildCommand],{"BUILD SUCCESSFUL":""});progress.log("App build and installation completed successfully!","success");}catch(error){throw new Error("Error building/installing app: "+error.message);}}async function launchApp(ADB_PATH,buildType="debug",targetDevice=null){if(!targetDevice){progress.log("No target device specified, skipping launch","warning");return;}if(targetDevice.type==="physical"){progress.log(`App installed on physical device: ${targetDevice.model}`,"success");progress.log("Manual launch required - check your device to open the app","info");return;}// Launch on emulator
24
+ try{progress.log("Launching app on emulator...","info");const packageName=`${ANDROID_PACKAGE}${buildType==="debug"?".debug":""}`;const launchCommand=`${ADB_PATH} shell monkey -p ${packageName} 1`;await(0,_utils.runInteractiveCommand)("sh",["-c",launchCommand],{});progress.log("App launched successfully on emulator!","success");}catch(error){progress.log(`Warning: Could not auto-launch app: ${error.message}`,"warning");progress.log("App was installed successfully, but auto-launch failed","info");}}async function copySplashscreenAssets(){try{const sourcePath=`${process.env.PWD}/build/`;const destPath=`${pwd}/androidProject/app/src/main/res`;const configFile=_fs.default.readFileSync(configPath,"utf8");const config=JSON.parse(configFile);const{WEBVIEW_CONFIG={}}=config;const themesFile=`${destPath}/values/themes.xml`;const themesNightFile=`${destPath}/values-night/themes.xml`;let themesContent=_fs.default.readFileSync(themesFile,"utf8");let themesNightContent=_fs.default.readFileSync(themesNightFile,"utf8");const colorsFile=`${destPath}/values/colors.xml`;let colorsContent=_fs.default.readFileSync(colorsFile,"utf8");const splashscreenProperties={icon:'<item name="android:windowSplashScreenAnimatedIcon">@drawable/splashscreen</item>',backgroundColor:'<item name="android:windowSplashScreenBackground">@color/splashscreen_background</item>'};const hasSplashscreenConfig=!!WEBVIEW_CONFIG.splashScreen;// Always remove splashscreen properties and colors first (regardless of config)
25
+ themesContent=themesContent.replace(splashscreenProperties.backgroundColor,"");themesContent=themesContent.replace(splashscreenProperties.icon,"");themesNightContent=themesNightContent.replace(splashscreenProperties.backgroundColor,"");themesNightContent=themesNightContent.replace(splashscreenProperties.icon,"");// Also remove splashscreen color from colors.xml
26
+ const existingColorLine=colorsContent.split("\n").find(line=>line.includes('name="splashscreen_background"'));if(existingColorLine){colorsContent=colorsContent.replace(existingColorLine,"");}if(!hasSplashscreenConfig){progress.log("Removed splashscreen properties from themes.xml and colors.xml","info");_fs.default.writeFileSync(colorsFile,colorsContent);_fs.default.writeFileSync(themesFile,themesContent);_fs.default.writeFileSync(themesNightFile,themesNightContent);return;}const styleEndTag="</style>";const insertPosition=themesContent.lastIndexOf(styleEndTag);if(insertPosition!==-1){const beforeStyle=themesContent.substring(0,insertPosition).trimEnd();const afterStyle=themesContent.substring(insertPosition);const splashscreenProps=Object.values(splashscreenProperties).map(prop=>`\t\t${prop}`).join("\n");// Insert properties with proper spacing
27
+ themesContent=beforeStyle+"\n"+splashscreenProps+"\n\t"+afterStyle;progress.log("Added splashscreen properties to themes.xml","info");}const nightInsertPosition=themesNightContent.lastIndexOf(styleEndTag);if(nightInsertPosition!==-1){const beforeNightStyle=themesNightContent.substring(0,nightInsertPosition).trimEnd();const afterNightStyle=themesNightContent.substring(nightInsertPosition);const splashscreenProps=Object.values(splashscreenProperties).map(prop=>`\t\t${prop}`).join("\n");// Insert properties with proper spacing
28
+ themesNightContent=beforeNightStyle+"\n"+splashscreenProps+"\n\t"+afterNightStyle;progress.log("Added splashscreen properties to themes-night.xml","info");}_fs.default.writeFileSync(themesFile,themesContent);_fs.default.writeFileSync(themesNightFile,themesNightContent);// Check for splashscreen image file in public directory
29
+ const imageFormats=["png","jpg","jpeg","gif","bmp","svg","webp"];let splashscreenImageFound=false;// Ensure public directory exists
30
+ if(!_fs.default.existsSync(publicPath)){progress.log(`Warning: Public directory not found at ${publicPath}`,"warning");return;}// Create drawable directory if it doesn't exist
31
+ const drawableDir=`${destPath}/drawable`;if(!_fs.default.existsSync(drawableDir)){_fs.default.mkdirSync(drawableDir,{recursive:true});}// Remove existing splashscreen assets to avoid conflicts
32
+ for(const format of imageFormats){const existingSplashscreenPath=`${destPath}/drawable/splashscreen.${format}`;if(_fs.default.existsSync(existingSplashscreenPath)){_fs.default.unlinkSync(existingSplashscreenPath);progress.log(`Removed existing splashscreen.${format}`,"info");}}for(const format of imageFormats){const splashscreenImagePath=`${publicPath}/splashscreen.${format}`;if(_fs.default.existsSync(splashscreenImagePath)){// Copy splashscreen image to drawable directory
33
+ const destImagePath=`${destPath}/drawable/splashscreen.${format}`;_fs.default.copyFileSync(splashscreenImagePath,destImagePath);progress.log(`Splashscreen image copied: splashscreen.${format}`,"success");splashscreenImageFound=true;break;}}if(!splashscreenImageFound){// Use the default Android launcher icon
34
+ const launcherIconPath=`${destPath}/drawable/splashscreen_fallback.png`;const destinationPath=`${destPath}/drawable/splashscreen.png`;if(_fs.default.existsSync(launcherIconPath)){_fs.default.copyFileSync(launcherIconPath,destinationPath);progress.log("Using default Android launcher icon as splashscreen","info");}}const backgroundColor=WEBVIEW_CONFIG.splashScreen?.backgroundColor||"#FFFFFF";// Ensure color starts with # and convert to Android format
35
+ const androidColor=backgroundColor.startsWith("#")?`#FF${backgroundColor.slice(1).toUpperCase()}`:`#FF${backgroundColor.toUpperCase()}`;const newColorEntry=`\t<color name="splashscreen_background">${androidColor}</color>`;colorsContent=colorsContent.replace("</resources>",`${newColorEntry}\n</resources>`);_fs.default.writeFileSync(colorsFile,colorsContent);if(!WEBVIEW_CONFIG.splashScreen?.backgroundColor){progress.log("Using fallback background color: #FFFFFF","info");}progress.log("Splashscreen assets copied successfully!","success");}catch(error){progress.log(`Warning: Error copying splashscreen assets: ${error.message}`,"warning");}}async function copyIconAssets(){try{const destPath=`${pwd}/androidProject/app/src/main/res`;const manifestPath=`${pwd}/androidProject/app/src/main/AndroidManifest.xml`;// Check for icon image file in public directory
36
+ const imageFormats=["png","jpg","jpeg","gif","bmp","svg","webp"];let iconImageFound=false;// Ensure public directory exists
37
+ if(!_fs.default.existsSync(publicPath)){progress.log(`Warning: Public directory not found at ${publicPath}`,"warning");return;}// Create drawable directory if it doesn't exist
38
+ const drawableDir=`${destPath}/drawable`;if(!_fs.default.existsSync(drawableDir)){_fs.default.mkdirSync(drawableDir,{recursive:true});}// Remove existing custom icon assets to avoid conflicts
39
+ for(const format of imageFormats){const existingIconPath=`${destPath}/drawable/icon.${format}`;if(_fs.default.existsSync(existingIconPath)){_fs.default.unlinkSync(existingIconPath);progress.log(`Removed existing icon.${format}`,"info");}}// Look for icon in public directory
40
+ for(const format of imageFormats){const iconImagePath=`${publicPath}/icon.${format}`;if(_fs.default.existsSync(iconImagePath)){// Copy icon image to drawable directory
41
+ const destImagePath=`${destPath}/drawable/icon.${format}`;_fs.default.copyFileSync(iconImagePath,destImagePath);progress.log(`App icon copied: icon.${format} -> icon.${format}`,"success");iconImageFound=true;break;}}// If no custom icon found, just log and return (keep default icon)
42
+ if(!iconImageFound){progress.log("No custom app icon found (icon.png/jpg/jpeg), using default","info");if(_fs.default.existsSync(manifestPath)){let manifestContent=_fs.default.readFileSync(manifestPath,"utf8");// Only revert if manifest currently references custom icon
43
+ if(manifestContent.includes('android:icon="@drawable/icon"')){manifestContent=manifestContent.replace(/android:icon="@drawable\/icon"/,'android:icon="@mipmap/ic_launcher"');manifestContent=manifestContent.replace(/android:roundIcon="@drawable\/icon"/,'android:roundIcon="@mipmap/ic_launcher_round"');_fs.default.writeFileSync(manifestPath,manifestContent);progress.log("Reverted AndroidManifest.xml to use default launcher icon","info");}}return;}// Update AndroidManifest.xml if it exists
44
+ if(_fs.default.existsSync(manifestPath)){let manifestContent=_fs.default.readFileSync(manifestPath,"utf8");// Look for the application tag and update the icon reference
45
+ const applicationTagRegex=/<application([^>]*android:icon="[^"]*"[^>]*>)/;const iconAttributeRegex=/android:icon="[^"]*"/;const roundIconAttributeRegex=/android:roundIcon="[^"]*"/;if(applicationTagRegex.test(manifestContent)){// Replace existing icon attribute
46
+ manifestContent=manifestContent.replace(iconAttributeRegex,'android:icon="@drawable/icon"');manifestContent=manifestContent.replace(roundIconAttributeRegex,'android:roundIcon="@drawable/icon"');progress.log("Updated app icon reference in AndroidManifest.xml","success");}_fs.default.writeFileSync(manifestPath,manifestContent);}progress.log("App icon assets processed successfully!","success");}catch(error){progress.log(`Warning: Error copying app icon assets: ${error.message}`,"warning");}}// Legacy function for backward compatibility
47
+ async function installApp(ADB_PATH,androidConfig,buildOptimisation,buildType="debug"){await buildApp(ADB_PATH,androidConfig,buildOptimisation,buildType);await launchApp(ADB_PATH,buildType,{type:"emulator"});}async function createAABConfig(androidConfig){// Create AAB configuration based on WebView config with defaults applied
26
48
  const aabConfig={// Apply defaults here in buildAppAndroid.js
27
49
  projectPath:androidConfig.projectPath||DEFAULT_PROJECT_PATH,deploymentPath:DEFAULT_DEPLOYMENT_PATH,oldProjectName:DEFAULT_OLD_PROJECT_NAME,overwriteExisting:androidConfig.overwriteExisting!==undefined?androidConfig.overwriteExisting:DEFAULT_OVERWRITE_EXISTING,// Required field - use configured value or derive from other config
28
- newProjectName:androidConfig.newProjectName||androidConfig.appName||androidConfig.packageName?.split('.').pop()||"catalystapp",// AAB specific settings
50
+ newProjectName:androidConfig.newProjectName||androidConfig.appName||androidConfig.packageName?.split(".").pop()||"catalystapp",// AAB specific settings
29
51
  createSignedAAB:true,outputPath:androidConfig.outputPath||`${process.env.PWD}/build-output`};// Add keystore configuration if available
30
52
  if(androidConfig.keystoreConfig){aabConfig.keystoreConfig=androidConfig.keystoreConfig;}else if(androidConfig.keystore){// Map old keystore format to new format
31
53
  aabConfig.keystoreConfig={keyAlias:androidConfig.keystore.alias||"release",storePassword:androidConfig.keystore.storePassword,keyPassword:androidConfig.keystore.keyPassword,validityYears:25,organizationInfo:{companyName:androidConfig.keystore.organizationName||"YourCompany",city:androidConfig.keystore.city||"YourCity",state:androidConfig.keystore.state||"YourState",countryCode:androidConfig.keystore.countryCode||"US"}};}// Log the configuration being used
32
- progress.log(`AAB Configuration:`,'info');progress.log(` Project Path: ${aabConfig.projectPath} ${aabConfig.projectPath===DEFAULT_PROJECT_PATH?'(default)':'(configured)'}`,'info');progress.log(` Deployment Path: ${aabConfig.deploymentPath} ${aabConfig.deploymentPath===DEFAULT_DEPLOYMENT_PATH?'(default)':'(configured)'}`,'info');progress.log(` Old Project Name: ${aabConfig.oldProjectName} ${aabConfig.oldProjectName===DEFAULT_OLD_PROJECT_NAME?'(default)':'(configured)'}`,'info');progress.log(` New Project Name: ${aabConfig.newProjectName}`,'info');progress.log(` Overwrite Existing: ${aabConfig.overwriteExisting} ${aabConfig.overwriteExisting===DEFAULT_OVERWRITE_EXISTING?'(default)':'(configured)'}`,'info');progress.log(` Output Path: ${aabConfig.outputPath}`,'info');return aabConfig;}async function buildSignedAAB(androidConfig){progress.log('Building signed AAB for release...','info');try{// Create AAB configuration directly without temporary file
54
+ progress.log(`AAB Configuration:`,"info");progress.log(` Project Path: ${aabConfig.projectPath} ${aabConfig.projectPath===DEFAULT_PROJECT_PATH?"(default)":"(configured)"}`,"info");progress.log(` Deployment Path: ${aabConfig.deploymentPath} ${aabConfig.deploymentPath===DEFAULT_DEPLOYMENT_PATH?"(default)":"(configured)"}`,"info");progress.log(` Old Project Name: ${aabConfig.oldProjectName} ${aabConfig.oldProjectName===DEFAULT_OLD_PROJECT_NAME?"(default)":"(configured)"}`,"info");progress.log(` New Project Name: ${aabConfig.newProjectName}`,"info");progress.log(` Overwrite Existing: ${aabConfig.overwriteExisting} ${aabConfig.overwriteExisting===DEFAULT_OVERWRITE_EXISTING?"(default)":"(configured)"}`,"info");progress.log(` Output Path: ${aabConfig.outputPath}`,"info");return aabConfig;}async function buildSignedAAB(androidConfig){progress.log("Building signed AAB for release...","info");try{// Create AAB configuration directly without temporary file
33
55
  const aabConfig=await createAABConfig(androidConfig);// Call the AAB builder directly with config object
34
- await(0,_renameAndroidProject.buildAndroidAAB)(aabConfig);progress.log('Signed AAB build completed successfully!','success');}catch(error){throw new Error('Error building signed AAB: '+error.message);}}async function buildAndroidApp(){// Initialize androidConfig outside try block to ensure it's available in catch
56
+ await(0,_renameAndroidProject.buildAndroidAAB)(aabConfig);progress.log("Signed AAB build completed successfully!","success");}catch(error){throw new Error("Error building signed AAB: "+error.message);}}async function buildAndroidApp(){// Initialize androidConfig outside try block to ensure it's available in catch
35
57
  let androidConfig=null;try{await(0,_setupServer.setupServer)(configPath);// Initialize configuration
36
- progress.start('config');const{WEBVIEW_CONFIG}=await initializeConfig();androidConfig=WEBVIEW_CONFIG.android;const buildType=androidConfig.buildType||'debug';const buildOptimisation=!!androidConfig.buildOptimisation||false;progress.complete('config');// Validate tools and get paths
37
- progress.start('tools');const{ANDROID_SDK,ADB_PATH,EMULATOR_PATH}=await validateAndroidTools(androidConfig);progress.complete('tools');// Device detection and setup (skip for release builds)
38
- let targetDevice=null;if(buildType!=='release'){progress.start('emulator');// Step 1: Check for physical device first
39
- const physicalDevice=await detectPhysicalDevice(ADB_PATH);if(physicalDevice){progress.log(`Found physical device: ${physicalDevice.model} (${physicalDevice.id})`,'success');// Test if we can install on physical device
58
+ progress.start("config");const{WEBVIEW_CONFIG}=await initializeConfig();androidConfig=WEBVIEW_CONFIG.android;const buildType=androidConfig.buildType||"debug";const buildOptimisation=!!androidConfig.buildOptimisation||false;progress.complete("config");// Validate tools and get paths
59
+ progress.start("tools");const{ANDROID_SDK,ADB_PATH,EMULATOR_PATH}=await validateAndroidTools(androidConfig);progress.complete("tools");// Device detection and setup (skip for release builds)
60
+ let targetDevice=null;if(buildType!=="release"){progress.start("emulator");// Step 1: Check for physical device first
61
+ const physicalDevice=await detectPhysicalDevice(ADB_PATH);if(physicalDevice){progress.log(`Found physical device: ${physicalDevice.model} (${physicalDevice.id})`,"success");// Test if we can install on physical device
40
62
  const canInstallOnPhysical=await testPhysicalDeviceInstallation(ADB_PATH,physicalDevice.id);if(canInstallOnPhysical){// Use physical device - skip emulator entirely
41
- targetDevice={type:'physical',id:physicalDevice.id,model:physicalDevice.model};progress.log(`Using physical device: ${physicalDevice.model}`,'success');}else{// Physical device failed, fallback to emulator
42
- progress.log('Physical device installation test failed, falling back to emulator','warning');targetDevice=await handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig);}}else{// No physical device, use emulator (current behavior)
43
- targetDevice=await handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig);}progress.complete('emulator');}else{progress.log('Skipping device setup for release build','info');}// Copy build assets
44
- progress.start('copyAssets');await copyBuildAssets(androidConfig,buildOptimisation);progress.log(`Build optimization: ${buildOptimisation?'Enabled':'Disabled'}`,'info');progress.complete('copyAssets');// Build based on type
45
- if(buildType==='release'){// Build signed AAB for release
46
- progress.start('aab');await buildSignedAAB(androidConfig);progress.complete('aab');}else{// Install debug app for development
47
- progress.start('build');await buildApp(ADB_PATH,androidConfig,buildOptimisation,buildType,targetDevice);await launchApp(ADB_PATH,buildType,targetDevice);progress.complete('build');}// Print build summary
48
- const summaryItems=['Build completed successfully:',{text:`Build Type: ${buildType}`,indent:1,prefix:'โ”œโ”€ ',color:'gray'},{text:`SDK Path: ${androidConfig.sdkPath}`,indent:1,prefix:'โ”œโ”€ ',color:'gray'},{text:`Build Optimization: ${buildOptimisation?'Enabled':'Disabled'}`,indent:1,prefix:'โ”œโ”€ ',color:'gray'}];if(buildType==='release'){summaryItems.push({text:`Output: Signed AAB generated in build-output/`,indent:1,prefix:'โ””โ”€ ',color:'green'});}else{if(targetDevice&&targetDevice.type==='physical'){summaryItems.push({text:`Target Device: ${targetDevice.model} (Physical)`,indent:1,prefix:'โ””โ”€ ',color:'green'});}else{summaryItems.push({text:`Target Device: ${androidConfig.emulatorName} (Emulator)`,indent:1,prefix:'โ””โ”€ ',color:'gray'});}}progress.printTreeContent('Build Summary',summaryItems);process.exit(0);}catch(error){if(progress.currentStep){progress.fail(progress.currentStep.id,error.message);const troubleshootingItems=['Build failed. Please try the following steps:',{text:'Check if Android SDK is properly configured',indent:1,prefix:'โ”œโ”€ ',color:'yellow'},{text:'Verify build assets exist in the source directory',indent:1,prefix:'โ”œโ”€ ',color:'yellow'}];// Use androidConfig safely with null checks
49
- const buildType=androidConfig?.buildType||'debug';// Device-specific troubleshooting
50
- if(buildType!=='release'){// Check if error is related to device issues
51
- if(error.message.includes('physical device')||error.message.includes('installation test')){troubleshootingItems.push('\nPhysical Device Issues:',{text:'Enable Developer Options on your device',indent:1,prefix:'โ”œโ”€ ',color:'yellow'},{text:'Enable USB Debugging in Developer Options',indent:1,prefix:'โ”œโ”€ ',color:'yellow'},{text:'Accept the "Allow USB Debugging" prompt on your device',indent:1,prefix:'โ”œโ”€ ',color:'yellow'},{text:'Try disconnecting and reconnecting your device',indent:1,prefix:'โ”œโ”€ ',color:'yellow'},{text:'Check if "adb devices" shows your device as authorized',indent:1,prefix:'โ”œโ”€ ',color:'yellow'});}if(buildType==='release'){troubleshootingItems.push({text:'Verify keystore configuration for release builds',indent:1,prefix:'โ”œโ”€ ',color:'yellow'},{text:'Check that keystore passwords are properly set',indent:1,prefix:'โ”œโ”€ ',color:'yellow'});}else{troubleshootingItems.push({text:'Verify that the emulator exists and is working',indent:1,prefix:'โ”œโ”€ ',color:'yellow'},{text:'If using physical device, ensure it stays connected',indent:1,prefix:'โ”œโ”€ ',color:'yellow'});}}else{troubleshootingItems.push({text:'Verify keystore configuration for release builds',indent:1,prefix:'โ”œโ”€ ',color:'yellow'},{text:'Check that keystore passwords are properly set',indent:1,prefix:'โ”œโ”€ ',color:'yellow'});}troubleshootingItems.push({text:'Run "npm run setupEmulator:android" to reconfigure Android settings',indent:1,prefix:'โ””โ”€ ',color:'yellow'},'\nVerify Configuration:');// Add configuration details only if androidConfig is available
52
- if(androidConfig){troubleshootingItems.push({text:`Build Type: ${buildType}`,indent:1,prefix:'โ”œโ”€ ',color:'gray'},{text:`Android SDK Path: ${androidConfig.sdkPath||'Not configured'}`,indent:1,prefix:'โ”œโ”€ ',color:'gray'});if(buildType!=='release'){// Show device information if available
53
- if(targetDevice){troubleshootingItems.push({text:`Target Device: ${targetDevice.type==='physical'?`${targetDevice.model} (Physical)`:`${androidConfig.emulatorName} (Emulator)`}`,indent:1,prefix:'โ””โ”€ ',color:'gray'});}else{troubleshootingItems.push({text:`Selected Emulator: ${androidConfig.emulatorName||'Not configured'}`,indent:1,prefix:'โ””โ”€ ',color:'gray'});}}else{troubleshootingItems.push({text:`Output Path: ${androidConfig.outputPath||'build-output/'}`,indent:1,prefix:'โ””โ”€ ',color:'gray'});}}else{troubleshootingItems.push({text:'Configuration could not be loaded',indent:1,prefix:'โ”œโ”€ ',color:'red'},{text:'Check if config/config.json exists and has valid Android configuration',indent:1,prefix:'โ””โ”€ ',color:'red'});}progress.printTreeContent('Troubleshooting Guide',troubleshootingItems);}process.exit(1);}}// Execute the main build process
63
+ targetDevice={type:"physical",id:physicalDevice.id,model:physicalDevice.model};progress.log(`Using physical device: ${physicalDevice.model}`,"success");}else{// Physical device failed, fallback to emulator
64
+ progress.log("Physical device installation test failed, falling back to emulator","warning");targetDevice=await handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig);}}else{// No physical device, use emulator (current behavior)
65
+ targetDevice=await handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig);}progress.complete("emulator");}else{progress.log("Skipping device setup for release build","info");}// Copy build assets
66
+ progress.start("copyAssets");await copyBuildAssets(androidConfig,buildOptimisation);await copySplashscreenAssets();await copyIconAssets();progress.log(`Build optimization: ${buildOptimisation?"Enabled":"Disabled"}`,"info");progress.complete("copyAssets");// Build based on type
67
+ if(buildType==="release"){// Build signed AAB for release
68
+ progress.start("aab");await buildSignedAAB(androidConfig);progress.complete("aab");}else{// Install debug app for development
69
+ progress.start("build");await buildApp(ADB_PATH,androidConfig,buildOptimisation,buildType,targetDevice);await launchApp(ADB_PATH,buildType,targetDevice);progress.complete("build");}// Print build summary
70
+ const summaryItems=["Build completed successfully:",{text:`Build Type: ${buildType}`,indent:1,prefix:"โ”œโ”€ ",color:"gray"},{text:`SDK Path: ${androidConfig.sdkPath}`,indent:1,prefix:"โ”œโ”€ ",color:"gray"},{text:`Build Optimization: ${buildOptimisation?"Enabled":"Disabled"}`,indent:1,prefix:"โ”œโ”€ ",color:"gray"}];if(buildType==="release"){summaryItems.push({text:`Output: Signed AAB generated in build-output/`,indent:1,prefix:"โ””โ”€ ",color:"green"});}else{if(targetDevice&&targetDevice.type==="physical"){summaryItems.push({text:`Target Device: ${targetDevice.model} (Physical)`,indent:1,prefix:"โ””โ”€ ",color:"green"});}else{summaryItems.push({text:`Target Device: ${androidConfig.emulatorName} (Emulator)`,indent:1,prefix:"โ””โ”€ ",color:"gray"});}}progress.printTreeContent("Build Summary",summaryItems);process.exit(0);}catch(error){if(progress.currentStep){progress.fail(progress.currentStep.id,error.message);const troubleshootingItems=["Build failed. Please try the following steps:",{text:"Check if Android SDK is properly configured",indent:1,prefix:"โ”œโ”€ ",color:"yellow"},{text:"Verify build assets exist in the source directory",indent:1,prefix:"โ”œโ”€ ",color:"yellow"}];// Use androidConfig safely with null checks
71
+ const buildType=androidConfig?.buildType||"debug";// Device-specific troubleshooting
72
+ if(buildType!=="release"){// Check if error is related to device issues
73
+ if(error.message.includes("physical device")||error.message.includes("installation test")){troubleshootingItems.push("\nPhysical Device Issues:",{text:"Enable Developer Options on your device",indent:1,prefix:"โ”œโ”€ ",color:"yellow"},{text:"Enable USB Debugging in Developer Options",indent:1,prefix:"โ”œโ”€ ",color:"yellow"},{text:'Accept the "Allow USB Debugging" prompt on your device',indent:1,prefix:"โ”œโ”€ ",color:"yellow"},{text:"Try disconnecting and reconnecting your device",indent:1,prefix:"โ”œโ”€ ",color:"yellow"},{text:'Check if "adb devices" shows your device as authorized',indent:1,prefix:"โ”œโ”€ ",color:"yellow"});}if(buildType==="release"){troubleshootingItems.push({text:"Verify keystore configuration for release builds",indent:1,prefix:"โ”œโ”€ ",color:"yellow"},{text:"Check that keystore passwords are properly set",indent:1,prefix:"โ”œโ”€ ",color:"yellow"});}else{troubleshootingItems.push({text:"Verify that the emulator exists and is working",indent:1,prefix:"โ”œโ”€ ",color:"yellow"},{text:"If using physical device, ensure it stays connected",indent:1,prefix:"โ”œโ”€ ",color:"yellow"});}}else{troubleshootingItems.push({text:"Verify keystore configuration for release builds",indent:1,prefix:"โ”œโ”€ ",color:"yellow"},{text:"Check that keystore passwords are properly set",indent:1,prefix:"โ”œโ”€ ",color:"yellow"});}troubleshootingItems.push({text:'Run "npm run setupEmulator:android" to reconfigure Android settings',indent:1,prefix:"โ””โ”€ ",color:"yellow"},"\nVerify Configuration:");// Add configuration details only if androidConfig is available
74
+ if(androidConfig){troubleshootingItems.push({text:`Build Type: ${buildType}`,indent:1,prefix:"โ”œโ”€ ",color:"gray"},{text:`Android SDK Path: ${androidConfig.sdkPath||"Not configured"}`,indent:1,prefix:"โ”œโ”€ ",color:"gray"});if(buildType!=="release"){// Show device information if available
75
+ if(targetDevice){troubleshootingItems.push({text:`Target Device: ${targetDevice.type==="physical"?`${targetDevice.model} (Physical)`:`${androidConfig.emulatorName} (Emulator)`}`,indent:1,prefix:"โ””โ”€ ",color:"gray"});}else{troubleshootingItems.push({text:`Selected Emulator: ${androidConfig.emulatorName||"Not configured"}`,indent:1,prefix:"โ””โ”€ ",color:"gray"});}}else{troubleshootingItems.push({text:`Output Path: ${androidConfig.outputPath||"build-output/"}`,indent:1,prefix:"โ””โ”€ ",color:"gray"});}}else{troubleshootingItems.push({text:"Configuration could not be loaded",indent:1,prefix:"โ”œโ”€ ",color:"red"},{text:"Check if config/config.json exists and has valid Android configuration",indent:1,prefix:"โ””โ”€ ",color:"red"});}progress.printTreeContent("Troubleshooting Guide",troubleshootingItems);}process.exit(1);}}// Execute the main build process
54
76
  buildAndroidApp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catalyst-core",
3
- "version": "0.0.3-canary.7",
3
+ "version": "0.0.3-canary.8",
4
4
  "main": "index.js",
5
5
  "description": "Web framework that provides great performance out of the box",
6
6
  "bin": {
@@ -1,13 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="DeviceTable">
4
- <option name="columnSorters">
5
- <list>
6
- <ColumnSorterState>
7
- <option name="column" value="Name" />
8
- <option name="order" value="ASCENDING" />
9
- </ColumnSorterState>
10
- </list>
11
- </option>
12
- </component>
13
- </project>