@absolutejs/absolute 0.20.0-beta.41 → 0.20.0-beta.43

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.
@@ -3788,11 +3788,12 @@ import {
3788
3788
 
3789
3789
  // src/mobile/remoteMacWire.ts
3790
3790
  var ABSOLUTE_REMOTE_MAC_EVENT_PREFIX = "ABSOLUTE_REMOTE_MAC\t";
3791
- var ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION = 1;
3791
+ var ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION = 2;
3792
3792
 
3793
3793
  // src/mobile/remoteMacProtocol.ts
3794
3794
  var PROFILE_FORMAT = 1;
3795
3795
  var REMOTE_STDIN_FLUSH_ATTEMPTS = 3;
3796
+ var REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000;
3796
3797
  var PROFILE_NAME = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/u;
3797
3798
  var SSH_DESTINATION = /^(?:[A-Za-z0-9._-]+@)?[A-Za-z0-9._:-]+$/u;
3798
3799
  var defaultProfilePath = () => join7(homedir2(), ".absolutejs", "mobile", "remote-macs.json");
@@ -3994,6 +3995,11 @@ var removeAbsoluteRemoteMacProfile = async (name, profilePath) => {
3994
3995
  return true;
3995
3996
  };
3996
3997
  var projectIdentity = (projectRoot, appId) => createHash7("sha256").update(`${resolvePath2(projectRoot)}\x00${appId}`).digest("hex").slice(0, 20);
3998
+ var createAbsoluteRemoteExpoIosDevProject = (config, projectRoot, profile) => {
3999
+ if (config.engine !== "expo")
4000
+ throw new TypeError("Remote Expo execution requires mobile.engine: expo.");
4001
+ return createAbsoluteRemoteIosDevProject(config, projectRoot, profile);
4002
+ };
3997
4003
  var createAbsoluteRemoteIosDevProject = (config, projectRoot, profile) => ({
3998
4004
  cap: join7(resolvePath2(projectRoot), "node_modules", ".bin", "cap"),
3999
4005
  config,
@@ -4085,6 +4091,7 @@ var portableMobileConfig = (project) => ({
4085
4091
  appId: project.config.appId,
4086
4092
  appName: project.config.appName,
4087
4093
  bundleDirectory: portableRelativePath(project.projectRoot, project.config.bundleDirectory),
4094
+ engine: project.config.engine,
4088
4095
  ...project.config.deepLinkScheme || project.config.deepLinkHosts.length > 1 || project.config.appleAppIdPrefix ? {
4089
4096
  deepLinks: {
4090
4097
  ...project.config.deepLinkScheme ? { scheme: project.config.deepLinkScheme } : {},
@@ -4097,6 +4104,16 @@ var portableMobileConfig = (project) => ({
4097
4104
  }
4098
4105
  } : {},
4099
4106
  entry: project.config.entry,
4107
+ ...project.config.engine === "expo" ? {
4108
+ expo: { sdkVersion: project.config.expoSdkVersion ?? 57 },
4109
+ routes: {
4110
+ default: "web",
4111
+ native: Object.fromEntries(Object.entries(project.config.expoNativeRoutes).map(([route, module]) => [
4112
+ route,
4113
+ portableRelativePath(project.projectRoot, module)
4114
+ ]))
4115
+ }
4116
+ } : {},
4100
4117
  ...project.config.iosVersion ? { ios: { version: project.config.iosVersion } } : {},
4101
4118
  nativeProject: {
4102
4119
  directory: portableRelativePath(project.projectRoot, project.config.nativeProjectDirectory),
@@ -4188,7 +4205,32 @@ var consumeLines2 = async (stream, onLine) => {
4188
4205
  reader.releaseLock();
4189
4206
  }
4190
4207
  };
4191
- var startAbsoluteRemoteIosDevSession = async (options) => {
4208
+ var startAbsoluteRemoteExpoIosDevSession = async (options) => {
4209
+ const session = await startAbsoluteRemoteDevSession({
4210
+ certificateAuthorityPath: options.certificateAuthorityPath,
4211
+ deviceIdentifier: options.deviceIdentifier,
4212
+ https: options.https,
4213
+ installAgent: options.installAgent,
4214
+ log: options.log,
4215
+ metroPort: options.metroPort,
4216
+ onExpoPhaseTiming: options.onPhaseTiming,
4217
+ onExpoStateChange: options.onStateChange,
4218
+ port: options.port,
4219
+ project: options.project,
4220
+ serverHost: options.serverHost,
4221
+ signal: options.signal,
4222
+ syncProject: options.syncProject,
4223
+ transport: options.transport
4224
+ });
4225
+ return {
4226
+ close: session.close,
4227
+ metroPort: options.metroPort,
4228
+ platforms: ["ios"],
4229
+ timings: session.timings
4230
+ };
4231
+ };
4232
+ var startAbsoluteRemoteIosDevSession = (options) => startAbsoluteRemoteDevSession(options);
4233
+ var startAbsoluteRemoteDevSession = async (options) => {
4192
4234
  const startedAt = performance.now();
4193
4235
  const transport = options.transport ?? defaultTransport;
4194
4236
  const installAgent = options.installAgent ?? installAbsoluteRemoteMacAgent;
@@ -4202,9 +4244,22 @@ var startAbsoluteRemoteIosDevSession = async (options) => {
4202
4244
  const encodedConfig = Buffer.from(JSON.stringify(portableMobileConfig(options.project))).toString("base64url");
4203
4245
  const encodedCertificateAuthority = options.certificateAuthorityPath ? (await readFile9(options.certificateAuthorityPath)).toString("base64url") : undefined;
4204
4246
  const physicalDevice = options.deviceIdentifier !== undefined;
4205
- let relayPort;
4206
- if (physicalDevice)
4207
- relayPort = options.port <= 49151 ? options.port + 16384 : options.port - 16384;
4247
+ const expo = options.project.config.engine === "expo";
4248
+ if (expo && options.metroPort === undefined)
4249
+ throw new TypeError("Remote Expo execution requires a Metro port.");
4250
+ if (options.metroPort === options.port)
4251
+ throw new TypeError("Expo Metro and AbsoluteJS must use different ports.");
4252
+ const translatedRelayPort = (sourcePort) => sourcePort <= 49151 ? sourcePort + 16384 : sourcePort - 16384;
4253
+ const occupiedRemotePorts = new Set([options.port, options.metroPort].filter((value) => value !== undefined));
4254
+ const reserveRelayPort = (sourcePort) => {
4255
+ let candidate = translatedRelayPort(sourcePort);
4256
+ while (occupiedRemotePorts.has(candidate))
4257
+ candidate = candidate === 65535 ? 1024 : candidate + 1;
4258
+ occupiedRemotePorts.add(candidate);
4259
+ return candidate;
4260
+ };
4261
+ const relayPort = physicalDevice ? reserveRelayPort(options.port) : undefined;
4262
+ const metroRelayPort = physicalDevice && options.metroPort !== undefined ? reserveRelayPort(options.metroPort) : undefined;
4208
4263
  if (physicalDevice && !options.serverHost)
4209
4264
  throw new Error("Remote physical iOS development requires the Remote Mac LAN host.");
4210
4265
  const remoteCommand = [
@@ -4217,6 +4272,7 @@ var startAbsoluteRemoteIosDevSession = async (options) => {
4217
4272
  String(options.port),
4218
4273
  "--mobile-config",
4219
4274
  shellQuote(encodedConfig),
4275
+ ...expo && options.metroPort !== undefined ? ["--metro-port", String(options.metroPort)] : [],
4220
4276
  ...encodedCertificateAuthority ? [
4221
4277
  "--certificate-authority",
4222
4278
  shellQuote(encodedCertificateAuthority)
@@ -4228,7 +4284,8 @@ var startAbsoluteRemoteIosDevSession = async (options) => {
4228
4284
  "--server-host",
4229
4285
  shellQuote(options.serverHost ?? ""),
4230
4286
  "--relay-port",
4231
- String(relayPort)
4287
+ String(relayPort),
4288
+ ...expo && metroRelayPort !== undefined ? ["--metro-relay-port", String(metroRelayPort)] : []
4232
4289
  ] : []
4233
4290
  ].join(" ");
4234
4291
  const command = [
@@ -4237,6 +4294,10 @@ var startAbsoluteRemoteIosDevSession = async (options) => {
4237
4294
  "ExitOnForwardFailure=yes",
4238
4295
  "-R",
4239
4296
  physicalDevice ? `127.0.0.1:${relayPort}:127.0.0.1:${options.port}` : `${options.port}:127.0.0.1:${options.port}`,
4297
+ ...expo && options.metroPort !== undefined ? [
4298
+ "-R",
4299
+ physicalDevice ? `127.0.0.1:${metroRelayPort}:127.0.0.1:${options.metroPort}` : `${options.metroPort}:127.0.0.1:${options.metroPort}`
4300
+ ] : [],
4240
4301
  "/bin/sh -lc",
4241
4302
  shellQuote(remoteCommand)
4242
4303
  ];
@@ -4262,11 +4323,19 @@ var startAbsoluteRemoteIosDevSession = async (options) => {
4262
4323
  if (event.type === "native-log")
4263
4324
  options.nativeLog?.(event.entry);
4264
4325
  if (event.type === "state") {
4265
- ({ state } = event);
4266
- options.onStateChange?.(state);
4326
+ if (expo)
4327
+ options.onExpoStateChange?.(event.state);
4328
+ else {
4329
+ state = event.state;
4330
+ options.onStateChange?.(state);
4331
+ }
4332
+ }
4333
+ if (event.type === "timing") {
4334
+ if (expo)
4335
+ options.onExpoPhaseTiming?.(event);
4336
+ else
4337
+ options.onPhaseTiming?.(event);
4267
4338
  }
4268
- if (event.type === "timing")
4269
- options.onPhaseTiming?.(event);
4270
4339
  if (event.type === "ready") {
4271
4340
  ready = event;
4272
4341
  resolveReady();
@@ -4308,9 +4377,26 @@ var startAbsoluteRemoteIosDevSession = async (options) => {
4308
4377
  pending.clear();
4309
4378
  return;
4310
4379
  });
4311
- await readyPromise;
4380
+ try {
4381
+ await readyPromise;
4382
+ } catch (error) {
4383
+ process2.stdin.end();
4384
+ process2.kill();
4385
+ await process2.exited.catch(() => {
4386
+ return;
4387
+ });
4388
+ throw error;
4389
+ }
4312
4390
  if (!ready)
4313
4391
  throw fatal ?? new Error("Remote Mac did not become ready.");
4392
+ if (ready.engine !== options.project.config.engine) {
4393
+ process2.stdin.end();
4394
+ process2.kill();
4395
+ await process2.exited.catch(() => {
4396
+ return;
4397
+ });
4398
+ throw new Error("Remote Mac executor returned the wrong mobile engine.");
4399
+ }
4314
4400
  const totalDuration = performance.now() - startedAt;
4315
4401
  let currentReady = {
4316
4402
  ...ready,
@@ -4322,11 +4408,11 @@ var startAbsoluteRemoteIosDevSession = async (options) => {
4322
4408
  total: totalDuration
4323
4409
  }
4324
4410
  };
4325
- options.log?.(`Remote Mac connected (${options.project.profile.name}); agent ${agent.uploaded ? "uploaded" : "cache hit"}, project synced, and iOS ready in ${totalDuration.toFixed(2)}ms.`);
4411
+ options.log?.(`Remote Mac connected (${options.project.profile.name}); agent ${agent.uploaded ? "uploaded" : "cache hit"}, project synced, and ${expo ? "Expo " : ""}iOS ready in ${totalDuration.toFixed(2)}ms.`);
4326
4412
  const request = (commandName) => {
4327
4413
  const id = randomUUID4();
4328
4414
  const response = new Promise((resolve6, reject) => pending.set(id, { reject, resolve: resolve6 }));
4329
- process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: 1 })}
4415
+ process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION })}
4330
4416
  `);
4331
4417
  const flush = async () => {
4332
4418
  for (let attempt = 0;attempt < REMOTE_STDIN_FLUSH_ATTEMPTS; attempt++) {
@@ -4345,13 +4431,24 @@ var startAbsoluteRemoteIosDevSession = async (options) => {
4345
4431
  if (closed)
4346
4432
  return;
4347
4433
  closed = true;
4348
- await request("close").catch(() => {
4349
- return;
4350
- });
4434
+ const timeout = () => new Promise((resolve6) => setTimeout(() => resolve6("timeout"), REMOTE_SESSION_CLOSE_TIMEOUT_MS));
4435
+ await Promise.race([
4436
+ request("close").catch(() => {
4437
+ return;
4438
+ }),
4439
+ timeout()
4440
+ ]);
4351
4441
  process2.stdin.end();
4352
- await process2.exited.catch(() => {
4353
- return;
4354
- });
4442
+ const exit = await Promise.race([
4443
+ process2.exited.then(() => "exited").catch(() => "exited"),
4444
+ timeout()
4445
+ ]);
4446
+ if (exit === "timeout") {
4447
+ process2.kill();
4448
+ await process2.exited.catch(() => {
4449
+ return;
4450
+ });
4451
+ }
4355
4452
  };
4356
4453
  const makeSession = () => ({
4357
4454
  close,
@@ -6090,6 +6187,57 @@ var expoAppConfig = (config) => ({
6090
6187
  version: config.iosVersion ?? "0.1.0"
6091
6188
  }
6092
6189
  });
6190
+ var expoDynamicAppConfig = `${EXPO_GENERATED_HEADER}const config = require('./app.json');
6191
+
6192
+ if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH) {
6193
+ config.expo.plugins = [
6194
+ ...config.expo.plugins,
6195
+ './plugins/withAbsoluteDevelopmentCa'
6196
+ ];
6197
+ }
6198
+
6199
+ module.exports = config;
6200
+ `;
6201
+ var expoDevelopmentCaPlugin = `${EXPO_GENERATED_HEADER}const { AndroidConfig, withAndroidManifest, withDangerousMod } = require('expo/config-plugins');
6202
+ const { copyFile, mkdir } = require('node:fs/promises');
6203
+ const path = require('node:path');
6204
+
6205
+ const NETWORK_CONFIG = [
6206
+ '<?xml version="1.0" encoding="utf-8"?>',
6207
+ '<network-security-config>',
6208
+ ' <debug-overrides>',
6209
+ ' <trust-anchors>',
6210
+ ' <certificates src="@raw/absolutejs_dev_ca" />',
6211
+ ' </trust-anchors>',
6212
+ ' </debug-overrides>',
6213
+ '</network-security-config>',
6214
+ ''
6215
+ ].join('\\n');
6216
+
6217
+ const withAbsoluteDevelopmentCa = config => {
6218
+ config = withAndroidManifest(config, value => {
6219
+ const application = AndroidConfig.Manifest.getMainApplicationOrThrow(value.modResults);
6220
+ application.$['android:networkSecurityConfig'] = '@xml/absolutejs_dev_network_security';
6221
+ return value;
6222
+ });
6223
+ return withDangerousMod(config, ['android', async value => {
6224
+ const certificate = process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH;
6225
+ if (!certificate) throw new Error('AbsoluteJS Expo HTTPS development requires its development CA path.');
6226
+ const resources = path.join(value.modRequest.platformProjectRoot, 'app', 'src', 'main', 'res');
6227
+ await Promise.all([
6228
+ mkdir(path.join(resources, 'raw'), { recursive: true }),
6229
+ mkdir(path.join(resources, 'xml'), { recursive: true })
6230
+ ]);
6231
+ await Promise.all([
6232
+ copyFile(certificate, path.join(resources, 'raw', 'absolutejs_dev_ca.pem')),
6233
+ require('node:fs/promises').writeFile(path.join(resources, 'xml', 'absolutejs_dev_network_security.xml'), NETWORK_CONFIG)
6234
+ ]);
6235
+ return value;
6236
+ }]);
6237
+ };
6238
+
6239
+ module.exports = withAbsoluteDevelopmentCa;
6240
+ `;
6093
6241
  var metroConfig = (projectRoot) => `${EXPO_GENERATED_HEADER}const { getDefaultConfig } = require('expo/metro-config');
6094
6242
  const path = require('node:path');
6095
6243
 
@@ -6433,8 +6581,13 @@ node_modules/
6433
6581
  `
6434
6582
  ],
6435
6583
  [join14(project, "app.json"), jsonSource(expoAppConfig(config))],
6584
+ [join14(project, "app.config.js"), expoDynamicAppConfig],
6436
6585
  [join14(project, "package.json"), jsonSource(expoPackage())],
6437
6586
  [join14(project, "metro.config.js"), metroConfig(projectRoot)],
6587
+ [
6588
+ join14(project, "plugins", "withAbsoluteDevelopmentCa.js"),
6589
+ expoDevelopmentCaPlugin
6590
+ ],
6438
6591
  [
6439
6592
  join14(project, "tsconfig.json"),
6440
6593
  jsonSource(expoTsConfig(projectRoot, project))
@@ -6960,8 +7113,12 @@ var METRO_READY_TIMEOUT_MS = 60000;
6960
7113
  var PROCESS_CLOSE_TIMEOUT_MS = 2000;
6961
7114
  var commandEnvironment = (options) => ({
6962
7115
  ABSOLUTE_EXPO_DEVELOPMENT: "1",
7116
+ ...options.certificateAuthorityPath ? {
7117
+ ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH: options.certificateAuthorityPath
7118
+ } : {},
6963
7119
  ...options.androidOrigin ? { EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN: options.androidOrigin } : {},
6964
- ...options.iosOrigin ? { EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN: options.iosOrigin } : {}
7120
+ ...options.iosOrigin ? { EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN: options.iosOrigin } : {},
7121
+ ...options.metroHost ? { REACT_NATIVE_PACKAGER_HOSTNAME: options.metroHost } : {}
6965
7122
  });
6966
7123
  var absoluteExpoExecutable = async (project) => {
6967
7124
  const executable = join16(project, "node_modules", ".bin", "expo");
@@ -6975,8 +7132,11 @@ var absoluteExpoExecutable = async (project) => {
6975
7132
  var planAbsoluteExpoDevSession = (config, options) => {
6976
7133
  if (config.engine !== "expo")
6977
7134
  throw new TypeError("The Expo development controller requires Expo.");
6978
- if (options.platforms.length === 0)
6979
- throw new TypeError("Expo development requires a local target platform.");
7135
+ if (options.platforms.length === 0 && options.metro === "external")
7136
+ throw new TypeError("External Expo execution requires a target platform.");
7137
+ const secureOrigin = [options.androidOrigin, options.iosOrigin].some((origin) => origin && new URL(origin).protocol === "https:");
7138
+ if (secureOrigin && !options.certificateAuthorityPath)
7139
+ throw new TypeError("Expo HTTPS development requires the AbsoluteJS development CA certificate.");
6980
7140
  const env = commandEnvironment(options);
6981
7141
  const metro = {
6982
7142
  args: [
@@ -6990,7 +7150,7 @@ var planAbsoluteExpoDevSession = (config, options) => {
6990
7150
  env,
6991
7151
  role: "metro"
6992
7152
  };
6993
- const prepare = {
7153
+ const prepare = options.platforms.length === 0 ? undefined : {
6994
7154
  args: [
6995
7155
  "prebuild",
6996
7156
  "--clean",
@@ -7018,7 +7178,11 @@ var planAbsoluteExpoDevSession = (config, options) => {
7018
7178
  return command;
7019
7179
  });
7020
7180
  return {
7021
- commands: [prepare, metro, ...native],
7181
+ commands: [
7182
+ ...prepare ? [prepare] : [],
7183
+ ...options.metro === "external" ? [] : [metro],
7184
+ ...native
7185
+ ],
7022
7186
  metroPort: options.metroPort,
7023
7187
  project: config.nativeProjectDirectory
7024
7188
  };
@@ -7066,6 +7230,21 @@ var waitForExit = (process2) => new Promise((resolve14) => {
7066
7230
  }
7067
7231
  process2.once("exit", (code) => resolve14(code ?? 1));
7068
7232
  });
7233
+ var runUtilityCommand = async (run, command, args, options) => {
7234
+ const child = run(command, args, {
7235
+ cwd: options.cwd,
7236
+ env: process.env,
7237
+ stdio: ["ignore", "pipe", "pipe"]
7238
+ });
7239
+ forwardLines(child, options.log);
7240
+ const abort = () => void stopProcess(child);
7241
+ options.signal?.addEventListener("abort", abort, { once: true });
7242
+ const exitCode = await waitForExit(child);
7243
+ options.signal?.removeEventListener("abort", abort);
7244
+ if (options.signal?.aborted)
7245
+ throw abortError();
7246
+ return exitCode;
7247
+ };
7069
7248
  var startAbsoluteExpoDevSession = async (options) => {
7070
7249
  const plan = planAbsoluteExpoDevSession(options.config, options);
7071
7250
  const executable = options.executable ?? await absoluteExpoExecutable(plan.project);
@@ -7074,6 +7253,16 @@ var startAbsoluteExpoDevSession = async (options) => {
7074
7253
  return;
7075
7254
  });
7076
7255
  const timings = {};
7256
+ let caEnrollmentServer = null;
7257
+ const closeEnrollmentServer = async () => {
7258
+ const server = caEnrollmentServer;
7259
+ caEnrollmentServer = null;
7260
+ await server?.close();
7261
+ };
7262
+ const publishTiming = (phase, durationMs) => {
7263
+ timings[phase] = (timings[phase] ?? 0) + durationMs;
7264
+ options.onPhaseTiming?.({ durationMs, phase });
7265
+ };
7077
7266
  const setState = (state) => {
7078
7267
  options.onStateChange?.(state);
7079
7268
  };
@@ -7082,47 +7271,48 @@ var startAbsoluteExpoDevSession = async (options) => {
7082
7271
  const prepareCommand = plan.commands.find((command) => command.role === "native-prepare");
7083
7272
  const metroCommand = plan.commands.find((command) => command.role === "metro");
7084
7273
  const nativeCommands = plan.commands.filter((command) => command.role === "native-build");
7085
- if (!prepareCommand)
7086
- throw new TypeError("Expo native preparation command is missing.");
7087
- if (!metroCommand)
7088
- throw new TypeError("Expo Metro command is missing.");
7089
- setState("preparing-native");
7090
- const prepareStarted = performance.now();
7091
- const prepareProcess = run(executable, prepareCommand.args, {
7092
- cwd: plan.project,
7093
- env: { ...process.env, ...prepareCommand.env },
7094
- stdio: ["ignore", "pipe", "pipe"]
7095
- });
7096
- forwardLines(prepareProcess, (line) => {
7097
- if (line)
7098
- log(`[prebuild] ${line}`);
7099
- });
7100
- const abortPrepare = () => void stopProcess(prepareProcess);
7101
- options.signal?.addEventListener("abort", abortPrepare, { once: true });
7102
- const prepareExit = await waitForExit(prepareProcess);
7103
- options.signal?.removeEventListener("abort", abortPrepare);
7104
- if (options.signal?.aborted)
7105
- throw abortError();
7106
- if (prepareExit !== 0) {
7107
- setState("failed");
7108
- throw new Error(`Expo native preparation exited with status ${prepareExit}.`);
7109
- }
7110
- const prepareMs = performance.now() - prepareStarted;
7111
- timings["preparing-native"] = prepareMs;
7112
- options.onPhaseTiming?.({
7113
- durationMs: prepareMs,
7114
- phase: "preparing-native"
7115
- });
7116
- setState("starting-metro");
7274
+ const runPrepareCommand = async (command) => {
7275
+ setState("preparing-native");
7276
+ const prepareStarted = performance.now();
7277
+ const prepareProcess = run(executable, command.args, {
7278
+ cwd: plan.project,
7279
+ env: { ...process.env, ...command.env },
7280
+ stdio: ["ignore", "pipe", "pipe"]
7281
+ });
7282
+ forwardLines(prepareProcess, (line) => {
7283
+ if (line)
7284
+ log(`[prebuild] ${line}`);
7285
+ });
7286
+ const abortPrepare = () => void stopProcess(prepareProcess);
7287
+ options.signal?.addEventListener("abort", abortPrepare, { once: true });
7288
+ const prepareExit = await waitForExit(prepareProcess);
7289
+ options.signal?.removeEventListener("abort", abortPrepare);
7290
+ if (options.signal?.aborted)
7291
+ throw abortError();
7292
+ if (prepareExit !== 0) {
7293
+ setState("failed");
7294
+ throw new Error(`Expo native preparation exited with status ${prepareExit}.`);
7295
+ }
7296
+ publishTiming("preparing-native", performance.now() - prepareStarted);
7297
+ };
7298
+ if (prepareCommand)
7299
+ await runPrepareCommand(prepareCommand);
7300
+ const managedMetro = metroCommand !== undefined;
7301
+ if (managedMetro)
7302
+ setState("starting-metro");
7117
7303
  const metroStarted = performance.now();
7118
- const metro = run(executable, metroCommand.args, {
7304
+ const metro = metroCommand ? run(executable, metroCommand.args, {
7119
7305
  cwd: plan.project,
7120
7306
  env: { ...process.env, ...metroCommand.env },
7121
7307
  stdio: ["ignore", "pipe", "pipe"]
7122
- });
7308
+ }) : undefined;
7123
7309
  let metroReady = false;
7124
7310
  let resolveMetro;
7125
7311
  const metroPromise = new Promise((resolve14, reject) => {
7312
+ if (!metro) {
7313
+ resolve14();
7314
+ return;
7315
+ }
7126
7316
  const timeout = setTimeout(() => {
7127
7317
  reject(new Error("Expo Metro did not become ready within 60 seconds."));
7128
7318
  }, METRO_READY_TIMEOUT_MS);
@@ -7137,15 +7327,19 @@ var startAbsoluteExpoDevSession = async (options) => {
7137
7327
  }
7138
7328
  });
7139
7329
  });
7140
- forwardLines(metro, (line) => {
7141
- if (line)
7142
- log(`[metro] ${line}`);
7143
- if (!metroReady && /(?:Waiting on|Metro waiting on|Dev server ready)/iu.test(line)) {
7144
- metroReady = true;
7145
- resolveMetro?.();
7146
- }
7147
- });
7148
- const abort = () => void stopProcess(metro);
7330
+ if (metro)
7331
+ forwardLines(metro, (line) => {
7332
+ if (line)
7333
+ log(`[metro] ${line}`);
7334
+ if (!metroReady && /(?:Waiting on|Metro waiting on|Dev server ready)/iu.test(line)) {
7335
+ metroReady = true;
7336
+ resolveMetro?.();
7337
+ }
7338
+ });
7339
+ const abort = () => {
7340
+ if (metro)
7341
+ stopProcess(metro);
7342
+ };
7149
7343
  options.signal?.addEventListener("abort", abort, { once: true });
7150
7344
  const runNativeCommand = async (command) => {
7151
7345
  if (options.signal?.aborted)
@@ -7174,6 +7368,30 @@ var startAbsoluteExpoDevSession = async (options) => {
7174
7368
  if (exitCode !== 0) {
7175
7369
  throw new Error(`Expo ${platform} development build exited with status ${exitCode}.`);
7176
7370
  }
7371
+ if (platform === "ios" && options.certificateAuthorityPath && options.iosOrigin && new URL(options.iosOrigin).protocol === "https:" && !options.iosDevice) {
7372
+ setState("enrolling-trust");
7373
+ const trustStarted = performance.now();
7374
+ const utilityOptions = {
7375
+ cwd: plan.project,
7376
+ signal: options.signal,
7377
+ log: (line) => line && log(`[ios-trust] ${line}`)
7378
+ };
7379
+ const trustExit = await runUtilityCommand(run, "xcrun", [
7380
+ "simctl",
7381
+ "keychain",
7382
+ "booted",
7383
+ "add-root-cert",
7384
+ options.certificateAuthorityPath
7385
+ ], utilityOptions);
7386
+ if (trustExit !== 0)
7387
+ throw new Error(`Expo iOS Simulator development CA trust exited with status ${trustExit}.`);
7388
+ await runUtilityCommand(run, "xcrun", ["simctl", "terminate", "booted", options.config.appId], utilityOptions);
7389
+ const launchExit = await runUtilityCommand(run, "xcrun", ["simctl", "launch", "booted", options.config.appId], utilityOptions);
7390
+ if (launchExit !== 0)
7391
+ throw new Error(`Expo iOS Simulator relaunch exited with status ${launchExit}.`);
7392
+ log("Installed the AbsoluteJS development CA into the Expo iOS Simulator and relaunched the app.");
7393
+ publishTiming("enrolling-trust", performance.now() - trustStarted);
7394
+ }
7177
7395
  const durationMs = performance.now() - started;
7178
7396
  timings[state] = durationMs;
7179
7397
  options.onPhaseTiming?.({ durationMs, phase: state });
@@ -7185,14 +7403,25 @@ var startAbsoluteExpoDevSession = async (options) => {
7185
7403
  await runNativeCommand(command);
7186
7404
  await runNativeCommands(remaining);
7187
7405
  };
7406
+ const startPhysicalIosEnrollment = async () => {
7407
+ if (!options.iosDevice || !options.certificateAuthorityPath || !options.iosOrigin || new URL(options.iosOrigin).protocol !== "https:") {
7408
+ return;
7409
+ }
7410
+ setState("enrolling-trust");
7411
+ const trustStarted = performance.now();
7412
+ const startEnrollment = options.startCaEnrollmentServer ?? startAbsoluteIosCaEnrollmentServer;
7413
+ caEnrollmentServer = await startEnrollment({
7414
+ certificateAuthorityPath: options.certificateAuthorityPath,
7415
+ displayHost: new URL(options.iosOrigin).hostname
7416
+ });
7417
+ log(`On the iOS device, open ${caEnrollmentServer.url}, install the AbsoluteJS development CA profile, then enable it under Settings > General > About > Certificate Trust Settings. This public CA endpoint exists only for this dev session.`);
7418
+ publishTiming("enrolling-trust", performance.now() - trustStarted);
7419
+ };
7188
7420
  try {
7421
+ await startPhysicalIosEnrollment();
7189
7422
  await metroPromise;
7190
- const metroMs = performance.now() - metroStarted;
7191
- timings["starting-metro"] = metroMs;
7192
- options.onPhaseTiming?.({
7193
- durationMs: metroMs,
7194
- phase: "starting-metro"
7195
- });
7423
+ if (managedMetro)
7424
+ publishTiming("starting-metro", performance.now() - metroStarted);
7196
7425
  await runNativeCommands(nativeCommands);
7197
7426
  setState("ready");
7198
7427
  return {
@@ -7201,13 +7430,17 @@ var startAbsoluteExpoDevSession = async (options) => {
7201
7430
  timings,
7202
7431
  close: async () => {
7203
7432
  options.signal?.removeEventListener("abort", abort);
7204
- await stopProcess(metro);
7433
+ if (metro)
7434
+ await stopProcess(metro);
7435
+ await closeEnrollmentServer();
7205
7436
  setState("closed");
7206
7437
  }
7207
7438
  };
7208
7439
  } catch (error) {
7209
7440
  options.signal?.removeEventListener("abort", abort);
7210
- await stopProcess(metro);
7441
+ if (metro)
7442
+ await stopProcess(metro);
7443
+ await closeEnrollmentServer();
7211
7444
  setState("failed");
7212
7445
  throw error;
7213
7446
  }
@@ -9279,6 +9512,7 @@ export {
9279
9512
  syncAbsoluteRemoteMacProject,
9280
9513
  syncAbsoluteExpoWebAssets,
9281
9514
  startAbsoluteRemoteIosDevSession,
9515
+ startAbsoluteRemoteExpoIosDevSession,
9282
9516
  startAbsoluteIosTcpRelay,
9283
9517
  startAbsoluteIosDevSession,
9284
9518
  startAbsoluteIosCaEnrollmentServer,
@@ -9355,6 +9589,7 @@ export {
9355
9589
  discoverAbsoluteDeviceCapabilities,
9356
9590
  directAbsoluteProjectPackages,
9357
9591
  createAbsoluteRemoteIosDevProject,
9592
+ createAbsoluteRemoteExpoIosDevProject,
9358
9593
  createAbsoluteMobileUpgradeResponse,
9359
9594
  createAbsoluteMobileRouteMetadataPlugin,
9360
9595
  createAbsoluteMobilePreviewPlugin,
@@ -9417,5 +9652,5 @@ export {
9417
9652
  ABSOLUTE_ANDROID_RELEASE_FORMAT
9418
9653
  };
9419
9654
 
9420
- //# debugId=DD3370D585C0D59B64756E2164756E21
9655
+ //# debugId=97C358B153ACDA7B64756E2164756E21
9421
9656
  //# sourceMappingURL=index.js.map