@absolutejs/absolute 0.20.0-beta.69 → 0.20.0-beta.70

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/dist/cli/index.js CHANGED
@@ -1942,6 +1942,7 @@ var init_updateRuntime = __esm(() => {
1942
1942
  // src/mobile/expoNativeObservability.ts
1943
1943
  import { join as join8 } from "path";
1944
1944
  var HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
1945
+ `, XML_HEADER = `<!-- Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync. -->
1945
1946
  `, moduleConfig, podspec, iosModule, androidBuild, androidManifest, androidModule, expoNativeObservabilityRuntimeSource, absoluteExpoNativeObservabilityFiles = (project) => new Map([
1946
1947
  [
1947
1948
  join8(project, "modules/absolute-mobile-observability/expo-module.config.json"),
@@ -2113,13 +2114,16 @@ android {
2113
2114
  lintOptions { abortOnError false }
2114
2115
  }
2115
2116
  `;
2116
- androidManifest = `${HEADER}<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
2117
+ androidManifest = `${XML_HEADER}<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
2117
2118
  `;
2118
2119
  androidModule = `${HEADER}package expo.modules.absolutemobileobservability
2119
2120
 
2120
2121
  import android.app.ActivityManager
2121
2122
  import android.app.ApplicationExitInfo
2123
+ import android.content.pm.ApplicationInfo
2122
2124
  import android.os.Build
2125
+ import android.os.Handler
2126
+ import android.os.Looper
2123
2127
  import android.util.Base64
2124
2128
  import expo.modules.kotlin.modules.Module
2125
2129
  import expo.modules.kotlin.modules.ModuleDefinition
@@ -2138,6 +2142,7 @@ class AbsoluteMobileObservabilityModule : Module() {
2138
2142
  private const val MAX_REPORTS = 8
2139
2143
  private const val MAX_TRACE_BYTES = 32 * 1024
2140
2144
  private const val ACKNOWLEDGED_KEY = "acknowledged"
2145
+ private const val CRASH_TEST_KEY = "crash-test-id"
2141
2146
  private const val PREFERENCES = "AbsoluteMobileObservability"
2142
2147
  }
2143
2148
 
@@ -2162,6 +2167,25 @@ class AbsoluteMobileObservabilityModule : Module() {
2162
2167
  }
2163
2168
  writeAcknowledged(acknowledged)
2164
2169
  }
2170
+
2171
+ AsyncFunction("crashForTesting") { options: Map<String, String> ->
2172
+ val context = context() ?: return@AsyncFunction false
2173
+ if (context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE == 0) {
2174
+ throw IllegalStateException("Native observability crash injection requires a debuggable build.")
2175
+ }
2176
+ val testId = options["testId"]
2177
+ ?.takeIf { it.matches(Regex("[A-Za-z0-9._:-]{1,128}")) }
2178
+ ?: throw IllegalArgumentException("Native observability crash injection requires a valid testId.")
2179
+ val preferences = context.getSharedPreferences(PREFERENCES, 0)
2180
+ if (preferences.getString(CRASH_TEST_KEY, null) == testId) {
2181
+ return@AsyncFunction false
2182
+ }
2183
+ preferences.edit().putString(CRASH_TEST_KEY, testId).commit()
2184
+ Handler(Looper.getMainLooper()).post {
2185
+ throw RuntimeException("AbsoluteJS native observability acceptance crash: $testId")
2186
+ }
2187
+ true
2188
+ }
2165
2189
  }
2166
2190
 
2167
2191
  private fun context() = appContext.reactContext
@@ -2296,6 +2320,7 @@ import { ABSOLUTE_MOBILE_MANIFEST } from './webAssets';
2296
2320
 
2297
2321
  type NativeObservabilityModule = {
2298
2322
  acknowledge(options: { ids: string[] }): Promise<void>;
2323
+ crashForTesting(options: { testId: string }): Promise<boolean>;
2299
2324
  pending(): Promise<{ reports: unknown[] }>;
2300
2325
  };
2301
2326
  type NativeReport = { details: Record<string, unknown>; id: string; kind: string; occurredAt: number; platform: 'android' | 'ios' };
@@ -2356,6 +2381,7 @@ export const startAbsoluteExpoNativeObservability = () => {
2356
2381
  }
2357
2382
  return drain();
2358
2383
  };
2384
+ export const crashAbsoluteExpoNativeObservabilityForTesting = (testId: string) => native.crashForTesting({ testId });
2359
2385
  `;
2360
2386
  });
2361
2387
 
@@ -3792,102 +3818,11 @@ export const ABSOLUTE_MOBILE_MANIFEST: AbsoluteMobileManifest | undefined = unde
3792
3818
  `;
3793
3819
  });
3794
3820
 
3795
- // src/mobile/iosPhysicalDeviceTransport.ts
3796
- import { randomUUID, X509Certificate as X509Certificate2 } from "crypto";
3797
- import { createServer as createServer2 } from "http";
3798
- import {
3799
- connect as connectTcp,
3800
- createServer as createTcpServer,
3801
- isIP
3802
- } from "net";
3803
- import { readFile as readFile2 } from "fs/promises";
3804
- var closeServer = (server) => new Promise((resolve6, reject) => {
3805
- server.close((error) => {
3806
- if (error)
3807
- reject(error);
3808
- else
3809
- resolve6();
3810
- });
3811
- }), listen = (server, port) => new Promise((resolve6, reject) => {
3812
- server.once("error", reject);
3813
- server.listen(port, "0.0.0.0", () => {
3814
- server.off("error", reject);
3815
- const address = server.address();
3816
- if (!address || typeof address === "string") {
3817
- reject(new Error("Could not determine the iOS device helper port."));
3818
- return;
3819
- }
3820
- resolve6(address.port);
3821
- });
3822
- }), findEphemeralPort = async () => {
3823
- const probe = createTcpServer();
3824
- const port = await new Promise((resolve6, reject) => {
3825
- probe.once("error", reject);
3826
- probe.listen(0, "127.0.0.1", () => {
3827
- const address = probe.address();
3828
- if (!address || typeof address === "string") {
3829
- reject(new Error("Could not allocate the iOS CA enrollment port."));
3830
- return;
3831
- }
3832
- resolve6(address.port);
3833
- });
3834
- });
3835
- await closeServer(probe);
3836
- return port;
3837
- }, normalizeAbsoluteIosDeviceHost = (value) => {
3838
- const normalized = value.trim();
3839
- if (!normalized || normalized.length > 253 || /[\0\s/?#]/u.test(normalized))
3840
- throw new TypeError("Physical iOS development requires a valid LAN host.");
3841
- return normalized;
3842
- }, normalizeAbsoluteIosDeviceIdentifier = (value) => {
3843
- const normalized = value.trim();
3844
- if (!normalized || normalized.length > 256 || /[\0\r\n]/u.test(normalized))
3845
- throw new TypeError("--ios-device requires a valid Xcode device identifier or name.");
3846
- return normalized;
3847
- }, urlForHost = (protocol, host2, port) => {
3848
- const url = new URL(`${protocol}://localhost:${port}`);
3849
- const normalizedHost = normalizeAbsoluteIosDeviceHost(host2);
3850
- url.hostname = isIP(normalizedHost) === 6 ? `[${normalizedHost}]` : normalizedHost;
3851
- return url;
3852
- }, startAbsoluteIosCaEnrollmentServer = async (options) => {
3853
- const certificate = new X509Certificate2(await readFile2(options.certificateAuthorityPath));
3854
- const certificateBytes = certificate.raw;
3855
- const token = randomUUID().replaceAll("-", "");
3856
- const certificatePath = `/${token}/absolutejs-development-ca.cer`;
3857
- const server = createServer2((request, response) => {
3858
- if (request.method !== "GET" || request.url !== certificatePath) {
3859
- response.writeHead(404, {
3860
- "Cache-Control": "no-store",
3861
- "Content-Type": "text/plain; charset=utf-8"
3862
- });
3863
- response.end("Not found.");
3864
- return;
3865
- }
3866
- response.writeHead(200, {
3867
- "Cache-Control": "no-store",
3868
- "Content-Disposition": 'attachment; filename="absolutejs-development-ca.cer"',
3869
- "Content-Length": String(certificateBytes.byteLength),
3870
- "Content-Type": "application/x-x509-ca-cert",
3871
- "X-Content-Type-Options": "nosniff"
3872
- });
3873
- response.end(certificateBytes);
3874
- });
3875
- const port = await findEphemeralPort();
3876
- await listen(server, port);
3877
- const url = urlForHost("http", options.displayHost, port);
3878
- url.pathname = certificatePath;
3879
- return {
3880
- url: url.href,
3881
- close: () => closeServer(server)
3882
- };
3883
- };
3884
- var init_iosPhysicalDeviceTransport = () => {};
3885
-
3886
3821
  // src/cli/utils.ts
3887
3822
  var {$: $2 } = globalThis.Bun;
3888
3823
  import { execSync } from "child_process";
3889
3824
  import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
3890
- import { createServer as createServer3 } from "net";
3825
+ import { createServer as createServer2 } from "net";
3891
3826
  import { resolve as resolve6 } from "path";
3892
3827
  var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backend/server.ts", isWSLEnvironment = () => {
3893
3828
  try {
@@ -3901,7 +3836,7 @@ var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backe
3901
3836
  process.kill(pid, "SIGTERM");
3902
3837
  } catch {}
3903
3838
  }, findFreePort = () => new Promise((_resolve, reject) => {
3904
- const server = createServer3();
3839
+ const server = createServer2();
3905
3840
  server.unref();
3906
3841
  server.once("error", reject);
3907
3842
  server.listen(0, "127.0.0.1", () => {
@@ -4023,9 +3958,9 @@ var init_utils = __esm(() => {
4023
3958
  });
4024
3959
 
4025
3960
  // src/mobile/emulatorDoctor.ts
4026
- import { access as access3 } from "fs/promises";
3961
+ import { access as access2 } from "fs/promises";
4027
3962
  import { homedir as homedir3 } from "os";
4028
- import { join as join11 } from "path";
3963
+ import { join as join10 } from "path";
4029
3964
  var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command) => {
4030
3965
  try {
4031
3966
  const result = Bun.spawnSync(command, {
@@ -4070,18 +4005,18 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
4070
4005
  }
4071
4006
  }, absoluteManagedAndroidSdkRoot = (host2, env = process.env) => {
4072
4007
  if (host2 === "windows") {
4073
- return join11(env.LOCALAPPDATA ?? join11(homedir3(), "AppData", "Local"), "AbsoluteJS", "Android", "Sdk");
4008
+ return join10(env.LOCALAPPDATA ?? join10(homedir3(), "AppData", "Local"), "AbsoluteJS", "Android", "Sdk");
4074
4009
  }
4075
4010
  if (host2 === "wsl") {
4076
4011
  const localAppData = windowsLocalAppDataFromWsl();
4077
4012
  if (localAppData) {
4078
- return join11(localAppData, "AbsoluteJS", "Android", "Sdk");
4013
+ return join10(localAppData, "AbsoluteJS", "Android", "Sdk");
4079
4014
  }
4080
4015
  }
4081
- return join11(homedir3(), ".absolutejs", "android-sdk");
4016
+ return join10(homedir3(), ".absolutejs", "android-sdk");
4082
4017
  }, pathExists = async (path) => {
4083
4018
  try {
4084
- await access3(path);
4019
+ await access2(path);
4085
4020
  return true;
4086
4021
  } catch {
4087
4022
  return false;
@@ -4131,7 +4066,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
4131
4066
  const capture = input.capture ?? captureCommand;
4132
4067
  const androidRoot = input.androidRoot === null ? undefined : input.androidRoot ?? env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2, env);
4133
4068
  const windowsAndroidTools = host2 === "windows" || host2 === "wsl";
4134
- const android = (segments) => androidRoot ? join11(androidRoot, ...segments) : undefined;
4069
+ const android = (segments) => androidRoot ? join10(androidRoot, ...segments) : undefined;
4135
4070
  const paths = (values) => values.filter((value) => Boolean(value));
4136
4071
  const adb = await findExecutable("adb", paths([
4137
4072
  android(["platform-tools", windowsAndroidTools ? "adb.exe" : "adb"])
@@ -4236,6 +4171,97 @@ var init_emulatorDoctor = __esm(() => {
4236
4171
  init_utils();
4237
4172
  });
4238
4173
 
4174
+ // src/mobile/iosPhysicalDeviceTransport.ts
4175
+ import { randomUUID, X509Certificate as X509Certificate2 } from "crypto";
4176
+ import { createServer as createServer3 } from "http";
4177
+ import {
4178
+ connect as connectTcp,
4179
+ createServer as createTcpServer,
4180
+ isIP
4181
+ } from "net";
4182
+ import { readFile as readFile2 } from "fs/promises";
4183
+ var closeServer = (server) => new Promise((resolve7, reject) => {
4184
+ server.close((error) => {
4185
+ if (error)
4186
+ reject(error);
4187
+ else
4188
+ resolve7();
4189
+ });
4190
+ }), listen = (server, port) => new Promise((resolve7, reject) => {
4191
+ server.once("error", reject);
4192
+ server.listen(port, "0.0.0.0", () => {
4193
+ server.off("error", reject);
4194
+ const address = server.address();
4195
+ if (!address || typeof address === "string") {
4196
+ reject(new Error("Could not determine the iOS device helper port."));
4197
+ return;
4198
+ }
4199
+ resolve7(address.port);
4200
+ });
4201
+ }), findEphemeralPort = async () => {
4202
+ const probe = createTcpServer();
4203
+ const port = await new Promise((resolve7, reject) => {
4204
+ probe.once("error", reject);
4205
+ probe.listen(0, "127.0.0.1", () => {
4206
+ const address = probe.address();
4207
+ if (!address || typeof address === "string") {
4208
+ reject(new Error("Could not allocate the iOS CA enrollment port."));
4209
+ return;
4210
+ }
4211
+ resolve7(address.port);
4212
+ });
4213
+ });
4214
+ await closeServer(probe);
4215
+ return port;
4216
+ }, normalizeAbsoluteIosDeviceHost = (value) => {
4217
+ const normalized = value.trim();
4218
+ if (!normalized || normalized.length > 253 || /[\0\s/?#]/u.test(normalized))
4219
+ throw new TypeError("Physical iOS development requires a valid LAN host.");
4220
+ return normalized;
4221
+ }, normalizeAbsoluteIosDeviceIdentifier = (value) => {
4222
+ const normalized = value.trim();
4223
+ if (!normalized || normalized.length > 256 || /[\0\r\n]/u.test(normalized))
4224
+ throw new TypeError("--ios-device requires a valid Xcode device identifier or name.");
4225
+ return normalized;
4226
+ }, urlForHost = (protocol, host2, port) => {
4227
+ const url = new URL(`${protocol}://localhost:${port}`);
4228
+ const normalizedHost = normalizeAbsoluteIosDeviceHost(host2);
4229
+ url.hostname = isIP(normalizedHost) === 6 ? `[${normalizedHost}]` : normalizedHost;
4230
+ return url;
4231
+ }, startAbsoluteIosCaEnrollmentServer = async (options) => {
4232
+ const certificate = new X509Certificate2(await readFile2(options.certificateAuthorityPath));
4233
+ const certificateBytes = certificate.raw;
4234
+ const token = randomUUID().replaceAll("-", "");
4235
+ const certificatePath = `/${token}/absolutejs-development-ca.cer`;
4236
+ const server = createServer3((request, response) => {
4237
+ if (request.method !== "GET" || request.url !== certificatePath) {
4238
+ response.writeHead(404, {
4239
+ "Cache-Control": "no-store",
4240
+ "Content-Type": "text/plain; charset=utf-8"
4241
+ });
4242
+ response.end("Not found.");
4243
+ return;
4244
+ }
4245
+ response.writeHead(200, {
4246
+ "Cache-Control": "no-store",
4247
+ "Content-Disposition": 'attachment; filename="absolutejs-development-ca.cer"',
4248
+ "Content-Length": String(certificateBytes.byteLength),
4249
+ "Content-Type": "application/x-x509-ca-cert",
4250
+ "X-Content-Type-Options": "nosniff"
4251
+ });
4252
+ response.end(certificateBytes);
4253
+ });
4254
+ const port = await findEphemeralPort();
4255
+ await listen(server, port);
4256
+ const url = urlForHost("http", options.displayHost, port);
4257
+ url.pathname = certificatePath;
4258
+ return {
4259
+ url: url.href,
4260
+ close: () => closeServer(server)
4261
+ };
4262
+ };
4263
+ var init_iosPhysicalDeviceTransport = () => {};
4264
+
4239
4265
  // src/mobile/capacitorProject.ts
4240
4266
  import { access as access4, readFile as readFile3, rename as rename2, writeFile as writeFile2 } from "fs/promises";
4241
4267
  import { relative as relative3, resolve as resolve7 } from "path";
@@ -4340,7 +4366,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4340
4366
  forwardCommandOutput(subprocess.stderr, process.stderr)
4341
4367
  ]);
4342
4368
  return exitCode;
4343
- }, captureCommand2 = (command, options = {}) => {
4369
+ }, captureCommand3 = (command, options = {}) => {
4344
4370
  try {
4345
4371
  const result = Bun.spawnSync(command, {
4346
4372
  cwd: options.cwd,
@@ -4776,7 +4802,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4776
4802
  return poll();
4777
4803
  };
4778
4804
  return poll();
4779
- }, windowsPathFromWsl = (path, capture) => {
4805
+ }, windowsPathFromWsl2 = (path, capture) => {
4780
4806
  const result = capture(["wslpath", "-w", path]);
4781
4807
  if (result.exitCode !== 0 || !result.stdout.trim()) {
4782
4808
  throw new Error(`Could not translate WSL path for Windows: ${path}`);
@@ -4791,7 +4817,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4791
4817
  const source = resolve8(project.nativeDirectory, sourcePath);
4792
4818
  dependencies.push({
4793
4819
  name,
4794
- windowsSource: windowsPathFromWsl(source, capture)
4820
+ windowsSource: windowsPathFromWsl2(source, capture)
4795
4821
  });
4796
4822
  return `project('${projectName}').projectDir = new File('./.absolutejs-dependencies/${name}')`;
4797
4823
  });
@@ -4845,16 +4871,16 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4845
4871
  return join12(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
4846
4872
  }, buildAbsoluteAndroidGradleArtifact = async (options) => {
4847
4873
  const { project, task } = options;
4848
- const capture = options.capture ?? captureCommand2;
4874
+ const capture = options.capture ?? captureCommand3;
4849
4875
  const run = options.run ?? runCommand;
4850
4876
  const env = options.env ?? process.env;
4851
4877
  const gradleArguments = options.gradleArguments ?? [];
4852
4878
  if (project.host === "wsl") {
4853
- const windowsSource = windowsPathFromWsl(project.nativeDirectory, capture);
4879
+ const windowsSource = windowsPathFromWsl2(project.nativeDirectory, capture);
4854
4880
  const buildId = Bun.hash(project.projectRoot).toString(HASH_RADIX);
4855
4881
  const managedBuildDirectory = resolve8(project.androidRoot, "..", "..", "Builds", `${project.config.appId}-${buildId}`);
4856
- const windowsDirectory = windowsPathFromWsl(managedBuildDirectory, capture);
4857
- const windowsAndroidRoot = windowsPathFromWsl(project.androidRoot, capture);
4882
+ const windowsDirectory = windowsPathFromWsl2(managedBuildDirectory, capture);
4883
+ const windowsAndroidRoot = windowsPathFromWsl2(project.androidRoot, capture);
4858
4884
  const { dependencies, rewrittenSettings } = await mirroredCapacitorDependencies(project, capture);
4859
4885
  await requireSuccess([
4860
4886
  "powershell.exe",
@@ -5126,12 +5152,12 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
5126
5152
  phaseStartedAt = now;
5127
5153
  options.onStateChange?.(next);
5128
5154
  };
5129
- const capture = options.capture ?? captureCommand2;
5155
+ const capture = options.capture ?? captureCommand3;
5130
5156
  const run = options.run ?? runCommand;
5131
5157
  const sleep = options.sleep ?? Bun.sleep;
5132
5158
  const spawn2 = options.spawn ?? spawnCommand;
5133
5159
  const log = options.log ?? console.log;
5134
- const androidHome = project.host === "wsl" ? windowsPathFromWsl(project.androidRoot, capture) : project.androidRoot;
5160
+ const androidHome = project.host === "wsl" ? windowsPathFromWsl2(project.androidRoot, capture) : project.androidRoot;
5135
5161
  const env = {
5136
5162
  ...process.env,
5137
5163
  ANDROID_HOME: androidHome
@@ -6880,7 +6906,7 @@ import {
6880
6906
  join as join16,
6881
6907
  posix,
6882
6908
  relative as relative7,
6883
- resolve as resolvePath,
6909
+ resolve as resolvePath2,
6884
6910
  sep as sep5
6885
6911
  } from "path";
6886
6912
  var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, REMOTE_RELEASE_LEASE_HEARTBEAT_MS = 15000, REMOTE_RELEASE_LEASE_STALE_SECONDS = 120, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join16(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
@@ -7054,16 +7080,16 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
7054
7080
  }
7055
7081
  await saveStore(store, profilePath);
7056
7082
  return true;
7057
- }, projectIdentity = (projectRoot, appId) => createHash8("sha256").update(`${resolvePath(projectRoot)}\x00${appId}`).digest("hex").slice(0, 20), createAbsoluteRemoteExpoIosDevProject = (config, projectRoot, profile) => {
7083
+ }, projectIdentity = (projectRoot, appId) => createHash8("sha256").update(`${resolvePath2(projectRoot)}\x00${appId}`).digest("hex").slice(0, 20), createAbsoluteRemoteExpoIosDevProject = (config, projectRoot, profile) => {
7058
7084
  if (config.engine !== "expo")
7059
7085
  throw new TypeError("Remote Expo execution requires mobile.engine: expo.");
7060
7086
  return createAbsoluteRemoteIosDevProject(config, projectRoot, profile);
7061
7087
  }, createAbsoluteRemoteIosDevProject = (config, projectRoot, profile) => ({
7062
- cap: join16(resolvePath(projectRoot), "node_modules", ".bin", "cap"),
7088
+ cap: join16(resolvePath2(projectRoot), "node_modules", ".bin", "cap"),
7063
7089
  config,
7064
7090
  nativeDirectory: join16(config.nativeProjectDirectory, "ios"),
7065
7091
  profile,
7066
- projectRoot: resolvePath(projectRoot),
7092
+ projectRoot: resolvePath2(projectRoot),
7067
7093
  remote: true,
7068
7094
  remoteProjectRoot: posix.join(profile.workspaceRoot, "projects", projectIdentity(projectRoot, config.appId), "current"),
7069
7095
  xcodebuild: "remote:xcodebuild",
@@ -7230,7 +7256,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
7230
7256
  const source = await sourceCandidates.reduce(async (found, candidate) => await found ?? (await Bun.file(candidate).exists() ? candidate : undefined), Promise.resolve(undefined));
7231
7257
  if (!source)
7232
7258
  throw new Error("The AbsoluteJS installation does not contain its remote Mac agent artifact.");
7233
- const outdir = join16(resolvePath(projectRoot), ".absolutejs", "mobile", "remote-agent");
7259
+ const outdir = join16(resolvePath2(projectRoot), ".absolutejs", "mobile", "remote-agent");
7234
7260
  await mkdir6(outdir, { recursive: true });
7235
7261
  const result = await Bun.build({
7236
7262
  entrypoints: [source],
@@ -7918,7 +7944,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
7918
7944
  },
7919
7945
  screenshot: async (destination) => {
7920
7946
  const result = await request("screenshot");
7921
- const target = resolvePath(options.project.projectRoot, destination);
7947
+ const target = resolvePath2(options.project.projectRoot, destination);
7922
7948
  const targetRelative = relative7(options.project.projectRoot, target);
7923
7949
  if (targetRelative.startsWith("..") || isAbsolute4(targetRelative))
7924
7950
  throw new Error("iOS screenshot must remain inside the project.");
@@ -10299,7 +10325,7 @@ import {
10299
10325
  rm as rm8,
10300
10326
  writeFile as writeFile9
10301
10327
  } from "fs/promises";
10302
- import { dirname as dirname13, join as join23, resolve as resolvePath3 } from "path";
10328
+ import { dirname as dirname13, join as join23, resolve as resolvePath4 } from "path";
10303
10329
  var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "current.json", BUNDLES_DIRECTORY = "bundles", ARTIFACT_FILE = "artifact.json", BUNDLE_ID_PATTERN, isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code, bundleIdFor = (currentReleaseId, releases) => {
10304
10330
  const identity = JSON.stringify({
10305
10331
  currentReleaseId,
@@ -10374,7 +10400,7 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
10374
10400
  }
10375
10401
  return release;
10376
10402
  });
10377
- const root = resolvePath3(input.root);
10403
+ const root = resolvePath4(input.root);
10378
10404
  const bundlesRoot = join23(root, BUNDLES_DIRECTORY);
10379
10405
  await mkdir10(bundlesRoot, { recursive: true });
10380
10406
  const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
@@ -10392,7 +10418,7 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
10392
10418
  await rename8(temporaryPointerPath, pointerPath);
10393
10419
  return index;
10394
10420
  }, readAbsoluteMobileMaterializedReleases = async (root) => {
10395
- const resolvedRoot = resolvePath3(root);
10421
+ const resolvedRoot = resolvePath4(root);
10396
10422
  try {
10397
10423
  const serialized = await readFile11(join23(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
10398
10424
  const parsed = JSON.parse(serialized);
@@ -21703,7 +21729,7 @@ class CdpConnection {
21703
21729
  pending.resolve(message.result);
21704
21730
  }
21705
21731
  }
21706
- var CDP_COMMAND_TIMEOUT_MS = 1e4, WEBVIEW_ATTACH_TIMEOUT_MS = 30000, WEBVIEW_POLL_MS = 200, captureCommand3 = (command) => {
21732
+ var CDP_COMMAND_TIMEOUT_MS = 1e4, WEBVIEW_ATTACH_TIMEOUT_MS = 30000, WEBVIEW_POLL_MS = 200, captureCommand4 = (command) => {
21707
21733
  const result = Bun.spawnSync(command, {
21708
21734
  stderr: "pipe",
21709
21735
  stdin: "ignore",
@@ -21903,7 +21929,7 @@ var CDP_COMMAND_TIMEOUT_MS = 1e4, WEBVIEW_ATTACH_TIMEOUT_MS = 30000, WEBVIEW_POL
21903
21929
  return;
21904
21930
  });
21905
21931
  }, Promise.resolve(undefined)), attachAbsoluteAndroidWebView = async (options) => {
21906
- const capture = options.capture ?? captureCommand3;
21932
+ const capture = options.capture ?? captureCommand4;
21907
21933
  const fetcher = options.fetch ?? fetch;
21908
21934
  const deadline = Date.now() + (options.timeoutMs ?? WEBVIEW_ATTACH_TIMEOUT_MS);
21909
21935
  let lastError;
@@ -42516,7 +42542,7 @@ AbsoluteJS can configure ${installPlatform} emulation:`);
42516
42542
  console.log(`
42517
42543
  Emulator setup verification:`);
42518
42544
  printDoctorChecks(result.checks.filter((check2) => check2.platform === "host" || check2.platform === installPlatform));
42519
- }, captureCommand4 = (command) => {
42545
+ }, captureCommand5 = (command) => {
42520
42546
  const result = Bun.spawnSync(command, {
42521
42547
  stderr: "pipe",
42522
42548
  stdin: "ignore",
@@ -42562,7 +42588,7 @@ Emulator setup verification:`);
42562
42588
  }
42563
42589
  return adb;
42564
42590
  }, selectAndroidSerial = (adb, explicitSerial) => {
42565
- const devices = captureCommand4([adb, "devices"]);
42591
+ const devices = captureCommand5([adb, "devices"]);
42566
42592
  if (devices.exitCode !== 0) {
42567
42593
  throw new Error(`Could not list Android targets: ${devices.stderr.trim() || devices.stdout.trim()}`);
42568
42594
  }
@@ -42832,10 +42858,10 @@ Emulator setup verification:`);
42832
42858
  remote: false,
42833
42859
  xcodeVersion: requireCapturedCommand(["/usr/bin/xcodebuild", "-version"], "Xcode version inspection").stdout.trim(),
42834
42860
  xcrun: "/usr/bin/xcrun",
42835
- capture: async (command) => captureCommand4(command)
42861
+ capture: async (command) => captureCommand5(command)
42836
42862
  };
42837
42863
  }, selectIosSimulator = (xcrun, explicitUdid) => {
42838
- const result = captureCommand4([
42864
+ const result = captureCommand5([
42839
42865
  xcrun,
42840
42866
  "simctl",
42841
42867
  "list",
@@ -42857,14 +42883,14 @@ Emulator setup verification:`);
42857
42883
  throw new TypeError("No ready AbsoluteJS iOS simulator was found. Start `bun dev` and wait for the iOS target to become ready.");
42858
42884
  return selected;
42859
42885
  }, requireCapturedCommand = (command, label) => {
42860
- const result = captureCommand4(command);
42886
+ const result = captureCommand5(command);
42861
42887
  if (result.exitCode !== 0)
42862
42888
  throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
42863
42889
  return result;
42864
42890
  }, writeIosFailureArtifacts = async (options) => {
42865
42891
  await mkdir21(options.artifactRoot, { recursive: true });
42866
42892
  const screenshot = join61(options.artifactRoot, "ios-failure.png");
42867
- const screenshotResult = captureCommand4([
42893
+ const screenshotResult = captureCommand5([
42868
42894
  options.xcrun,
42869
42895
  "simctl",
42870
42896
  "io",
@@ -43806,7 +43832,7 @@ import {
43806
43832
  writeFileSync as writeFileSync4
43807
43833
  } from "fs";
43808
43834
  import { tmpdir as tmpdir2 } from "os";
43809
- import { join as join18, resolve as resolvePath2 } from "path";
43835
+ import { join as join18, resolve as resolvePath3 } from "path";
43810
43836
 
43811
43837
  // src/dev/tunnel/client.ts
43812
43838
  var RECONNECT_DELAY_MS = 2000;
@@ -44313,14 +44339,164 @@ init_config();
44313
44339
  init_expoProject();
44314
44340
 
44315
44341
  // src/mobile/expoDevController.ts
44342
+ init_emulatorDoctor();
44316
44343
  init_iosPhysicalDeviceTransport();
44317
44344
  import { spawn } from "child_process";
44318
- import { access as access2 } from "fs/promises";
44319
- import { join as join10 } from "path";
44345
+ import { access as access3 } from "fs/promises";
44346
+ import { join as join11, resolve as resolvePath } from "path";
44320
44347
  var METRO_READY_TIMEOUT_MS = 60000;
44321
44348
  var PROCESS_CLOSE_TIMEOUT_MS = 2000;
44349
+ var preserveWindowsSubstRealpaths = `
44350
+ const fs = require('node:fs');
44351
+ const path = require('node:path');
44352
+ const physicalRoot = process.env.ABSOLUTE_EXPO_PHYSICAL_ROOT;
44353
+ const mappedRoot = process.env.ABSOLUTE_EXPO_MAPPED_ROOT;
44354
+ const remap = (value) => {
44355
+ if (typeof value !== 'string' || !physicalRoot || !mappedRoot) return value;
44356
+ const normalizedValue = path.resolve(value);
44357
+ const normalizedRoot = path.resolve(physicalRoot);
44358
+ if (normalizedValue.toLowerCase() === normalizedRoot.toLowerCase()) return mappedRoot;
44359
+ const prefix = normalizedRoot.endsWith(path.sep) ? normalizedRoot : normalizedRoot + path.sep;
44360
+ return normalizedValue.toLowerCase().startsWith(prefix.toLowerCase())
44361
+ ? path.join(mappedRoot, normalizedValue.slice(prefix.length))
44362
+ : value;
44363
+ };
44364
+ const realpath = fs.realpath.bind(fs);
44365
+ const realpathSync = fs.realpathSync.bind(fs);
44366
+ const realpathNative = fs.realpathSync.native?.bind(fs.realpathSync);
44367
+ fs.realpath = (value, options, callback) => {
44368
+ const done = typeof options === 'function' ? options : callback;
44369
+ const encoding = typeof options === 'function' ? undefined : options;
44370
+ return realpath(value, encoding, (error, result) => done(error, remap(result)));
44371
+ };
44372
+ fs.realpathSync = (value, options) => remap(realpathSync(value, options));
44373
+ if (realpathNative) fs.realpathSync.native = (value, options) => remap(realpathNative(value, options));
44374
+ const promiseRealpath = fs.promises.realpath.bind(fs.promises);
44375
+ fs.promises.realpath = async (value, options) => remap(await promiseRealpath(value, options));
44376
+ `;
44377
+ var expoDevelopmentClientScheme = (appId) => `exp+${appId.toLowerCase().replaceAll(/[^a-z0-9]+/gu, "-")}`;
44378
+ var expoDevelopmentClientUrl = (appId, host2, port) => `${expoDevelopmentClientScheme(appId)}://expo-development-client/?url=${encodeURIComponent(`http://${host2}:${port}`)}`;
44379
+ var captureCommand2 = (command) => {
44380
+ const result = Bun.spawnSync(command, {
44381
+ stderr: "ignore",
44382
+ stdout: "pipe"
44383
+ });
44384
+ return { exitCode: result.exitCode, stdout: result.stdout.toString() };
44385
+ };
44386
+ var windowsPathFromWsl = (path, capture) => {
44387
+ const result = capture(["wslpath", "-w", path]);
44388
+ if (result.exitCode !== 0 || !result.stdout.trim()) {
44389
+ throw new Error(`Could not translate WSL path for Windows: ${path}`);
44390
+ }
44391
+ return result.stdout.trim();
44392
+ };
44393
+ var connectLocalExpoAndroid = (adb, appId, metroPort, capture, preferredDevice) => {
44394
+ const devices = capture([adb, "devices"]);
44395
+ if (devices.exitCode !== 0)
44396
+ throw new Error("Could not inspect devices after the Expo Android build.");
44397
+ const ready = devices.stdout.split(/\r?\n/u).flatMap((line) => {
44398
+ const match = /^(\S+)\s+device$/u.exec(line.trim());
44399
+ return match?.[1] ? [match[1]] : [];
44400
+ });
44401
+ const serial = preferredDevice ? ready.find((value) => value === preferredDevice) : ready.find((value) => value.startsWith("emulator-")) ?? ready[0];
44402
+ if (!serial)
44403
+ throw new Error(preferredDevice ? "The selected Expo Android device is not connected after build." : "Expo Android build completed but no ready device was found.");
44404
+ const run = (args, message) => {
44405
+ if (capture([adb, "-s", serial, ...args]).exitCode !== 0)
44406
+ throw new Error(message);
44407
+ };
44408
+ run(["reverse", `tcp:${metroPort}`, `tcp:${metroPort}`], "Expo Android Metro forwarding failed.");
44409
+ run(["shell", "am", "force-stop", appId], "Expo Android development-client reset failed.");
44410
+ run([
44411
+ "shell",
44412
+ "am",
44413
+ "start",
44414
+ "-W",
44415
+ "-a",
44416
+ "android.intent.action.VIEW",
44417
+ "-d",
44418
+ expoDevelopmentClientUrl(appId, "localhost", metroPort),
44419
+ appId
44420
+ ], "Expo Android development-client launch failed.");
44421
+ return serial;
44422
+ };
44423
+ var encodedWindowsExpoAndroidCommand = (project, androidRoot, appId, args, capture, metroPort, preferredDevice) => {
44424
+ const windowsSource = windowsPathFromWsl(project, capture);
44425
+ const buildId = Bun.hash(resolvePath(project)).toString(16);
44426
+ const buildDirectory = resolvePath(androidRoot, "..", "..", "ExpoBuilds", buildId.slice(0, 10));
44427
+ const windowsDirectory = windowsPathFromWsl(buildDirectory, capture);
44428
+ const windowsAndroidRoot = windowsPathFromWsl(androidRoot, capture);
44429
+ const encode = (value) => Buffer.from(value, "utf8").toString("base64");
44430
+ const developmentScheme = expoDevelopmentClientScheme(appId);
44431
+ const realpathHook = encode(preserveWindowsSubstRealpaths);
44432
+ const mappedBuild = [
44433
+ "$mappedProject = Join-Path ($drive + '\\') (Split-Path -Leaf $directory)",
44434
+ "Set-Location $mappedProject",
44435
+ "$hook = Join-Path $mappedProject '.absolutejs-preserve-subst.cjs'",
44436
+ `[IO.File]::WriteAllText($hook, [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${realpathHook}')))`,
44437
+ "$env:ABSOLUTE_EXPO_PHYSICAL_ROOT = $directory",
44438
+ "$env:ABSOLUTE_EXPO_MAPPED_ROOT = $mappedProject",
44439
+ '$env:NODE_OPTIONS = "--require=$hook"',
44440
+ "$autolinkingCache = Join-Path $mappedProject 'android\\build\\generated\\autolinking'",
44441
+ "if ([IO.Directory]::Exists($autolinkingCache)) { [IO.Directory]::Delete($autolinkingCache, $true) }",
44442
+ "$expo = Join-Path $mappedProject 'node_modules\\.bin\\expo.exe'",
44443
+ "& $expo @expoArguments",
44444
+ "if ($LASTEXITCODE -ne 0) { throw 'Expo Android native build failed.' }",
44445
+ "$adb = Join-Path $androidHome 'platform-tools\\adb.exe'",
44446
+ "$readyDevices = @(& $adb devices) | Where-Object { $_ -match '\\tdevice$' } | ForEach-Object { ($_ -split '\\s+')[0] }",
44447
+ ...preferredDevice ? [
44448
+ `$serial = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encode(preferredDevice)}'))`,
44449
+ "if ($readyDevices -notcontains $serial) { throw 'The selected Expo Android device is not connected after build.' }"
44450
+ ] : [
44451
+ "$serial = $readyDevices | Where-Object { $_ -like 'emulator-*' } | Select-Object -First 1",
44452
+ "if (-not $serial) { $serial = $readyDevices | Select-Object -First 1 }"
44453
+ ],
44454
+ "if (-not $serial) { throw 'Expo Android build completed but no ready device was found.' }",
44455
+ `& $adb -s $serial reverse 'tcp:${metroPort}' 'tcp:${metroPort}'`,
44456
+ "if ($LASTEXITCODE -ne 0) { throw 'Expo Android Metro forwarding failed.' }",
44457
+ `& $adb -s $serial shell am force-stop '${appId}'`,
44458
+ "if ($LASTEXITCODE -ne 0) { throw 'Expo Android development-client reset failed.' }",
44459
+ `$developmentUrl = '${developmentScheme}://expo-development-client/?url=' + [Uri]::EscapeDataString('http://localhost:${metroPort}')`,
44460
+ `& $adb -s $serial shell am start -W -a android.intent.action.VIEW -d $developmentUrl '${appId}'`,
44461
+ "if ($LASTEXITCODE -ne 0) { throw 'Expo Android development-client launch failed.' }"
44462
+ ].join("; ");
44463
+ const source = [
44464
+ "$ErrorActionPreference = 'Stop'",
44465
+ "$ProgressPreference = 'SilentlyContinue'",
44466
+ `$source = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encode(windowsSource)}'))`,
44467
+ `$directory = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encode(windowsDirectory)}'))`,
44468
+ `$androidHome = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encode(windowsAndroidRoot)}'))`,
44469
+ `$expoArguments = @([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encode(JSON.stringify(args))}')) | ConvertFrom-Json)`,
44470
+ "$env:ANDROID_HOME = $androidHome",
44471
+ "$env:ANDROID_SDK_ROOT = $androidHome",
44472
+ "$env:NODE_ENV = 'development'",
44473
+ ...preferredDevice ? [] : ["$env:ORG_GRADLE_PROJECT_reactNativeArchitectures = 'x86_64'"],
44474
+ "New-Item -ItemType Directory -Force -Path $directory | Out-Null",
44475
+ "& robocopy.exe $source $directory /MIR /XD node_modules .expo .git .gradle .cxx .kotlin build /XF bun.lock bun.lockb .absolutejs-preserve-subst.cjs /NFL /NDL /NJH /NJS /NP",
44476
+ "$copyExit = $LASTEXITCODE",
44477
+ "if ($copyExit -ge 8) { exit $copyExit }",
44478
+ "Set-Location $directory",
44479
+ "$bun = (Get-Command bun.exe -ErrorAction Stop).Source",
44480
+ "& $bun install",
44481
+ "if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }",
44482
+ "$drive = @('Z:', 'Y:', 'X:', 'W:', 'V:', 'U:', 'T:') | Where-Object { -not (Test-Path ($_ + '\\')) } | Select-Object -First 1",
44483
+ "if (-not $drive) { throw 'AbsoluteJS could not reserve a temporary drive letter for the Expo Android build.' }",
44484
+ "$mirrorRoot = Split-Path -Parent $directory",
44485
+ "& subst.exe $drive $mirrorRoot",
44486
+ "if ($LASTEXITCODE -ne 0) { throw 'AbsoluteJS could not create the short Expo Android build path.' }",
44487
+ `try { ${mappedBuild} } finally { Set-Location ($env:SystemDrive + '\\'); & subst.exe $drive /D | Out-Null }`,
44488
+ "exit 0"
44489
+ ].join("; ");
44490
+ return [
44491
+ "powershell.exe",
44492
+ "-NoProfile",
44493
+ "-EncodedCommand",
44494
+ Buffer.from(source, "utf16le").toString("base64")
44495
+ ];
44496
+ };
44322
44497
  var commandEnvironment = (options) => ({
44323
44498
  ABSOLUTE_EXPO_DEVELOPMENT: "1",
44499
+ NODE_ENV: "development",
44324
44500
  ...options.certificateAuthorityPath ? {
44325
44501
  ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH: options.certificateAuthorityPath
44326
44502
  } : {},
@@ -44329,9 +44505,9 @@ var commandEnvironment = (options) => ({
44329
44505
  ...options.metroHost ? { REACT_NATIVE_PACKAGER_HOSTNAME: options.metroHost } : {}
44330
44506
  });
44331
44507
  var absoluteExpoExecutable = async (project) => {
44332
- const executable = join10(project, "node_modules", ".bin", "expo");
44508
+ const executable = join11(project, "node_modules", ".bin", "expo");
44333
44509
  try {
44334
- await access2(executable);
44510
+ await access3(executable);
44335
44511
  return executable;
44336
44512
  } catch {
44337
44513
  throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
@@ -44375,8 +44551,6 @@ var planAbsoluteExpoDevSession = (config, options) => {
44375
44551
  args: [
44376
44552
  `run:${platform2}`,
44377
44553
  "--no-bundler",
44378
- "--port",
44379
- String(options.metroPort),
44380
44554
  ...device ? ["--device", device] : []
44381
44555
  ],
44382
44556
  env,
@@ -44425,18 +44599,18 @@ var stopProcess = async (process2) => {
44425
44599
  return;
44426
44600
  process2.kill("SIGTERM");
44427
44601
  await Promise.race([
44428
- new Promise((resolve6) => process2.once("exit", () => resolve6())),
44429
- new Promise((resolve6) => setTimeout(resolve6, PROCESS_CLOSE_TIMEOUT_MS))
44602
+ new Promise((resolve7) => process2.once("exit", () => resolve7())),
44603
+ new Promise((resolve7) => setTimeout(resolve7, PROCESS_CLOSE_TIMEOUT_MS))
44430
44604
  ]);
44431
44605
  if (process2.exitCode === null)
44432
44606
  process2.kill("SIGKILL");
44433
44607
  };
44434
- var waitForExit = (process2) => new Promise((resolve6) => {
44608
+ var waitForExit = (process2) => new Promise((resolve7) => {
44435
44609
  if (process2.exitCode !== null) {
44436
- resolve6(process2.exitCode);
44610
+ resolve7(process2.exitCode);
44437
44611
  return;
44438
44612
  }
44439
- process2.once("exit", (code) => resolve6(code ?? 1));
44613
+ process2.once("exit", (code) => resolve7(code ?? 1));
44440
44614
  });
44441
44615
  var runUtilityCommand = async (run, command, args, options) => {
44442
44616
  const child = run(command, args, {
@@ -44457,6 +44631,8 @@ var startAbsoluteExpoDevSession = async (options) => {
44457
44631
  const plan = planAbsoluteExpoDevSession(options.config, options);
44458
44632
  const executable = options.executable ?? await absoluteExpoExecutable(plan.project);
44459
44633
  const run = options.spawnProcess ?? spawn;
44634
+ const host2 = options.host ?? detectAbsoluteMobileHost();
44635
+ const capture = options.capture ?? captureCommand2;
44460
44636
  const log = options.log ?? (() => {
44461
44637
  return;
44462
44638
  });
@@ -44516,9 +44692,9 @@ var startAbsoluteExpoDevSession = async (options) => {
44516
44692
  }) : undefined;
44517
44693
  let metroReady = false;
44518
44694
  let resolveMetro;
44519
- const metroPromise = new Promise((resolve6, reject) => {
44695
+ const metroPromise = new Promise((resolve7, reject) => {
44520
44696
  if (!metro) {
44521
- resolve6();
44697
+ resolve7();
44522
44698
  return;
44523
44699
  }
44524
44700
  const timeout = setTimeout(() => {
@@ -44526,7 +44702,7 @@ var startAbsoluteExpoDevSession = async (options) => {
44526
44702
  }, METRO_READY_TIMEOUT_MS);
44527
44703
  resolveMetro = () => {
44528
44704
  clearTimeout(timeout);
44529
- resolve6();
44705
+ resolve7();
44530
44706
  };
44531
44707
  metro.once("exit", (code) => {
44532
44708
  if (!metroReady) {
@@ -44558,7 +44734,14 @@ var startAbsoluteExpoDevSession = async (options) => {
44558
44734
  const state = platform2 === "android" ? "building-android" : "building-ios";
44559
44735
  setState(state);
44560
44736
  const started = performance.now();
44561
- const child = run(executable, command.args, {
44737
+ const invocation = platform2 === "android" && host2 === "wsl" ? encodedWindowsExpoAndroidCommand(plan.project, options.androidRoot ?? absoluteManagedAndroidSdkRoot(host2), options.config.appId, command.args, capture, options.metroPort, options.androidDevice) : [executable, ...command.args];
44738
+ if (platform2 === "android" && host2 === "wsl") {
44739
+ log("[android] Mirroring native inputs to the Windows host for accelerated Expo build and launch.");
44740
+ }
44741
+ const [nativeExecutable, ...nativeArguments] = invocation;
44742
+ if (!nativeExecutable)
44743
+ throw new Error("Expo native build command is empty.");
44744
+ const child = run(nativeExecutable, nativeArguments, {
44562
44745
  cwd: plan.project,
44563
44746
  env: { ...process.env, ...command.env },
44564
44747
  stdio: ["ignore", "pipe", "pipe"]
@@ -44576,6 +44759,12 @@ var startAbsoluteExpoDevSession = async (options) => {
44576
44759
  if (exitCode !== 0) {
44577
44760
  throw new Error(`Expo ${platform2} development build exited with status ${exitCode}.`);
44578
44761
  }
44762
+ if (platform2 === "android" && host2 !== "wsl") {
44763
+ const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2);
44764
+ const adb = options.androidAdb ?? join11(androidRoot, "platform-tools", host2 === "windows" ? "adb.exe" : "adb");
44765
+ connectLocalExpoAndroid(adb, options.config.appId, options.metroPort, capture, options.androidDevice);
44766
+ log("Connected the Expo Android development client to Metro.");
44767
+ }
44579
44768
  if (platform2 === "ios" && options.certificateAuthorityPath && options.iosOrigin && new URL(options.iosOrigin).protocol === "https:" && !options.iosDevice) {
44580
44769
  setState("enrolling-trust");
44581
44770
  const trustStarted = performance.now();
@@ -44600,6 +44789,23 @@ var startAbsoluteExpoDevSession = async (options) => {
44600
44789
  log("Installed the AbsoluteJS development CA into the Expo iOS Simulator and relaunched the app.");
44601
44790
  publishTiming("enrolling-trust", performance.now() - trustStarted);
44602
44791
  }
44792
+ if (platform2 === "ios" && !options.iosDevice) {
44793
+ const utilityOptions = {
44794
+ cwd: plan.project,
44795
+ signal: options.signal,
44796
+ log: (line) => line && log(`[ios-connect] ${line}`)
44797
+ };
44798
+ await runUtilityCommand(run, "xcrun", ["simctl", "terminate", "booted", options.config.appId], utilityOptions);
44799
+ const connectExit = await runUtilityCommand(run, "xcrun", [
44800
+ "simctl",
44801
+ "openurl",
44802
+ "booted",
44803
+ expoDevelopmentClientUrl(options.config.appId, options.metroHost ?? "localhost", options.metroPort)
44804
+ ], utilityOptions);
44805
+ if (connectExit !== 0)
44806
+ throw new Error(`Expo iOS Simulator Metro connection exited with status ${connectExit}.`);
44807
+ log("Connected the Expo iOS Simulator development client to Metro.");
44808
+ }
44603
44809
  const durationMs = performance.now() - started;
44604
44810
  timings[state] = durationMs;
44605
44811
  options.onPhaseTiming?.({ durationMs, phase: state });
@@ -44890,8 +45096,8 @@ var iosLogTag = (entry) => {
44890
45096
  var DEFAULT_PORT_RANGE = 10;
44891
45097
  var RESTART_PARK_POLL_MS = 20;
44892
45098
  var NODE_API_IMPORT_ERROR = "To load Node-API modules, use require() or process.dlopen instead of import.";
44893
- var sourceServerBootstrap = resolvePath2(import.meta.dir, "../../dev/serverBootstrap.ts");
44894
- var serverBootstrap = existsSync6(sourceServerBootstrap) ? sourceServerBootstrap : resolvePath2(import.meta.dir, "../dev/serverBootstrap.js");
45099
+ var sourceServerBootstrap = resolvePath3(import.meta.dir, "../../dev/serverBootstrap.ts");
45100
+ var serverBootstrap = existsSync6(sourceServerBootstrap) ? sourceServerBootstrap : resolvePath3(import.meta.dir, "../dev/serverBootstrap.js");
44895
45101
  var formatServerBootDiagnostic = (output, serverEntry) => {
44896
45102
  if (!output.includes(NODE_API_IMPORT_ERROR))
44897
45103
  return null;
@@ -45019,7 +45225,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45019
45225
  let httpsEnabled = false;
45020
45226
  let devCertificateAuthorityPath = null;
45021
45227
  let resolvedDev;
45022
- let buildDirectory = resolvePath2(process.cwd(), "build");
45228
+ let buildDirectory = resolvePath3(process.cwd(), "build");
45023
45229
  let mobileConfig;
45024
45230
  let normalizedMobileConfig;
45025
45231
  let iosPhysicalServerHost;
@@ -45035,7 +45241,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45035
45241
  resolvedDev = resolveDevConfig(config?.dev);
45036
45242
  httpsEnabled = resolvedDev.https;
45037
45243
  if (config?.buildDirectory) {
45038
- buildDirectory = resolvePath2(process.cwd(), config.buildDirectory);
45244
+ buildDirectory = resolvePath3(process.cwd(), config.buildDirectory);
45039
45245
  }
45040
45246
  } catch {
45041
45247
  resolvedDev = resolveDevConfig(undefined);
@@ -45280,7 +45486,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45280
45486
  instanceLog.write(text2.replace(ANSI_LOG_REGEX, ""));
45281
45487
  } catch {}
45282
45488
  };
45283
- const usesDocker = existsSync6(resolvePath2(COMPOSE_PATH));
45489
+ const usesDocker = existsSync6(resolvePath3(COMPOSE_PATH));
45284
45490
  const scripts = usesDocker ? await readDbScripts() : null;
45285
45491
  if (scripts)
45286
45492
  await startDatabase(scripts);
@@ -45790,7 +45996,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45790
45996
  };
45791
45997
  let lastBuildDirectoryWarned;
45792
45998
  const refreshDevConfigForSpawn = async () => {
45793
- const cfgPath = resolvePath2(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts");
45999
+ const cfgPath = resolvePath3(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts");
45794
46000
  let cfg;
45795
46001
  try {
45796
46002
  const mod = await import(`${cfgPath}?t=${Date.now()}`);
@@ -45805,7 +46011,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45805
46011
  if (options.androidDevice && ["localhost", "127.0.0.1", "::1"].includes(refreshedDevConfig.host)) {
45806
46012
  refreshedDevConfig.host = "0.0.0.0";
45807
46013
  }
45808
- const desiredBuildDir = cfg?.buildDirectory ? resolvePath2(process.cwd(), cfg.buildDirectory) : resolvePath2(process.cwd(), "build");
46014
+ const desiredBuildDir = cfg?.buildDirectory ? resolvePath3(process.cwd(), cfg.buildDirectory) : resolvePath3(process.cwd(), "build");
45809
46015
  if (desiredBuildDir !== buildDirectory && desiredBuildDir !== lastBuildDirectoryWarned) {
45810
46016
  lastBuildDirectoryWarned = desiredBuildDir;
45811
46017
  console.log(cliTag("\x1B[33m", `buildDirectory changed in config (${buildDirectory} \u2192 ${desiredBuildDir}) \u2014 restart \`absolute dev\` to apply (the parent CLI holds a directory lock at the original path).`));
@@ -45837,7 +46043,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45837
46043
  for (const name of candidates) {
45838
46044
  let text2;
45839
46045
  try {
45840
- text2 = readFileSync11(resolvePath2(process.cwd(), name), "utf8");
46046
+ text2 = readFileSync11(resolvePath3(process.cwd(), name), "utf8");
45841
46047
  } catch {
45842
46048
  continue;
45843
46049
  }
@@ -45890,7 +46096,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45890
46096
  ABSOLUTE_HOST: resolvedDev.host,
45891
46097
  ABSOLUTE_INSTANCE_MANAGED: "1",
45892
46098
  ABSOLUTE_PORT: String(port),
45893
- ABSOLUTE_SERVER_ENTRY: resolvePath2(serverEntry),
46099
+ ABSOLUTE_SERVER_ENTRY: resolvePath3(serverEntry),
45894
46100
  ...mobileConfig ? { ABSOLUTE_MOBILE_PREVIEW: "1" } : {},
45895
46101
  FORCE_COLOR: "1",
45896
46102
  NODE_ENV: "development",
@@ -45959,7 +46165,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45959
46165
  try {
45960
46166
  const { watch: watch3 } = await import("fs");
45961
46167
  const { dirname: dirname9 } = await import("path");
45962
- const absServerEntry = resolvePath2(serverEntry);
46168
+ const absServerEntry = resolvePath3(serverEntry);
45963
46169
  const serverEntryDir = dirname9(absServerEntry);
45964
46170
  const ROOT_RESTART_DENY = new Set([
45965
46171
  "build",