@jetstart/core 2.3.0 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ Central build server and real-time orchestration layer for JetStart.
4
4
 
5
5
  ## Overview
6
6
 
7
- `@jetstart/core` is the engine that powers `jetstart`. It runs three networked services and orchestrates the complete hot reload pipeline — from detecting a file change to having new code running on a physical Android device in under 100ms.
7
+ `@jetstart/core` is the engine that powers `jetstart`. It runs three networked services and orchestrates the complete hot reload pipeline — from detecting a file change to having new code running on a physical Android device in instantly.
8
8
 
9
9
  ```
10
10
  src/
@@ -42,6 +42,7 @@ const child_process_1 = require("child_process");
42
42
  const fs = __importStar(require("fs"));
43
43
  const path = __importStar(require("path"));
44
44
  const os = __importStar(require("os"));
45
+ const shared_1 = require("@jetstart/shared");
45
46
  const parser_1 = require("./parser");
46
47
  /**
47
48
  * ADB Helper for auto-installing APKs
@@ -288,16 +289,18 @@ class GradleExecutor {
288
289
  async execute(config) {
289
290
  const startTime = Date.now();
290
291
  const gradlePath = this.findGradle(config.projectPath);
291
- // If Gradle not found, return mock build for testing
292
+ // If Gradle not found, fail build with clear error
292
293
  if (!gradlePath) {
293
- console.log('[Gradle] No Gradle found, returning mock successful build for testing');
294
- // Simulate build delay
295
- await new Promise(resolve => setTimeout(resolve, 2000));
296
294
  return {
297
- success: true,
295
+ success: false,
298
296
  buildTime: Date.now() - startTime,
299
- apkPath: path.join(config.projectPath, 'build/outputs/apk/debug/app-debug.apk'),
300
- apkSize: 5242880, // Mock size: 5MB
297
+ errors: [{
298
+ file: 'gradle',
299
+ line: 0,
300
+ column: 0,
301
+ message: 'Gradle not found. Please install Gradle 8.0+ or ensure the Gradle wrapper (gradlew) exists in your project project.',
302
+ severity: shared_1.ErrorSeverity.ERROR
303
+ }]
301
304
  };
302
305
  }
303
306
  // Ensure Android SDK is configured
@@ -22,6 +22,13 @@ export declare class KotlinCompiler {
22
22
  findComposeCompiler(): Promise<string | null>;
23
23
  /**
24
24
  * Find kotlinc executable
25
+ *
26
+ * Search order:
27
+ * 1. KOTLIN_HOME env var (set in .env or shell)
28
+ * 2. ANDROID_STUDIO_HOME env var
29
+ * 3. Platform-specific static paths (Scoop, Chocolatey, SDKMAN, snap, …)
30
+ * 4. Versioned IDE directories (IntelliJ IDEA, JetBrains Toolbox, Android Studio)
31
+ * 5. `where` / `which` fallback
25
32
  */
26
33
  findKotlinc(): Promise<string | null>;
27
34
  /**
@@ -102,27 +102,88 @@ class KotlinCompiler {
102
102
  }
103
103
  /**
104
104
  * Find kotlinc executable
105
+ *
106
+ * Search order:
107
+ * 1. KOTLIN_HOME env var (set in .env or shell)
108
+ * 2. ANDROID_STUDIO_HOME env var
109
+ * 3. Platform-specific static paths (Scoop, Chocolatey, SDKMAN, snap, …)
110
+ * 4. Versioned IDE directories (IntelliJ IDEA, JetBrains Toolbox, Android Studio)
111
+ * 5. `where` / `which` fallback
105
112
  */
106
113
  async findKotlinc() {
107
114
  if (this.kotlincPath)
108
115
  return this.kotlincPath;
109
- // Check common locations
116
+ const win = os.platform() === 'win32';
117
+ const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
118
+ const progFiles = process.env.PROGRAMFILES || 'C:\\Program Files';
119
+ /**
120
+ * Scan `parentDir` for subdirectories whose names start with `entryPrefix`
121
+ * (pass '' to accept any entry), then return the first candidate that exists:
122
+ * path.join(parentDir, entry, ...rest)
123
+ * Entries are sorted descending so the latest version is tried first.
124
+ * Returns null (never throws) if the directory is missing or unreadable.
125
+ */
126
+ const findInVersionedDir = (parentDir, entryPrefix, ...rest) => {
127
+ try {
128
+ if (!fs.existsSync(parentDir))
129
+ return null;
130
+ const entries = fs.readdirSync(parentDir)
131
+ .filter(e => !entryPrefix || e.toLowerCase().startsWith(entryPrefix.toLowerCase()))
132
+ .sort()
133
+ .reverse(); // latest version first
134
+ for (const entry of entries) {
135
+ const candidate = path.join(parentDir, entry, ...rest);
136
+ if (fs.existsSync(candidate))
137
+ return candidate;
138
+ }
139
+ }
140
+ catch { /* permission / access errors — silently skip */ }
141
+ return null;
142
+ };
110
143
  const locations = [
111
- // From environment variable
112
- process.env.KOTLIN_HOME ? path.join(process.env.KOTLIN_HOME, 'bin', 'kotlinc') : null,
113
- // From Android Studio
114
- process.env.ANDROID_STUDIO_HOME ? path.join(process.env.ANDROID_STUDIO_HOME, 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc') : null,
115
- // System-wide installation (Windows)
116
- 'C:\\Program Files\\kotlinc\\bin\\kotlinc.bat',
117
- 'C:\\kotlinc\\bin\\kotlinc.bat',
118
- // System-wide installation (Unix)
119
- '/usr/local/bin/kotlinc',
120
- '/usr/bin/kotlinc',
121
- // Homebrew (macOS)
122
- '/opt/homebrew/bin/kotlinc',
123
- ].filter(Boolean);
144
+ // env overrides
145
+ process.env.KOTLIN_HOME
146
+ ? path.join(process.env.KOTLIN_HOME, 'bin', 'kotlinc')
147
+ : null,
148
+ process.env.ANDROID_STUDIO_HOME
149
+ ? path.join(process.env.ANDROID_STUDIO_HOME, 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc')
150
+ : null,
151
+ // Windows static paths
152
+ win ? path.join(progFiles, 'kotlinc', 'bin', 'kotlinc.bat') : null,
153
+ win ? 'C:\\kotlinc\\bin\\kotlinc.bat' : null,
154
+ // Scoop (~\scoop\apps\kotlin\current)
155
+ win ? path.join(os.homedir(), 'scoop', 'apps', 'kotlin', 'current', 'bin', 'kotlinc.bat') : null,
156
+ // Chocolatey
157
+ win ? 'C:\\ProgramData\\chocolatey\\bin\\kotlinc.bat' : null,
158
+ // Android Studio — standard Google installer
159
+ win ? path.join(progFiles, 'Android', 'Android Studio', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
160
+ win ? path.join(localAppData, 'Programs', 'Android Studio', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
161
+ // Windows versioned IDE paths (wildcard scan)
162
+ // IntelliJ IDEA — any version under C:\Program Files\JetBrains
163
+ win ? findInVersionedDir(path.join(progFiles, 'JetBrains'), 'IntelliJ IDEA', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
164
+ // JetBrains Toolbox — IDEA Ultimate
165
+ win ? findInVersionedDir(path.join(localAppData, 'JetBrains', 'Toolbox', 'apps', 'IDEA-U', 'ch-0'), '', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
166
+ // JetBrains Toolbox — IDEA Community
167
+ win ? findInVersionedDir(path.join(localAppData, 'JetBrains', 'Toolbox', 'apps', 'IDEA-C', 'ch-0'), '', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
168
+ // JetBrains Toolbox — Android Studio (Jellyfish+)
169
+ win ? findInVersionedDir(path.join(localAppData, 'JetBrains', 'Toolbox', 'apps', 'AndroidStudio', 'ch-0'), '', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
170
+ // Unix / macOS static paths
171
+ !win ? '/usr/local/bin/kotlinc' : null,
172
+ !win ? '/usr/bin/kotlinc' : null,
173
+ // Homebrew (Apple Silicon + Intel)
174
+ !win ? '/opt/homebrew/bin/kotlinc' : null,
175
+ !win ? '/usr/local/opt/kotlin/bin/kotlinc' : null,
176
+ // SDKMAN (~/.sdkman/candidates/kotlin/current)
177
+ !win ? path.join(os.homedir(), '.sdkman', 'candidates', 'kotlin', 'current', 'bin', 'kotlinc') : null,
178
+ // Snap
179
+ !win ? '/snap/bin/kotlinc' : null,
180
+ // IntelliJ IDEA — Linux Toolbox
181
+ !win ? findInVersionedDir(path.join(os.homedir(), '.local', 'share', 'JetBrains', 'Toolbox', 'apps', 'IDEA-U', 'ch-0'), '', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc') : null,
182
+ ];
124
183
  for (const loc of locations) {
125
- const execPath = os.platform() === 'win32' && !loc.endsWith('.bat') ? `${loc}.bat` : loc;
184
+ if (!loc)
185
+ continue;
186
+ const execPath = win && !loc.endsWith('.bat') ? `${loc}.bat` : loc;
126
187
  if (fs.existsSync(execPath)) {
127
188
  this.kotlincPath = execPath;
128
189
  (0, logger_1.log)(`Found kotlinc at: ${execPath}`);
@@ -131,7 +192,7 @@ class KotlinCompiler {
131
192
  }
132
193
  // Try to find via 'where' (Windows) or 'which' (Unix)
133
194
  try {
134
- const cmd = os.platform() === 'win32' ? 'where' : 'which';
195
+ const cmd = win ? 'where' : 'which';
135
196
  const result = await this.runCommand(cmd, ['kotlinc']);
136
197
  if (result.success && result.stdout.trim()) {
137
198
  this.kotlincPath = result.stdout.trim().split('\n')[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetstart/core",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Build server and orchestration for JetStart",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -33,8 +33,8 @@
33
33
  },
34
34
  "homepage": "https://github.com/dev-phantom/jetstart#readme",
35
35
  "dependencies": {
36
- "@jetstart/shared": "^2.3.0",
37
- "@jetstart/logs": "^2.3.0",
36
+ "@jetstart/shared": "^2.3.2",
37
+ "@jetstart/logs": "^2.3.2",
38
38
  "express": "^4.18.2",
39
39
  "ws": "^8.14.2",
40
40
  "chokidar": "^3.5.3",
@@ -7,7 +7,7 @@ import { spawn, ChildProcess, execSync } from 'child_process';
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
9
  import * as os from 'os';
10
- import { BuildConfig, BuildResult } from '@jetstart/shared';
10
+ import { BuildConfig, BuildResult, ErrorSeverity } from '@jetstart/shared';
11
11
  import { BuildOutputParser } from './parser';
12
12
 
13
13
  /**
@@ -289,18 +289,18 @@ export class GradleExecutor {
289
289
  const startTime = Date.now();
290
290
  const gradlePath = this.findGradle(config.projectPath);
291
291
 
292
- // If Gradle not found, return mock build for testing
292
+ // If Gradle not found, fail build with clear error
293
293
  if (!gradlePath) {
294
- console.log('[Gradle] No Gradle found, returning mock successful build for testing');
295
-
296
- // Simulate build delay
297
- await new Promise(resolve => setTimeout(resolve, 2000));
298
-
299
294
  return {
300
- success: true,
295
+ success: false,
301
296
  buildTime: Date.now() - startTime,
302
- apkPath: path.join(config.projectPath, 'build/outputs/apk/debug/app-debug.apk'),
303
- apkSize: 5242880, // Mock size: 5MB
297
+ errors: [{
298
+ file: 'gradle',
299
+ line: 0,
300
+ column: 0,
301
+ message: 'Gradle not found. Please install Gradle 8.0+ or ensure the Gradle wrapper (gradlew) exists in your project project.',
302
+ severity: ErrorSeverity.ERROR
303
+ }]
304
304
  };
305
305
  }
306
306
 
@@ -82,28 +82,99 @@ export class KotlinCompiler {
82
82
 
83
83
  /**
84
84
  * Find kotlinc executable
85
+ *
86
+ * Search order:
87
+ * 1. KOTLIN_HOME env var (set in .env or shell)
88
+ * 2. ANDROID_STUDIO_HOME env var
89
+ * 3. Platform-specific static paths (Scoop, Chocolatey, SDKMAN, snap, …)
90
+ * 4. Versioned IDE directories (IntelliJ IDEA, JetBrains Toolbox, Android Studio)
91
+ * 5. `where` / `which` fallback
85
92
  */
86
93
  async findKotlinc(): Promise<string | null> {
87
94
  if (this.kotlincPath) return this.kotlincPath;
88
95
 
89
- // Check common locations
90
- const locations = [
91
- // From environment variable
92
- process.env.KOTLIN_HOME ? path.join(process.env.KOTLIN_HOME, 'bin', 'kotlinc') : null,
93
- // From Android Studio
94
- process.env.ANDROID_STUDIO_HOME ? path.join(process.env.ANDROID_STUDIO_HOME, 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc') : null,
95
- // System-wide installation (Windows)
96
- 'C:\\Program Files\\kotlinc\\bin\\kotlinc.bat',
97
- 'C:\\kotlinc\\bin\\kotlinc.bat',
98
- // System-wide installation (Unix)
99
- '/usr/local/bin/kotlinc',
100
- '/usr/bin/kotlinc',
101
- // Homebrew (macOS)
102
- '/opt/homebrew/bin/kotlinc',
103
- ].filter(Boolean) as string[];
96
+ const win = os.platform() === 'win32';
97
+ const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
98
+ const progFiles = process.env.PROGRAMFILES || 'C:\\Program Files';
99
+
100
+ /**
101
+ * Scan `parentDir` for subdirectories whose names start with `entryPrefix`
102
+ * (pass '' to accept any entry), then return the first candidate that exists:
103
+ * path.join(parentDir, entry, ...rest)
104
+ * Entries are sorted descending so the latest version is tried first.
105
+ * Returns null (never throws) if the directory is missing or unreadable.
106
+ */
107
+ const findInVersionedDir = (
108
+ parentDir: string,
109
+ entryPrefix: string,
110
+ ...rest: string[]
111
+ ): string | null => {
112
+ try {
113
+ if (!fs.existsSync(parentDir)) return null;
114
+ const entries = fs.readdirSync(parentDir)
115
+ .filter(e => !entryPrefix || e.toLowerCase().startsWith(entryPrefix.toLowerCase()))
116
+ .sort()
117
+ .reverse(); // latest version first
118
+ for (const entry of entries) {
119
+ const candidate = path.join(parentDir, entry, ...rest);
120
+ if (fs.existsSync(candidate)) return candidate;
121
+ }
122
+ } catch { /* permission / access errors — silently skip */ }
123
+ return null;
124
+ };
125
+
126
+ const locations: Array<string | null> = [
127
+ // env overrides
128
+ process.env.KOTLIN_HOME
129
+ ? path.join(process.env.KOTLIN_HOME, 'bin', 'kotlinc')
130
+ : null,
131
+ process.env.ANDROID_STUDIO_HOME
132
+ ? path.join(process.env.ANDROID_STUDIO_HOME, 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc')
133
+ : null,
134
+
135
+ // Windows static paths
136
+ win ? path.join(progFiles, 'kotlinc', 'bin', 'kotlinc.bat') : null,
137
+ win ? 'C:\\kotlinc\\bin\\kotlinc.bat' : null,
138
+ // Scoop (~\scoop\apps\kotlin\current)
139
+ win ? path.join(os.homedir(), 'scoop', 'apps', 'kotlin', 'current', 'bin', 'kotlinc.bat') : null,
140
+ // Chocolatey
141
+ win ? 'C:\\ProgramData\\chocolatey\\bin\\kotlinc.bat' : null,
142
+ // Android Studio — standard Google installer
143
+ win ? path.join(progFiles, 'Android', 'Android Studio', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
144
+ win ? path.join(localAppData,'Programs', 'Android Studio', 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
145
+
146
+ // Windows versioned IDE paths (wildcard scan)
147
+ // IntelliJ IDEA — any version under C:\Program Files\JetBrains
148
+ win ? findInVersionedDir(path.join(progFiles, 'JetBrains'), 'IntelliJ IDEA',
149
+ 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
150
+ // JetBrains Toolbox — IDEA Ultimate
151
+ win ? findInVersionedDir(path.join(localAppData, 'JetBrains', 'Toolbox', 'apps', 'IDEA-U', 'ch-0'), '',
152
+ 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
153
+ // JetBrains Toolbox — IDEA Community
154
+ win ? findInVersionedDir(path.join(localAppData, 'JetBrains', 'Toolbox', 'apps', 'IDEA-C', 'ch-0'), '',
155
+ 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
156
+ // JetBrains Toolbox — Android Studio (Jellyfish+)
157
+ win ? findInVersionedDir(path.join(localAppData, 'JetBrains', 'Toolbox', 'apps', 'AndroidStudio', 'ch-0'), '',
158
+ 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc.bat') : null,
159
+
160
+ // Unix / macOS static paths
161
+ !win ? '/usr/local/bin/kotlinc' : null,
162
+ !win ? '/usr/bin/kotlinc' : null,
163
+ // Homebrew (Apple Silicon + Intel)
164
+ !win ? '/opt/homebrew/bin/kotlinc' : null,
165
+ !win ? '/usr/local/opt/kotlin/bin/kotlinc' : null,
166
+ // SDKMAN (~/.sdkman/candidates/kotlin/current)
167
+ !win ? path.join(os.homedir(), '.sdkman', 'candidates', 'kotlin', 'current', 'bin', 'kotlinc') : null,
168
+ // Snap
169
+ !win ? '/snap/bin/kotlinc' : null,
170
+ // IntelliJ IDEA — Linux Toolbox
171
+ !win ? findInVersionedDir(path.join(os.homedir(), '.local', 'share', 'JetBrains', 'Toolbox', 'apps', 'IDEA-U', 'ch-0'), '',
172
+ 'plugins', 'Kotlin', 'kotlinc', 'bin', 'kotlinc') : null,
173
+ ];
104
174
 
105
175
  for (const loc of locations) {
106
- const execPath = os.platform() === 'win32' && !loc.endsWith('.bat') ? `${loc}.bat` : loc;
176
+ if (!loc) continue;
177
+ const execPath = win && !loc.endsWith('.bat') ? `${loc}.bat` : loc;
107
178
  if (fs.existsSync(execPath)) {
108
179
  this.kotlincPath = execPath;
109
180
  log(`Found kotlinc at: ${execPath}`);
@@ -113,7 +184,7 @@ export class KotlinCompiler {
113
184
 
114
185
  // Try to find via 'where' (Windows) or 'which' (Unix)
115
186
  try {
116
- const cmd = os.platform() === 'win32' ? 'where' : 'which';
187
+ const cmd = win ? 'where' : 'which';
117
188
  const result = await this.runCommand(cmd, ['kotlinc']);
118
189
  if (result.success && result.stdout.trim()) {
119
190
  this.kotlincPath = result.stdout.trim().split('\n')[0];