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

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.
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-DRGeXr/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-vdpOlu/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-DRGeXr/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-vdpOlu/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -48,7 +48,7 @@ var warnMissingStreamingSlotCollector = (primitiveName) => {
48
48
  getWarningController()?.maybeWarn(primitiveName);
49
49
  };
50
50
 
51
- // .angular-partial-tmp-DRGeXr/src/core/streamingSlotRegistry.ts
51
+ // .angular-partial-tmp-vdpOlu/src/core/streamingSlotRegistry.ts
52
52
  var STREAMING_SLOT_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotAsyncLocalStorage");
53
53
  var isObjectRecord2 = (value) => Boolean(value) && typeof value === "object";
54
54
  var isAsyncLocalStorage = (value) => isObjectRecord2(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
package/dist/cli/index.js CHANGED
@@ -935,7 +935,7 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
935
935
  slug: config.appId.toLowerCase().replaceAll(".", "-"),
936
936
  version: config.iosVersion ?? "0.1.0"
937
937
  }
938
- }), metroConfig = (projectRoot) => `${EXPO_GENERATED_HEADER}const { getDefaultConfig } = require('expo/metro-config');
938
+ }), expoDynamicAppConfig, expoDevelopmentCaPlugin, metroConfig = (projectRoot) => `${EXPO_GENERATED_HEADER}const { getDefaultConfig } = require('expo/metro-config');
939
939
  const path = require('node:path');
940
940
 
941
941
  const projectRoot = __dirname;
@@ -1223,8 +1223,13 @@ node_modules/
1223
1223
  `
1224
1224
  ],
1225
1225
  [join5(project, "app.json"), jsonSource(expoAppConfig(config))],
1226
+ [join5(project, "app.config.js"), expoDynamicAppConfig],
1226
1227
  [join5(project, "package.json"), jsonSource(expoPackage())],
1227
1228
  [join5(project, "metro.config.js"), metroConfig(projectRoot)],
1229
+ [
1230
+ join5(project, "plugins", "withAbsoluteDevelopmentCa.js"),
1231
+ expoDevelopmentCaPlugin
1232
+ ],
1228
1233
  [
1229
1234
  join5(project, "tsconfig.json"),
1230
1235
  jsonSource(expoTsConfig(projectRoot, project))
@@ -1360,6 +1365,57 @@ export const materializeAbsoluteWebBundle = async () => {
1360
1365
  return { appBuild, assets: assets.length, bundleId, path: destination };
1361
1366
  };
1362
1367
  var init_expoProject = __esm(() => {
1368
+ expoDynamicAppConfig = `${EXPO_GENERATED_HEADER}const config = require('./app.json');
1369
+
1370
+ if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH) {
1371
+ config.expo.plugins = [
1372
+ ...config.expo.plugins,
1373
+ './plugins/withAbsoluteDevelopmentCa'
1374
+ ];
1375
+ }
1376
+
1377
+ module.exports = config;
1378
+ `;
1379
+ expoDevelopmentCaPlugin = `${EXPO_GENERATED_HEADER}const { AndroidConfig, withAndroidManifest, withDangerousMod } = require('expo/config-plugins');
1380
+ const { copyFile, mkdir } = require('node:fs/promises');
1381
+ const path = require('node:path');
1382
+
1383
+ const NETWORK_CONFIG = [
1384
+ '<?xml version="1.0" encoding="utf-8"?>',
1385
+ '<network-security-config>',
1386
+ ' <debug-overrides>',
1387
+ ' <trust-anchors>',
1388
+ ' <certificates src="@raw/absolutejs_dev_ca" />',
1389
+ ' </trust-anchors>',
1390
+ ' </debug-overrides>',
1391
+ '</network-security-config>',
1392
+ ''
1393
+ ].join('\\n');
1394
+
1395
+ const withAbsoluteDevelopmentCa = config => {
1396
+ config = withAndroidManifest(config, value => {
1397
+ const application = AndroidConfig.Manifest.getMainApplicationOrThrow(value.modResults);
1398
+ application.$['android:networkSecurityConfig'] = '@xml/absolutejs_dev_network_security';
1399
+ return value;
1400
+ });
1401
+ return withDangerousMod(config, ['android', async value => {
1402
+ const certificate = process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH;
1403
+ if (!certificate) throw new Error('AbsoluteJS Expo HTTPS development requires its development CA path.');
1404
+ const resources = path.join(value.modRequest.platformProjectRoot, 'app', 'src', 'main', 'res');
1405
+ await Promise.all([
1406
+ mkdir(path.join(resources, 'raw'), { recursive: true }),
1407
+ mkdir(path.join(resources, 'xml'), { recursive: true })
1408
+ ]);
1409
+ await Promise.all([
1410
+ copyFile(certificate, path.join(resources, 'raw', 'absolutejs_dev_ca.pem')),
1411
+ require('node:fs/promises').writeFile(path.join(resources, 'xml', 'absolutejs_dev_network_security.xml'), NETWORK_CONFIG)
1412
+ ]);
1413
+ return value;
1414
+ }]);
1415
+ };
1416
+
1417
+ module.exports = withAbsoluteDevelopmentCa;
1418
+ `;
1363
1419
  layoutSource = `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
1364
1420
 
1365
1421
  export default function AbsoluteLayout() {
@@ -1411,6 +1467,97 @@ export default AbsoluteWebHost;
1411
1467
  `;
1412
1468
  });
1413
1469
 
1470
+ // src/mobile/iosPhysicalDeviceTransport.ts
1471
+ import { randomUUID, X509Certificate } from "crypto";
1472
+ import { createServer as createServer2 } from "http";
1473
+ import {
1474
+ connect as connectTcp,
1475
+ createServer as createTcpServer,
1476
+ isIP
1477
+ } from "net";
1478
+ import { readFile as readFile2 } from "fs/promises";
1479
+ var closeServer = (server) => new Promise((resolve4, reject) => {
1480
+ server.close((error) => {
1481
+ if (error)
1482
+ reject(error);
1483
+ else
1484
+ resolve4();
1485
+ });
1486
+ }), listen = (server, port) => new Promise((resolve4, reject) => {
1487
+ server.once("error", reject);
1488
+ server.listen(port, "0.0.0.0", () => {
1489
+ server.off("error", reject);
1490
+ const address = server.address();
1491
+ if (!address || typeof address === "string") {
1492
+ reject(new Error("Could not determine the iOS device helper port."));
1493
+ return;
1494
+ }
1495
+ resolve4(address.port);
1496
+ });
1497
+ }), findEphemeralPort = async () => {
1498
+ const probe = createTcpServer();
1499
+ const port = await new Promise((resolve4, reject) => {
1500
+ probe.once("error", reject);
1501
+ probe.listen(0, "127.0.0.1", () => {
1502
+ const address = probe.address();
1503
+ if (!address || typeof address === "string") {
1504
+ reject(new Error("Could not allocate the iOS CA enrollment port."));
1505
+ return;
1506
+ }
1507
+ resolve4(address.port);
1508
+ });
1509
+ });
1510
+ await closeServer(probe);
1511
+ return port;
1512
+ }, normalizeAbsoluteIosDeviceHost = (value) => {
1513
+ const normalized = value.trim();
1514
+ if (!normalized || normalized.length > 253 || /[\0\s/?#]/u.test(normalized))
1515
+ throw new TypeError("Physical iOS development requires a valid LAN host.");
1516
+ return normalized;
1517
+ }, normalizeAbsoluteIosDeviceIdentifier = (value) => {
1518
+ const normalized = value.trim();
1519
+ if (!normalized || normalized.length > 256 || /[\0\r\n]/u.test(normalized))
1520
+ throw new TypeError("--ios-device requires a valid Xcode device identifier or name.");
1521
+ return normalized;
1522
+ }, urlForHost = (protocol, host, port) => {
1523
+ const url = new URL(`${protocol}://localhost:${port}`);
1524
+ const normalizedHost = normalizeAbsoluteIosDeviceHost(host);
1525
+ url.hostname = isIP(normalizedHost) === 6 ? `[${normalizedHost}]` : normalizedHost;
1526
+ return url;
1527
+ }, startAbsoluteIosCaEnrollmentServer = async (options) => {
1528
+ const certificate = new X509Certificate(await readFile2(options.certificateAuthorityPath));
1529
+ const certificateBytes = certificate.raw;
1530
+ const token = randomUUID().replaceAll("-", "");
1531
+ const certificatePath = `/${token}/absolutejs-development-ca.cer`;
1532
+ const server = createServer2((request, response) => {
1533
+ if (request.method !== "GET" || request.url !== certificatePath) {
1534
+ response.writeHead(404, {
1535
+ "Cache-Control": "no-store",
1536
+ "Content-Type": "text/plain; charset=utf-8"
1537
+ });
1538
+ response.end("Not found.");
1539
+ return;
1540
+ }
1541
+ response.writeHead(200, {
1542
+ "Cache-Control": "no-store",
1543
+ "Content-Disposition": 'attachment; filename="absolutejs-development-ca.cer"',
1544
+ "Content-Length": String(certificateBytes.byteLength),
1545
+ "Content-Type": "application/x-x509-ca-cert",
1546
+ "X-Content-Type-Options": "nosniff"
1547
+ });
1548
+ response.end(certificateBytes);
1549
+ });
1550
+ const port = await findEphemeralPort();
1551
+ await listen(server, port);
1552
+ const url = urlForHost("http", options.displayHost, port);
1553
+ url.pathname = certificatePath;
1554
+ return {
1555
+ url: url.href,
1556
+ close: () => closeServer(server)
1557
+ };
1558
+ };
1559
+ var init_iosPhysicalDeviceTransport = () => {};
1560
+
1414
1561
  // src/mobile/nativeAuth.ts
1415
1562
  import { readFileSync as readFileSync5 } from "fs";
1416
1563
  import { join as join7 } from "path";
@@ -1459,7 +1606,7 @@ var init_nativeAuth = __esm(() => {
1459
1606
  var {$: $2 } = globalThis.Bun;
1460
1607
  import { execSync } from "child_process";
1461
1608
  import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
1462
- import { createServer as createServer2 } from "net";
1609
+ import { createServer as createServer3 } from "net";
1463
1610
  import { resolve as resolve4 } from "path";
1464
1611
  var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backend/server.ts", isWSLEnvironment = () => {
1465
1612
  try {
@@ -1473,7 +1620,7 @@ var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backe
1473
1620
  process.kill(pid, "SIGTERM");
1474
1621
  } catch {}
1475
1622
  }, findFreePort = () => new Promise((_resolve, reject) => {
1476
- const server = createServer2();
1623
+ const server = createServer3();
1477
1624
  server.unref();
1478
1625
  server.once("error", reject);
1479
1626
  server.listen(0, "127.0.0.1", () => {
@@ -1809,7 +1956,7 @@ var init_emulatorDoctor = __esm(() => {
1809
1956
  });
1810
1957
 
1811
1958
  // src/mobile/capacitorProject.ts
1812
- import { access as access4, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
1959
+ import { access as access4, readFile as readFile3, rename as rename2, writeFile as writeFile2 } from "fs/promises";
1813
1960
  import { relative as relative2, resolve as resolve5 } from "path";
1814
1961
  var CONFIG_FILE = "capacitor.config.ts", portableRelative2 = (root, path) => relative2(root, path).replaceAll("\\", "/"), capacitorConfigSource = (config, projectRoot) => `import type { CapacitorConfig } from '@capacitor/cli';
1815
1962
 
@@ -1838,7 +1985,7 @@ export default config;
1838
1985
  const destination = resolve5(projectRoot, CONFIG_FILE);
1839
1986
  const source = capacitorConfigSource(config, projectRoot);
1840
1987
  if (await exists2(destination)) {
1841
- const current = await readFile2(destination, "utf8");
1988
+ const current = await readFile3(destination, "utf8");
1842
1989
  if (current === source)
1843
1990
  return { changed: false, path: destination };
1844
1991
  if (!options.force) {
@@ -1858,7 +2005,7 @@ import {
1858
2005
  copyFile,
1859
2006
  lstat,
1860
2007
  mkdir as mkdir2,
1861
- readFile as readFile3,
2008
+ readFile as readFile4,
1862
2009
  readdir as readdir2,
1863
2010
  readlink,
1864
2011
  realpath,
@@ -1866,7 +2013,7 @@ import {
1866
2013
  rm as rm2,
1867
2014
  writeFile as writeFile3
1868
2015
  } from "fs/promises";
1869
- import { createHash as createHash2, randomUUID } from "crypto";
2016
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
1870
2017
  import {
1871
2018
  dirname as dirname4,
1872
2019
  isAbsolute,
@@ -2072,9 +2219,9 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2072
2219
  String(identity)
2073
2220
  ]))
2074
2221
  };
2075
- }, readNativeCache = async (projectRoot) => readFile3(nativeCachePath(projectRoot), "utf8").then((source) => parseNativeCache(JSON.parse(source))).catch(() => null), writeNativeCache = async (projectRoot, cache) => {
2222
+ }, readNativeCache = async (projectRoot) => readFile4(nativeCachePath(projectRoot), "utf8").then((source) => parseNativeCache(JSON.parse(source))).catch(() => null), writeNativeCache = async (projectRoot, cache) => {
2076
2223
  const destination = nativeCachePath(projectRoot);
2077
- const temporary = `${destination}.${process.pid}.${randomUUID()}.tmp`;
2224
+ const temporary = `${destination}.${process.pid}.${randomUUID2()}.tmp`;
2078
2225
  await mkdir2(dirname4(destination), { recursive: true });
2079
2226
  try {
2080
2227
  await writeFile3(temporary, `${JSON.stringify(cache, null, "\t")}
@@ -2088,7 +2235,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2088
2235
  });
2089
2236
  }
2090
2237
  }, nativeDependencySources = async (nativeDirectory) => {
2091
- const settings = await readFile3(join9(nativeDirectory, "capacitor.settings.gradle"), "utf8");
2238
+ const settings = await readFile4(join9(nativeDirectory, "capacitor.settings.gradle"), "utf8");
2092
2239
  const pattern = new RegExp(CAPACITOR_PROJECT_DIRECTORY_PATTERN.source, CAPACITOR_PROJECT_DIRECTORY_PATTERN.flags);
2093
2240
  const dependencies = [...settings.matchAll(pattern)].map((match) => ({
2094
2241
  name: (match[1] ?? "").slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_"),
@@ -2119,7 +2266,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2119
2266
  return [];
2120
2267
  const [metadata, contents] = await Promise.all([
2121
2268
  lstat(path),
2122
- readFile3(path)
2269
+ readFile4(path)
2123
2270
  ]);
2124
2271
  const contentDigest = createHash2("sha256").update(contents).digest("hex");
2125
2272
  return [
@@ -2189,7 +2336,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2189
2336
  await rm2(paths.root, { force: true, recursive: true });
2190
2337
  return false;
2191
2338
  }
2192
- const journal = await readFile3(paths.journal, "utf8").then((source) => parseJournal(JSON.parse(source))).catch(() => null);
2339
+ const journal = await readFile4(paths.journal, "utf8").then((source) => parseJournal(JSON.parse(source))).catch(() => null);
2193
2340
  if (!journal || !isInside(projectRoot, journal.nativeConfigPath) || !isInside(paths.root, journal.backupPath) || journal.nativeManifestPath !== undefined && !isInside(projectRoot, journal.nativeManifestPath) || journal.manifestBackupPath !== undefined && !isInside(paths.root, journal.manifestBackupPath) || journal.projectedFiles?.some((file) => !isInside(projectRoot, file.path) || file.backupPath !== undefined && !isInside(paths.root, file.backupPath))) {
2194
2341
  throw new Error(`Refusing unsafe or invalid mobile dev journal at ${paths.journal}.`);
2195
2342
  }
@@ -2248,8 +2395,8 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2248
2395
  }, writeDevConfig = async (projectRoot, nativeConfigPath, nativeManifestPath, port, https, entry, embeddedBundle, serverHost, certificateAuthorityPath) => {
2249
2396
  const paths = journalPaths(projectRoot);
2250
2397
  await repairAbsoluteAndroidDevSession(projectRoot);
2251
- const source = await readFile3(nativeConfigPath, "utf8");
2252
- const manifestSource = await readFile3(nativeManifestPath, "utf8");
2398
+ const source = await readFile4(nativeConfigPath, "utf8");
2399
+ const manifestSource = await readFile4(nativeManifestPath, "utf8");
2253
2400
  const parsed = JSON.parse(source);
2254
2401
  if (!isRecord(parsed)) {
2255
2402
  throw new Error(`Invalid Capacitor native config at ${nativeConfigPath}.`);
@@ -2302,7 +2449,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2302
2449
  mkdir2(dirname4(networkConfigPath), { recursive: true })
2303
2450
  ]);
2304
2451
  await copyFile(certificateAuthorityPath, caPath);
2305
- const existingNetworkConfigSource = existingNetworkConfig ? await readFile3(networkConfigPath, "utf8") : `<?xml version="1.0" encoding="utf-8"?>
2452
+ const existingNetworkConfigSource = existingNetworkConfig ? await readFile4(networkConfigPath, "utf8") : `<?xml version="1.0" encoding="utf-8"?>
2306
2453
  <network-security-config>
2307
2454
  </network-security-config>
2308
2455
  `;
@@ -2355,7 +2502,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2355
2502
  return result.stdout.trim();
2356
2503
  }, mirroredCapacitorDependencies = async (project, capture) => {
2357
2504
  const settingsPath = join9(project.nativeDirectory, "capacitor.settings.gradle");
2358
- const settings = await readFile3(settingsPath, "utf8");
2505
+ const settings = await readFile4(settingsPath, "utf8");
2359
2506
  const dependencies = [];
2360
2507
  const rewrittenSettings = settings.replace(CAPACITOR_PROJECT_DIRECTORY_PATTERN, (_statement, projectName, sourcePath) => {
2361
2508
  const name = projectName.slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_");
@@ -2855,7 +3002,7 @@ var init_androidEmulatorController = __esm(() => {
2855
3002
 
2856
3003
  // src/mobile/emulatorInstaller.ts
2857
3004
  import { createHash as createHash3 } from "crypto";
2858
- import { cp as cp2, mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile4, rm as rm3 } from "fs/promises";
3005
+ import { cp as cp2, mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile5, rm as rm3 } from "fs/promises";
2859
3006
  import { tmpdir } from "os";
2860
3007
  import { basename as basename3, join as join10 } from "path";
2861
3008
  var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION = "15859902", LICENSE_ACCEPTANCE_RESPONSES = 100, COMMAND_LINE_TOOLS, defaultRun = async (command, options = {}) => {
@@ -3064,7 +3211,7 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3064
3211
  const arch2 = options.arch ?? process.arch;
3065
3212
  const exists3 = options.exists ?? (async (path) => {
3066
3213
  try {
3067
- await readFile4(path);
3214
+ await readFile5(path);
3068
3215
  return true;
3069
3216
  } catch {
3070
3217
  return false;
@@ -3167,7 +3314,7 @@ import {
3167
3314
  mkdir as mkdir4,
3168
3315
  mkdtemp as mkdtemp3,
3169
3316
  readdir as readdir3,
3170
- readFile as readFile5,
3317
+ readFile as readFile6,
3171
3318
  rename as rename4,
3172
3319
  rm as rm4,
3173
3320
  stat,
@@ -3243,7 +3390,7 @@ var developmentTeamArgument = (value) => {
3243
3390
  }, fingerprintAbsoluteIosNativeProject = async (nativeDirectory, options = {}) => {
3244
3391
  const hasher = createHash4("sha256");
3245
3392
  const files = await fingerprintFiles(nativeDirectory, nativeDirectory, options);
3246
- const contents = await Promise.all(files.map((file) => readFile5(file)));
3393
+ const contents = await Promise.all(files.map((file) => readFile6(file)));
3247
3394
  files.forEach((file, index) => {
3248
3395
  hasher.update(relative4(nativeDirectory, file).replaceAll("\\", "/"));
3249
3396
  hasher.update("\x00");
@@ -3259,7 +3406,7 @@ var developmentTeamArgument = (value) => {
3259
3406
  throw new TypeError("mobile build --outdir must remain inside the project.");
3260
3407
  }
3261
3408
  return output;
3262
- }, sha256File = async (path) => createHash4("sha256").update(await readFile5(path)).digest("hex"), findByExtension = async (root, extension) => {
3409
+ }, sha256File = async (path) => createHash4("sha256").update(await readFile6(path)).digest("hex"), findByExtension = async (root, extension) => {
3263
3410
  if (!await pathExists3(root))
3264
3411
  return;
3265
3412
  const entries = await readdir3(root, { withFileTypes: true });
@@ -3290,7 +3437,7 @@ var developmentTeamArgument = (value) => {
3290
3437
  const releaseRoot = join11(outputRoot, metadata.releaseId);
3291
3438
  const destination = join11(releaseRoot, "App.ipa");
3292
3439
  if (await pathExists3(releaseRoot)) {
3293
- const value = JSON.parse(await readFile5(join11(releaseRoot, "release.json"), "utf8"));
3440
+ const value = JSON.parse(await readFile6(join11(releaseRoot, "release.json"), "utf8"));
3294
3441
  if (!isRecord2(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
3295
3442
  throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
3296
3443
  }
@@ -3334,7 +3481,7 @@ var developmentTeamArgument = (value) => {
3334
3481
  const marketingVersion = options.config.iosVersion;
3335
3482
  if (!marketingVersion)
3336
3483
  throw new TypeError("iOS release builds require mobile.ios.version in absolutejs.config.ts.");
3337
- const manifest = requireManifest(JSON.parse(await readFile5(join11(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
3484
+ const manifest = requireManifest(JSON.parse(await readFile6(join11(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
3338
3485
  if (manifest.appId !== options.config.appId)
3339
3486
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
3340
3487
  const nativeDirectory = join11(options.config.nativeProjectDirectory, "ios");
@@ -3439,97 +3586,6 @@ var init_iosRelease = __esm(() => {
3439
3586
  ]);
3440
3587
  });
3441
3588
 
3442
- // src/mobile/iosPhysicalDeviceTransport.ts
3443
- import { randomUUID as randomUUID2, X509Certificate } from "crypto";
3444
- import { createServer as createServer3 } from "http";
3445
- import {
3446
- connect as connectTcp,
3447
- createServer as createTcpServer,
3448
- isIP
3449
- } from "net";
3450
- import { readFile as readFile6 } from "fs/promises";
3451
- var closeServer = (server) => new Promise((resolve8, reject) => {
3452
- server.close((error) => {
3453
- if (error)
3454
- reject(error);
3455
- else
3456
- resolve8();
3457
- });
3458
- }), listen = (server, port) => new Promise((resolve8, reject) => {
3459
- server.once("error", reject);
3460
- server.listen(port, "0.0.0.0", () => {
3461
- server.off("error", reject);
3462
- const address = server.address();
3463
- if (!address || typeof address === "string") {
3464
- reject(new Error("Could not determine the iOS device helper port."));
3465
- return;
3466
- }
3467
- resolve8(address.port);
3468
- });
3469
- }), findEphemeralPort = async () => {
3470
- const probe = createTcpServer();
3471
- const port = await new Promise((resolve8, reject) => {
3472
- probe.once("error", reject);
3473
- probe.listen(0, "127.0.0.1", () => {
3474
- const address = probe.address();
3475
- if (!address || typeof address === "string") {
3476
- reject(new Error("Could not allocate the iOS CA enrollment port."));
3477
- return;
3478
- }
3479
- resolve8(address.port);
3480
- });
3481
- });
3482
- await closeServer(probe);
3483
- return port;
3484
- }, normalizeAbsoluteIosDeviceHost = (value) => {
3485
- const normalized = value.trim();
3486
- if (!normalized || normalized.length > 253 || /[\0\s/?#]/u.test(normalized))
3487
- throw new TypeError("Physical iOS development requires a valid LAN host.");
3488
- return normalized;
3489
- }, normalizeAbsoluteIosDeviceIdentifier = (value) => {
3490
- const normalized = value.trim();
3491
- if (!normalized || normalized.length > 256 || /[\0\r\n]/u.test(normalized))
3492
- throw new TypeError("--ios-device requires a valid Xcode device identifier or name.");
3493
- return normalized;
3494
- }, urlForHost = (protocol, host, port) => {
3495
- const url = new URL(`${protocol}://localhost:${port}`);
3496
- const normalizedHost = normalizeAbsoluteIosDeviceHost(host);
3497
- url.hostname = isIP(normalizedHost) === 6 ? `[${normalizedHost}]` : normalizedHost;
3498
- return url;
3499
- }, startAbsoluteIosCaEnrollmentServer = async (options) => {
3500
- const certificate = new X509Certificate(await readFile6(options.certificateAuthorityPath));
3501
- const certificateBytes = certificate.raw;
3502
- const token = randomUUID2().replaceAll("-", "");
3503
- const certificatePath = `/${token}/absolutejs-development-ca.cer`;
3504
- const server = createServer3((request, response) => {
3505
- if (request.method !== "GET" || request.url !== certificatePath) {
3506
- response.writeHead(404, {
3507
- "Cache-Control": "no-store",
3508
- "Content-Type": "text/plain; charset=utf-8"
3509
- });
3510
- response.end("Not found.");
3511
- return;
3512
- }
3513
- response.writeHead(200, {
3514
- "Cache-Control": "no-store",
3515
- "Content-Disposition": 'attachment; filename="absolutejs-development-ca.cer"',
3516
- "Content-Length": String(certificateBytes.byteLength),
3517
- "Content-Type": "application/x-x509-ca-cert",
3518
- "X-Content-Type-Options": "nosniff"
3519
- });
3520
- response.end(certificateBytes);
3521
- });
3522
- const port = await findEphemeralPort();
3523
- await listen(server, port);
3524
- const url = urlForHost("http", options.displayHost, port);
3525
- url.pathname = certificatePath;
3526
- return {
3527
- url: url.href,
3528
- close: () => closeServer(server)
3529
- };
3530
- };
3531
- var init_iosPhysicalDeviceTransport = () => {};
3532
-
3533
3589
  // src/mobile/iosSimulatorController.ts
3534
3590
  import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
3535
3591
  import {
@@ -23303,6 +23359,7 @@ init_config();
23303
23359
  init_expoProject();
23304
23360
 
23305
23361
  // src/mobile/expoDevController.ts
23362
+ init_iosPhysicalDeviceTransport();
23306
23363
  import { spawn } from "child_process";
23307
23364
  import { access as access2 } from "fs/promises";
23308
23365
  import { join as join6 } from "path";
@@ -23310,6 +23367,9 @@ var METRO_READY_TIMEOUT_MS = 60000;
23310
23367
  var PROCESS_CLOSE_TIMEOUT_MS = 2000;
23311
23368
  var commandEnvironment = (options) => ({
23312
23369
  ABSOLUTE_EXPO_DEVELOPMENT: "1",
23370
+ ...options.certificateAuthorityPath ? {
23371
+ ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH: options.certificateAuthorityPath
23372
+ } : {},
23313
23373
  ...options.androidOrigin ? { EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN: options.androidOrigin } : {},
23314
23374
  ...options.iosOrigin ? { EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN: options.iosOrigin } : {}
23315
23375
  });
@@ -23327,6 +23387,9 @@ var planAbsoluteExpoDevSession = (config, options) => {
23327
23387
  throw new TypeError("The Expo development controller requires Expo.");
23328
23388
  if (options.platforms.length === 0)
23329
23389
  throw new TypeError("Expo development requires a local target platform.");
23390
+ const secureOrigin = [options.androidOrigin, options.iosOrigin].some((origin) => origin && new URL(origin).protocol === "https:");
23391
+ if (secureOrigin && !options.certificateAuthorityPath)
23392
+ throw new TypeError("Expo HTTPS development requires the AbsoluteJS development CA certificate.");
23330
23393
  const env = commandEnvironment(options);
23331
23394
  const metro = {
23332
23395
  args: [
@@ -23416,6 +23479,21 @@ var waitForExit = (process2) => new Promise((resolve4) => {
23416
23479
  }
23417
23480
  process2.once("exit", (code) => resolve4(code ?? 1));
23418
23481
  });
23482
+ var runUtilityCommand = async (run, command, args, options) => {
23483
+ const child = run(command, args, {
23484
+ cwd: options.cwd,
23485
+ env: process.env,
23486
+ stdio: ["ignore", "pipe", "pipe"]
23487
+ });
23488
+ forwardLines(child, options.log);
23489
+ const abort = () => void stopProcess(child);
23490
+ options.signal?.addEventListener("abort", abort, { once: true });
23491
+ const exitCode = await waitForExit(child);
23492
+ options.signal?.removeEventListener("abort", abort);
23493
+ if (options.signal?.aborted)
23494
+ throw abortError();
23495
+ return exitCode;
23496
+ };
23419
23497
  var startAbsoluteExpoDevSession = async (options) => {
23420
23498
  const plan = planAbsoluteExpoDevSession(options.config, options);
23421
23499
  const executable = options.executable ?? await absoluteExpoExecutable(plan.project);
@@ -23424,6 +23502,16 @@ var startAbsoluteExpoDevSession = async (options) => {
23424
23502
  return;
23425
23503
  });
23426
23504
  const timings = {};
23505
+ let caEnrollmentServer = null;
23506
+ const closeEnrollmentServer = async () => {
23507
+ const server = caEnrollmentServer;
23508
+ caEnrollmentServer = null;
23509
+ await server?.close();
23510
+ };
23511
+ const publishTiming = (phase, durationMs) => {
23512
+ timings[phase] = (timings[phase] ?? 0) + durationMs;
23513
+ options.onPhaseTiming?.({ durationMs, phase });
23514
+ };
23427
23515
  const setState = (state) => {
23428
23516
  options.onStateChange?.(state);
23429
23517
  };
@@ -23524,6 +23612,30 @@ var startAbsoluteExpoDevSession = async (options) => {
23524
23612
  if (exitCode !== 0) {
23525
23613
  throw new Error(`Expo ${platform2} development build exited with status ${exitCode}.`);
23526
23614
  }
23615
+ if (platform2 === "ios" && options.certificateAuthorityPath && options.iosOrigin && new URL(options.iosOrigin).protocol === "https:" && !options.iosDevice) {
23616
+ setState("enrolling-trust");
23617
+ const trustStarted = performance.now();
23618
+ const utilityOptions = {
23619
+ cwd: plan.project,
23620
+ signal: options.signal,
23621
+ log: (line) => line && log(`[ios-trust] ${line}`)
23622
+ };
23623
+ const trustExit = await runUtilityCommand(run, "xcrun", [
23624
+ "simctl",
23625
+ "keychain",
23626
+ "booted",
23627
+ "add-root-cert",
23628
+ options.certificateAuthorityPath
23629
+ ], utilityOptions);
23630
+ if (trustExit !== 0)
23631
+ throw new Error(`Expo iOS Simulator development CA trust exited with status ${trustExit}.`);
23632
+ await runUtilityCommand(run, "xcrun", ["simctl", "terminate", "booted", options.config.appId], utilityOptions);
23633
+ const launchExit = await runUtilityCommand(run, "xcrun", ["simctl", "launch", "booted", options.config.appId], utilityOptions);
23634
+ if (launchExit !== 0)
23635
+ throw new Error(`Expo iOS Simulator relaunch exited with status ${launchExit}.`);
23636
+ log("Installed the AbsoluteJS development CA into the Expo iOS Simulator and relaunched the app.");
23637
+ publishTiming("enrolling-trust", performance.now() - trustStarted);
23638
+ }
23527
23639
  const durationMs = performance.now() - started;
23528
23640
  timings[state] = durationMs;
23529
23641
  options.onPhaseTiming?.({ durationMs, phase: state });
@@ -23535,7 +23647,22 @@ var startAbsoluteExpoDevSession = async (options) => {
23535
23647
  await runNativeCommand(command);
23536
23648
  await runNativeCommands(remaining);
23537
23649
  };
23650
+ const startPhysicalIosEnrollment = async () => {
23651
+ if (!options.iosDevice || !options.certificateAuthorityPath || !options.iosOrigin || new URL(options.iosOrigin).protocol !== "https:") {
23652
+ return;
23653
+ }
23654
+ setState("enrolling-trust");
23655
+ const trustStarted = performance.now();
23656
+ const startEnrollment = options.startCaEnrollmentServer ?? startAbsoluteIosCaEnrollmentServer;
23657
+ caEnrollmentServer = await startEnrollment({
23658
+ certificateAuthorityPath: options.certificateAuthorityPath,
23659
+ displayHost: new URL(options.iosOrigin).hostname
23660
+ });
23661
+ 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.`);
23662
+ publishTiming("enrolling-trust", performance.now() - trustStarted);
23663
+ };
23538
23664
  try {
23665
+ await startPhysicalIosEnrollment();
23539
23666
  await metroPromise;
23540
23667
  const metroMs = performance.now() - metroStarted;
23541
23668
  timings["starting-metro"] = metroMs;
@@ -23552,12 +23679,14 @@ var startAbsoluteExpoDevSession = async (options) => {
23552
23679
  close: async () => {
23553
23680
  options.signal?.removeEventListener("abort", abort);
23554
23681
  await stopProcess(metro);
23682
+ await closeEnrollmentServer();
23555
23683
  setState("closed");
23556
23684
  }
23557
23685
  };
23558
23686
  } catch (error) {
23559
23687
  options.signal?.removeEventListener("abort", abort);
23560
23688
  await stopProcess(metro);
23689
+ await closeEnrollmentServer();
23561
23690
  setState("failed");
23562
23691
  throw error;
23563
23692
  }
@@ -23930,14 +24059,17 @@ var dev = async (serverEntry, configPath2, options = {}) => {
23930
24059
  let resolvedDev;
23931
24060
  let buildDirectory = resolvePath2(process.cwd(), "build");
23932
24061
  let mobileConfig;
24062
+ let normalizedMobileConfig;
23933
24063
  let iosPhysicalServerHost;
23934
24064
  let selectedRemoteMacProfile;
23935
24065
  let expoDevProject;
23936
24066
  try {
23937
24067
  const config = await loadConfig(configPath2);
23938
24068
  mobileConfig = config?.mobile;
23939
- if (mobileConfig)
23940
- installAbsoluteMobileAuthEnvironment(process.cwd(), normalizeAbsoluteMobileConfig(mobileConfig, process.cwd()));
24069
+ if (mobileConfig) {
24070
+ normalizedMobileConfig = normalizeAbsoluteMobileConfig(mobileConfig, process.cwd());
24071
+ installAbsoluteMobileAuthEnvironment(process.cwd(), normalizedMobileConfig);
24072
+ }
23941
24073
  resolvedDev = resolveDevConfig(config?.dev);
23942
24074
  httpsEnabled = resolvedDev.https;
23943
24075
  if (config?.buildDirectory) {
@@ -23974,6 +24106,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
23974
24106
  }
23975
24107
  if (httpsEnabled) {
23976
24108
  const certificateHosts = [
24109
+ ...normalizedMobileConfig?.engine === "expo" && !options.androidDevice && normalizedMobileConfig.platforms.includes("android") ? ["10.0.2.2"] : [],
23977
24110
  ...options.androidDevice ? [mobileReachableHost(resolvedDev.host)] : [],
23978
24111
  ...iosPhysicalServerHost ? [iosPhysicalServerHost] : []
23979
24112
  ];
@@ -23982,13 +24115,10 @@ var dev = async (serverEntry, configPath2, options = {}) => {
23982
24115
  let androidDevProject = null;
23983
24116
  let iosDevProject = null;
23984
24117
  const mobileInteractive = options.mobile !== false && process.env.ABSOLUTE_NO_MOBILE !== "1" && process.stdin.isTTY === true && process.stdout.isTTY === true;
23985
- if (mobileConfig && mobileInteractive) {
24118
+ if (normalizedMobileConfig && mobileInteractive) {
23986
24119
  try {
23987
- const normalized = normalizeAbsoluteMobileConfig(mobileConfig, process.cwd());
24120
+ const normalized = normalizedMobileConfig;
23988
24121
  if (normalized.engine === "expo") {
23989
- if (httpsEnabled) {
23990
- throw new TypeError("Combined Expo development currently requires dev.https: false while debug-only CA projection is completed; productionOrigin remains HTTPS.");
23991
- }
23992
24122
  const generated = await writeAbsoluteExpoProject(normalized, {
23993
24123
  projectRoot: process.cwd()
23994
24124
  });
@@ -24461,7 +24591,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24461
24591
  const expoTelemetryPlatform = (phase) => {
24462
24592
  if (phase.endsWith("android"))
24463
24593
  return "android";
24464
- if (phase.endsWith("ios"))
24594
+ if (phase.endsWith("ios") || phase === "enrolling-trust")
24465
24595
  return "ios";
24466
24596
  return "shared";
24467
24597
  };
@@ -24475,6 +24605,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24475
24605
  expoDevStart = startAbsoluteExpoDevSession({
24476
24606
  androidDevice: options.androidDevice,
24477
24607
  androidOrigin: project.platforms.includes("android") ? absoluteDevOrigin(androidHost) : undefined,
24608
+ certificateAuthorityPath: devCertificateAuthorityPath ?? undefined,
24478
24609
  config: project.mobile,
24479
24610
  executable: project.executable,
24480
24611
  iosDevice: options.iosDevice,