vaderjs-native 1.0.30 → 1.0.32

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.
@@ -22,7 +22,7 @@ To build and run on Android, you need the **Android SDK**:
22
22
  # Add to your .bashrc, .zshrc, or Windows ENV
23
23
  ANDROID_HOME=$HOME/Android/Sdk
24
24
  PATH=$PATH:$ANDROID_HOME/platform-tools
25
- ```
25
+
26
26
 
27
27
  ### 3. Windows Setup (Desktop)
28
28
 
@@ -208,4 +208,5 @@ VaderNative implements **Native Pipe & Log Tailing**.
208
208
  * **Hot Module Replacement:** Fast development with real-time updates
209
209
  * **Cross-Platform:** Build for web, Android, and Windows from the same codebase
210
210
 
211
-
211
+
212
+
@@ -10,6 +10,7 @@ import { logger } from "../logger.js";
10
10
  import { loadConfig } from "../../main.js";
11
11
  import { Config } from "../../config/index.js";
12
12
  import { fetchBinary } from "../binaries/fetch.js";
13
+
13
14
  const PROJECT_ROOT = process.cwd();
14
15
  const DIST_DIR = path.join(PROJECT_ROOT, "dist");
15
16
 
@@ -30,16 +31,13 @@ function patchPermissions(buildDir: string) {
30
31
 
31
32
  let content = fsSync.readFileSync(manifestPath, "utf8");
32
33
 
33
- // Remove existing permissions
34
34
  content = content.replace(/<uses-permission android:name="[^"]*" \/>/g, "");
35
35
 
36
- // Add basic permissions
37
36
  const basePerms = [
38
37
  ' <uses-permission android:name="android.permission.INTERNET" />',
39
38
  ' <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />'
40
39
  ];
41
40
 
42
- // Insert permissions before application tag
43
41
  const applicationIndex = content.indexOf("<application");
44
42
  if (applicationIndex !== -1) {
45
43
  const beforeApplication = content.substring(0, applicationIndex);
@@ -54,16 +52,12 @@ function patchPermissions(buildDir: string) {
54
52
  async function ensureLocalProperties(buildDir: string, sdkPath?: string) {
55
53
  const localPropsPath = path.join(buildDir, "local.properties");
56
54
 
57
- // If the file already exists, skip
58
-
59
- // Determine SDK path if not passed in
60
55
  if (!sdkPath) {
61
56
  const sdkInfo = findAndroidSdk();
62
57
  sdkPath = sdkInfo?.sdkPath;
63
58
  if (!sdkPath) throw new Error("Android SDK not found");
64
59
  }
65
60
 
66
- // Write local.properties with the proper sdk path
67
61
  await fs.writeFile(
68
62
  localPropsPath,
69
63
  `sdk.dir=${sdkPath.replace(/\\/g, "\\\\")}\n`
@@ -72,9 +66,7 @@ async function ensureLocalProperties(buildDir: string, sdkPath?: string) {
72
66
  logger.success(`✅ Created local.properties → ${sdkPath}`);
73
67
  }
74
68
 
75
-
76
69
  async function copyDir(src: string, dest: string) {
77
- // Async recursive copy with explicit encoding handling
78
70
  const entries = await fs.readdir(src, { withFileTypes: true });
79
71
  await fs.mkdir(dest, { recursive: true });
80
72
  await Promise.all(entries.map(async (entry) => {
@@ -83,7 +75,6 @@ async function copyDir(src: string, dest: string) {
83
75
  if (entry.isDirectory()) {
84
76
  await copyDir(srcPath, destPath);
85
77
  } else {
86
- // For .kt files, read and write to ensure encoding is preserved
87
78
  if (entry.name.endsWith('.kt')) {
88
79
  const content = await fs.readFile(srcPath, "utf8");
89
80
  await fs.writeFile(destPath, content, "utf8");
@@ -94,25 +85,6 @@ async function copyDir(src: string, dest: string) {
94
85
  }));
95
86
  }
96
87
 
97
- async function patchAllKotlinFiles(javaDir: string, APP_ID: string) {
98
- async function patchDir(dir: string) {
99
- const entries = await fs.readdir(dir, { withFileTypes: true });
100
- await Promise.all(entries.map(async (entry) => {
101
- const fullPath = path.join(dir, entry.name);
102
- if (entry.isDirectory()) {
103
- await patchDir(fullPath);
104
- } else if (entry.name.endsWith('.kt')) {
105
- let content = await fs.readFile(fullPath, "utf8");
106
- content = content.replace(/package \{\{APP_PACKAGE\}\}/g, `package ${APP_ID}`);
107
- await fs.writeFile(fullPath, content, "utf8");
108
- }
109
- }));
110
- }
111
-
112
- await patchDir(javaDir);
113
- logger.success("✅ All Kotlin files patched with package name");
114
- }
115
-
116
88
  async function removeDir(dir: string) {
117
89
  if (existsSync(dir)) {
118
90
  try {
@@ -120,7 +92,6 @@ async function removeDir(dir: string) {
120
92
  } catch (error: any) {
121
93
  if (error.code === 'EBUSY') {
122
94
  logger.warn(`⚠️ Directory ${dir} is busy, retrying...`);
123
- // Wait and retry
124
95
  await new Promise(resolve => setTimeout(resolve, 1000));
125
96
  try {
126
97
  await fs.rm(dir, { recursive: true, force: true });
@@ -145,7 +116,6 @@ async function patchMainActivity(buildDir: string, APP_ID: string, isDev: boolea
145
116
 
146
117
  let content = await fs.readFile(mainActivityPath, "utf8");
147
118
 
148
- // Replace package declaration
149
119
  content = content.replace(/package \{\{APP_PACKAGE\}\}/g, `package ${APP_ID}`);
150
120
 
151
121
  const baseUrl = isDev
@@ -158,19 +128,6 @@ async function patchMainActivity(buildDir: string, APP_ID: string, isDev: boolea
158
128
  logger.success(`✅ MainActivity patched → ${baseUrl} (${isDev ? "DEV" : "PROD"} mode)`);
159
129
  }
160
130
 
161
- async function patchAndroidBridge(buildDir: string, APP_ID: string) {
162
- const javaDir = path.join(buildDir, "app", "src", "main", "java");
163
- const packageDir = path.join(javaDir, ...APP_ID.split("."));
164
- const bridgePath = path.join(packageDir, "AndroidBridge.kt");
165
-
166
- if (!existsSync(bridgePath)) return;
167
-
168
- let content = await fs.readFile(bridgePath, "utf8");
169
- content = content.replace(/package \{\{APP_PACKAGE\}\}/g, `package ${APP_ID}`);
170
- await fs.writeFile(bridgePath, content, "utf8");
171
- logger.success("✅ AndroidBridge patched");
172
- }
173
-
174
131
  async function copyAssets(buildDir: string, APP_ID: string) {
175
132
  const assetsDir = path.join(buildDir, "app", "src", "main", "assets", APP_ID);
176
133
  await removeDir(assetsDir);
@@ -196,7 +153,6 @@ async function renamePackage(buildDir: string, oldPackage: string, newPackage: s
196
153
  return;
197
154
  }
198
155
 
199
- // Create parent directory first
200
156
  await fs.mkdir(path.dirname(newDir), { recursive: true });
201
157
 
202
158
  if (existsSync(newDir)) {
@@ -223,12 +179,10 @@ function patchAppMeta(buildDir: string, config: Config) {
223
179
 
224
180
  let content = fsSync.readFileSync(manifestPath, "utf8");
225
181
 
226
- // Update app name and label if provided
227
182
  if (config.app?.name) {
228
183
  content = content.replace(/android:label="[^"]*"/, `android:label="${config.app.name}"`);
229
184
  }
230
185
 
231
- // Update version info if provided
232
186
  if (config.app?.version) {
233
187
  content = content.replace(/android:versionCode="[^"]*"/, `android:versionCode="${config.app.version.code}"`);
234
188
  content = content.replace(/android:versionName="[^"]*"/, `android:versionName="${config.app.version.name}"`);
@@ -277,30 +231,35 @@ export async function buildAndroid(isDev = false) {
277
231
  logger.step("🚀 Android Build");
278
232
  ensureAndroidInstalled();
279
233
 
280
- // FIX: Remove the duplicate mkdir call
281
- // 1️⃣ Clean old build folder with retry
234
+ // 1️⃣ Clean old build folder
282
235
  try {
283
236
  await removeDir(BUILD_DIR);
284
237
  } catch (error) {
285
238
  logger.warn(`⚠️ Could not clean build directory, continuing...`);
286
239
  }
287
- // DON'T call mkdir here - it will be created by copyDir later
288
240
 
289
- // 2️⃣ Copy template asynchronously
241
+ // 2️⃣ Copy template
290
242
  await copyDir(BUILD_SRC, BUILD_DIR);
291
243
 
292
- // 3️⃣ Rename package and patch MainActivity
244
+ // 3️⃣ Rename package and patch files
293
245
  await renamePackage(BUILD_DIR, "myapp", APP_ID);
294
246
 
295
247
  await ensureLocalProperties(BUILD_DIR);
248
+
296
249
  // Patch AndroidBridge.kt directly
297
- const bridgePath = path.join(BUILD_DIR, "app", "src", "main", "java", ...APP_ID.split("."), "AndroidBridge.kt");
298
- if (existsSync(bridgePath)) {
250
+ const bridgePath = path.join(BUILD_DIR, "app", "src", "main", "java", ...APP_ID.split("."), "AndroidBridge.kt");
251
+ if (existsSync(bridgePath)) {
299
252
  let bridgeContent = await fs.readFile(bridgePath, "utf8");
300
- bridgeContent = `package ${APP_ID}
301
- ${bridgeContent}`;
253
+
254
+ // Remove any existing package declaration
255
+ bridgeContent = bridgeContent.replace(/^package\s+[^\n]+\n/, '');
256
+
257
+ // Add correct package declaration at the beginning
258
+ bridgeContent = `package ${APP_ID}\n\n${bridgeContent}`;
259
+
302
260
  await fs.writeFile(bridgePath, bridgeContent, "utf8");
303
- }
261
+ logger.success("✅ AndroidBridge patched with package name");
262
+ }
304
263
 
305
264
  await patchMainActivity(BUILD_DIR, APP_ID, isDev, config);
306
265
  await patchGradleFiles(BUILD_DIR, APP_ID);
@@ -311,7 +270,7 @@ export async function buildAndroid(isDev = false) {
311
270
  // 5️⃣ Clean Gradle artifacts
312
271
  await removeDir(path.join(BUILD_DIR, "app", "build"));
313
272
 
314
- // 6️⃣ Local properties, permissions, meta, assets
273
+ // 6️⃣ Apply patches and copy assets
315
274
  patchPermissions(BUILD_DIR);
316
275
  patchAppMeta(BUILD_DIR, config);
317
276
  await copyAssets(BUILD_DIR, APP_ID);
@@ -320,30 +279,75 @@ export async function buildAndroid(isDev = false) {
320
279
  await addDeepLinks(BUILD_DIR);
321
280
  }
322
281
 
323
- // 7️⃣ Gradle build
282
+ // 7️⃣ Gradle build - FIXED: Use Bun.spawn instead of Node's spawn
324
283
  let gradleCmd = process.platform === "win32"
325
284
  ? path.join(BUILD_DIR, "gradlew.bat")
326
285
  : path.join(BUILD_DIR, "gradlew");
327
286
 
328
- if (!existsSync(gradleCmd)) gradleCmd = "gradle";
287
+ // Check if gradlew exists
288
+ if (!existsSync(gradleCmd)) {
289
+ logger.error(`❌ Gradle wrapper not found at: ${gradleCmd}`);
290
+ logger.info("⚠️ Trying to use system gradle...");
291
+ gradleCmd = "gradle";
292
+ } else {
293
+ logger.info(`✅ Found gradlew at: ${gradleCmd}`);
294
+ }
329
295
 
330
- logger.info("⚙️ Running Gradle assembleDebug (--no-daemon)...");
331
- await new Promise<void>((resolve, reject) => {
332
- const proc = spawn(gradleCmd, ["assembleDebug", "--no-daemon"], {
296
+ logger.info("⚙️ Running Gradle assembleDebug...");
297
+
298
+ // Use Bun.spawn which handles paths better
299
+ try {
300
+ const proc = Bun.spawn([gradleCmd, "assembleDebug", "--no-daemon"], {
333
301
  cwd: BUILD_DIR,
334
- stdio: "inherit",
335
- shell: true
302
+ stdout: "inherit",
303
+ stderr: "inherit",
304
+ stdin: "inherit"
336
305
  });
337
- proc.on("exit", code => (code === 0 ? resolve() : reject(new Error(`❌ Gradle failed (${code})`))));
338
- proc.on("error", reject);
339
- });
340
306
 
341
- // 8️⃣ Cleanup lingering Java processes
307
+ const exitCode = await proc.exited;
308
+
309
+ if (exitCode !== 0) {
310
+ throw new Error(`❌ Gradle failed with exit code ${exitCode}`);
311
+ }
312
+
313
+ logger.success("✅ Gradle build completed successfully");
314
+ } catch (error: any) {
315
+ logger.error(`❌ Failed to run Gradle: ${error.message}`);
316
+
317
+ // Fallback: try using cmd /c for Windows
318
+ if (process.platform === "win32") {
319
+ logger.info("🔄 Trying fallback method with cmd /c...");
320
+ try {
321
+ const cmd = `cd /d "${BUILD_DIR}" && "${gradleCmd}" assembleDebug --no-daemon`;
322
+ logger.info(`📝 Running: ${cmd}`);
323
+
324
+ const proc = Bun.spawn(["cmd", "/c", cmd], {
325
+ stdout: "inherit",
326
+ stderr: "inherit",
327
+ stdin: "inherit"
328
+ });
329
+
330
+ const exitCode = await proc.exited;
331
+
332
+ if (exitCode !== 0) {
333
+ throw new Error(`❌ Fallback also failed with exit code ${exitCode}`);
334
+ }
335
+
336
+ logger.success("✅ Gradle build completed with fallback method");
337
+ } catch (fallbackError: any) {
338
+ throw new Error(`❌ All Gradle build attempts failed: ${fallbackError.message}`);
339
+ }
340
+ } else {
341
+ throw error;
342
+ }
343
+ }
344
+
345
+ // 8️⃣ Cleanup Java processes if needed
342
346
  try {
343
347
  if (process.platform === "win32") {
344
- execSync("taskkill /F /IM java.exe /T", { stdio: "ignore" });
348
+ execSync("taskkill /F /IM java.exe /T 2>nul", { stdio: "ignore" });
345
349
  } else {
346
- execSync("pkill -f java", { stdio: "ignore" });
350
+ execSync("pkill -f java 2>/dev/null", { stdio: "ignore" });
347
351
  }
348
352
  } catch {}
349
353
 
@@ -351,7 +355,6 @@ export async function buildAndroid(isDev = false) {
351
355
  const APK_SRC = path.join(BUILD_DIR, "app", "build", "outputs", "apk", "debug", "app-debug.apk");
352
356
  const APK_DEST_DIR = path.join(PROJECT_ROOT, "build");
353
357
 
354
- // FIX: Use existsSync check before mkdir
355
358
  if (!existsSync(APK_DEST_DIR)) {
356
359
  await fs.mkdir(APK_DEST_DIR, { recursive: true });
357
360
  }
@@ -359,7 +362,14 @@ export async function buildAndroid(isDev = false) {
359
362
  const APK_DEST = path.join(APK_DEST_DIR, `${APP_ID}-debug.apk`);
360
363
 
361
364
  if (!existsSync(APK_SRC)) {
362
- throw new Error(`❌ APK not found after build at: ${APK_SRC}`);
365
+ // Try alternative APK location
366
+ const altApkPath = path.join(BUILD_DIR, "app", "build", "outputs", "apk", "debug", "app-debug.apk");
367
+ if (existsSync(altApkPath)) {
368
+ await fs.copyFile(altApkPath, APK_DEST);
369
+ logger.success(`✅ APK ready → ${APK_DEST}`);
370
+ return APK_DEST;
371
+ }
372
+ throw new Error(`❌ APK not found after build. Checked: ${APK_SRC}`);
363
373
  }
364
374
 
365
375
  await fs.copyFile(APK_SRC, APK_DEST);
package/index.ts CHANGED
@@ -27,6 +27,7 @@ if (typeof window !== 'undefined') {
27
27
  }
28
28
 
29
29
  // Error Boundary Component
30
+ // Error Boundary Component - FIXED VERSION
30
31
  export function ErrorBoundary({
31
32
  children,
32
33
  fallback,
@@ -35,6 +36,44 @@ export function ErrorBoundary({
35
36
  children: VNode | VNode[];
36
37
  fallback?: (error: Error, reset: () => void) => VNode;
37
38
  onError?: (error: Error, errorInfo: { componentStack: string }) => void;
39
+ }): VNode | null {
40
+ // We need to use try-catch because hooks can also throw errors
41
+ try {
42
+ // Move the hook calls into a separate component
43
+ return ErrorBoundaryInner({ children, fallback, onError });
44
+ } catch (error) {
45
+ // If hooks fail, render a simple fallback
46
+ const errorObj = error instanceof Error ? error : new Error(String(error));
47
+ if (fallback) {
48
+ return fallback(errorObj, () => window.location.reload());
49
+ }
50
+
51
+ return createElement(
52
+ "div",
53
+ {
54
+ style: {
55
+ padding: '20px',
56
+ backgroundColor: '#f8d7da',
57
+ color: '#721c24',
58
+ border: '1px solid #f5c6cb',
59
+ borderRadius: '5px'
60
+ }
61
+ },
62
+ createElement("h3", {}, "Error Boundary Failed"),
63
+ createElement("pre", { style: { whiteSpace: 'pre-wrap' } }, errorObj.toString())
64
+ );
65
+ }
66
+ }
67
+
68
+ // Inner component that uses hooks
69
+ function ErrorBoundaryInner({
70
+ children,
71
+ fallback,
72
+ onError
73
+ }: {
74
+ children: VNode | VNode[];
75
+ fallback?: (error: Error, reset: () => void) => VNode;
76
+ onError?: (error: Error, errorInfo: { componentStack: string }) => void;
38
77
  }): VNode | null {
39
78
  const [error, setError] = useState<Error | null>(null);
40
79
  const [errorInfo, setErrorInfo] = useState<{ componentStack: string } | null>(null);
@@ -44,21 +83,38 @@ export function ErrorBoundary({
44
83
  setErrorInfo(null);
45
84
  };
46
85
 
47
- // Listen for global errors
48
86
  useEffect(() => {
49
- const originalHandler = globalErrorHandler;
50
- globalErrorHandler = (error: Error, componentStack?: string) => {
51
- setError(error);
87
+ // Handle errors from children components
88
+ const handleError = (err: Error, componentStack?: string) => {
89
+ setError(err);
52
90
  if (componentStack) {
53
91
  setErrorInfo({ componentStack });
54
92
  }
55
93
  if (onError) {
56
- onError(error, { componentStack: componentStack || '' });
94
+ onError(err, { componentStack: componentStack || '' });
57
95
  }
58
96
  };
59
-
97
+
98
+ // Store the previous handler
99
+ const previousHandler = globalErrorHandler;
100
+ globalErrorHandler = handleError;
101
+
102
+ // Also catch unhandled errors and promise rejections
103
+ const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
104
+ handleError(event.reason, 'Unhandled Promise Rejection');
105
+ };
106
+
107
+ const handleUncaughtError = (event: ErrorEvent) => {
108
+ handleError(event.error, 'Uncaught Error');
109
+ };
110
+
111
+ window.addEventListener('unhandledrejection', handleUnhandledRejection);
112
+ window.addEventListener('error', handleUncaughtError);
113
+
60
114
  return () => {
61
- globalErrorHandler = originalHandler;
115
+ globalErrorHandler = previousHandler;
116
+ window.removeEventListener('unhandledrejection', handleUnhandledRejection);
117
+ window.removeEventListener('error', handleUncaughtError);
62
118
  };
63
119
  }, [onError]);
64
120
 
@@ -90,7 +146,7 @@ export function ErrorBoundary({
90
146
  color: '#ff6b6b',
91
147
  marginBottom: '20px',
92
148
  fontSize: '24px'
93
- } }, "⚠️ VaderJS Error"),
149
+ } }, "⚠️ Application Error"),
94
150
 
95
151
  createElement("div", { style: {
96
152
  backgroundColor: '#2a2a2a',
@@ -838,7 +894,7 @@ export function render(element: VNode, container: Node): void {
838
894
  wipRoot = {
839
895
  dom: container,
840
896
  props: {
841
- children: [element],
897
+ children: isDev ? [createElement(ErrorBoundary, {}, element)] : [element],
842
898
  },
843
899
  alternate: currentRoot,
844
900
  };
@@ -2237,10 +2293,4 @@ Object.defineProperty(window, "Vader", {
2237
2293
  configurable: false,
2238
2294
  });
2239
2295
 
2240
- if (isDev) {
2241
- const originalRender = Vader.render;
2242
- Vader.render = function(element: VNode, container: Node) {
2243
- // Wrap in error boundary in dev mode
2244
- renderWithErrorBoundary(element, container);
2245
- };
2246
- }
2296
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vaderjs-native",
3
- "version": "1.0.30",
4
- "binaryVersion":"1.0.26",
3
+ "version": "1.0.32",
4
+ "binaryVersion": "1.0.26",
5
5
  "description": "Build Native Applications using Vaderjs framework.",
6
6
  "bin": {
7
7
  "vaderjs": "./main.ts"
@@ -11,8 +11,8 @@
11
11
  "url": "https://github.com/Postr-Inc/Vaderjs-Native"
12
12
  },
13
13
  "license": "MIT",
14
- "dependencies": {
14
+ "dependencies": {
15
15
  "vaderjs-types": "latest",
16
- "extract-zip":"latest"
17
- }
16
+ "extract-zip": "latest"
17
+ }
18
18
  }