@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.
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/index.js +573 -217
- package/dist/mobile/index.js +311 -76
- package/dist/mobile/index.js.map +6 -6
- package/dist/mobile/remoteMacAgentEntry.js +335 -14
- package/dist/src/mobile/expoDevController.d.ts +12 -5
- package/dist/src/mobile/remoteMacProtocol.d.ts +27 -0
- package/dist/src/mobile/remoteMacWire.d.ts +1 -1
- package/package.json +1 -1
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
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}.${
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
2252
|
-
const manifestSource = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
@@ -4432,7 +4488,7 @@ var init_iosSimulatorController = __esm(() => {
|
|
|
4432
4488
|
});
|
|
4433
4489
|
|
|
4434
4490
|
// src/mobile/remoteMacWire.ts
|
|
4435
|
-
var ABSOLUTE_REMOTE_MAC_EVENT_PREFIX = "ABSOLUTE_REMOTE_MAC\t", ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION =
|
|
4491
|
+
var ABSOLUTE_REMOTE_MAC_EVENT_PREFIX = "ABSOLUTE_REMOTE_MAC\t", ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION = 2;
|
|
4436
4492
|
|
|
4437
4493
|
// src/mobile/remoteMacProtocol.ts
|
|
4438
4494
|
import { createHash as createHash6, randomUUID as randomUUID4 } from "crypto";
|
|
@@ -4448,7 +4504,7 @@ import {
|
|
|
4448
4504
|
resolve as resolvePath,
|
|
4449
4505
|
sep as sep4
|
|
4450
4506
|
} from "path";
|
|
4451
|
-
var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join13(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
|
|
4507
|
+
var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join13(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
|
|
4452
4508
|
format: PROFILE_FORMAT,
|
|
4453
4509
|
profiles: {}
|
|
4454
4510
|
}), loadStore = async (path = defaultProfilePath()) => {
|
|
@@ -4619,7 +4675,11 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4619
4675
|
}
|
|
4620
4676
|
await saveStore(store, profilePath);
|
|
4621
4677
|
return true;
|
|
4622
|
-
}, projectIdentity = (projectRoot, appId) => createHash6("sha256").update(`${resolvePath(projectRoot)}\x00${appId}`).digest("hex").slice(0, 20),
|
|
4678
|
+
}, projectIdentity = (projectRoot, appId) => createHash6("sha256").update(`${resolvePath(projectRoot)}\x00${appId}`).digest("hex").slice(0, 20), createAbsoluteRemoteExpoIosDevProject = (config, projectRoot, profile) => {
|
|
4679
|
+
if (config.engine !== "expo")
|
|
4680
|
+
throw new TypeError("Remote Expo execution requires mobile.engine: expo.");
|
|
4681
|
+
return createAbsoluteRemoteIosDevProject(config, projectRoot, profile);
|
|
4682
|
+
}, createAbsoluteRemoteIosDevProject = (config, projectRoot, profile) => ({
|
|
4623
4683
|
cap: join13(resolvePath(projectRoot), "node_modules", ".bin", "cap"),
|
|
4624
4684
|
config,
|
|
4625
4685
|
nativeDirectory: join13(config.nativeProjectDirectory, "ios"),
|
|
@@ -4706,6 +4766,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4706
4766
|
appId: project.config.appId,
|
|
4707
4767
|
appName: project.config.appName,
|
|
4708
4768
|
bundleDirectory: portableRelativePath(project.projectRoot, project.config.bundleDirectory),
|
|
4769
|
+
engine: project.config.engine,
|
|
4709
4770
|
...project.config.deepLinkScheme || project.config.deepLinkHosts.length > 1 || project.config.appleAppIdPrefix ? {
|
|
4710
4771
|
deepLinks: {
|
|
4711
4772
|
...project.config.deepLinkScheme ? { scheme: project.config.deepLinkScheme } : {},
|
|
@@ -4718,6 +4779,16 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4718
4779
|
}
|
|
4719
4780
|
} : {},
|
|
4720
4781
|
entry: project.config.entry,
|
|
4782
|
+
...project.config.engine === "expo" ? {
|
|
4783
|
+
expo: { sdkVersion: project.config.expoSdkVersion ?? 57 },
|
|
4784
|
+
routes: {
|
|
4785
|
+
default: "web",
|
|
4786
|
+
native: Object.fromEntries(Object.entries(project.config.expoNativeRoutes).map(([route, module]) => [
|
|
4787
|
+
route,
|
|
4788
|
+
portableRelativePath(project.projectRoot, module)
|
|
4789
|
+
]))
|
|
4790
|
+
}
|
|
4791
|
+
} : {},
|
|
4721
4792
|
...project.config.iosVersion ? { ios: { version: project.config.iosVersion } } : {},
|
|
4722
4793
|
nativeProject: {
|
|
4723
4794
|
directory: portableRelativePath(project.projectRoot, project.config.nativeProjectDirectory),
|
|
@@ -4805,7 +4876,30 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4805
4876
|
} finally {
|
|
4806
4877
|
reader.releaseLock();
|
|
4807
4878
|
}
|
|
4808
|
-
},
|
|
4879
|
+
}, startAbsoluteRemoteExpoIosDevSession = async (options) => {
|
|
4880
|
+
const session = await startAbsoluteRemoteDevSession({
|
|
4881
|
+
certificateAuthorityPath: options.certificateAuthorityPath,
|
|
4882
|
+
deviceIdentifier: options.deviceIdentifier,
|
|
4883
|
+
https: options.https,
|
|
4884
|
+
installAgent: options.installAgent,
|
|
4885
|
+
log: options.log,
|
|
4886
|
+
metroPort: options.metroPort,
|
|
4887
|
+
onExpoPhaseTiming: options.onPhaseTiming,
|
|
4888
|
+
onExpoStateChange: options.onStateChange,
|
|
4889
|
+
port: options.port,
|
|
4890
|
+
project: options.project,
|
|
4891
|
+
serverHost: options.serverHost,
|
|
4892
|
+
signal: options.signal,
|
|
4893
|
+
syncProject: options.syncProject,
|
|
4894
|
+
transport: options.transport
|
|
4895
|
+
});
|
|
4896
|
+
return {
|
|
4897
|
+
close: session.close,
|
|
4898
|
+
metroPort: options.metroPort,
|
|
4899
|
+
platforms: ["ios"],
|
|
4900
|
+
timings: session.timings
|
|
4901
|
+
};
|
|
4902
|
+
}, startAbsoluteRemoteIosDevSession = (options) => startAbsoluteRemoteDevSession(options), startAbsoluteRemoteDevSession = async (options) => {
|
|
4809
4903
|
const startedAt = performance.now();
|
|
4810
4904
|
const transport = options.transport ?? defaultTransport;
|
|
4811
4905
|
const installAgent = options.installAgent ?? installAbsoluteRemoteMacAgent;
|
|
@@ -4819,9 +4913,22 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4819
4913
|
const encodedConfig = Buffer.from(JSON.stringify(portableMobileConfig(options.project))).toString("base64url");
|
|
4820
4914
|
const encodedCertificateAuthority = options.certificateAuthorityPath ? (await readFile8(options.certificateAuthorityPath)).toString("base64url") : undefined;
|
|
4821
4915
|
const physicalDevice = options.deviceIdentifier !== undefined;
|
|
4822
|
-
|
|
4823
|
-
if (
|
|
4824
|
-
|
|
4916
|
+
const expo = options.project.config.engine === "expo";
|
|
4917
|
+
if (expo && options.metroPort === undefined)
|
|
4918
|
+
throw new TypeError("Remote Expo execution requires a Metro port.");
|
|
4919
|
+
if (options.metroPort === options.port)
|
|
4920
|
+
throw new TypeError("Expo Metro and AbsoluteJS must use different ports.");
|
|
4921
|
+
const translatedRelayPort = (sourcePort) => sourcePort <= 49151 ? sourcePort + 16384 : sourcePort - 16384;
|
|
4922
|
+
const occupiedRemotePorts = new Set([options.port, options.metroPort].filter((value) => value !== undefined));
|
|
4923
|
+
const reserveRelayPort = (sourcePort) => {
|
|
4924
|
+
let candidate = translatedRelayPort(sourcePort);
|
|
4925
|
+
while (occupiedRemotePorts.has(candidate))
|
|
4926
|
+
candidate = candidate === 65535 ? 1024 : candidate + 1;
|
|
4927
|
+
occupiedRemotePorts.add(candidate);
|
|
4928
|
+
return candidate;
|
|
4929
|
+
};
|
|
4930
|
+
const relayPort = physicalDevice ? reserveRelayPort(options.port) : undefined;
|
|
4931
|
+
const metroRelayPort = physicalDevice && options.metroPort !== undefined ? reserveRelayPort(options.metroPort) : undefined;
|
|
4825
4932
|
if (physicalDevice && !options.serverHost)
|
|
4826
4933
|
throw new Error("Remote physical iOS development requires the Remote Mac LAN host.");
|
|
4827
4934
|
const remoteCommand = [
|
|
@@ -4834,6 +4941,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4834
4941
|
String(options.port),
|
|
4835
4942
|
"--mobile-config",
|
|
4836
4943
|
shellQuote(encodedConfig),
|
|
4944
|
+
...expo && options.metroPort !== undefined ? ["--metro-port", String(options.metroPort)] : [],
|
|
4837
4945
|
...encodedCertificateAuthority ? [
|
|
4838
4946
|
"--certificate-authority",
|
|
4839
4947
|
shellQuote(encodedCertificateAuthority)
|
|
@@ -4845,7 +4953,8 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4845
4953
|
"--server-host",
|
|
4846
4954
|
shellQuote(options.serverHost ?? ""),
|
|
4847
4955
|
"--relay-port",
|
|
4848
|
-
String(relayPort)
|
|
4956
|
+
String(relayPort),
|
|
4957
|
+
...expo && metroRelayPort !== undefined ? ["--metro-relay-port", String(metroRelayPort)] : []
|
|
4849
4958
|
] : []
|
|
4850
4959
|
].join(" ");
|
|
4851
4960
|
const command = [
|
|
@@ -4854,6 +4963,10 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4854
4963
|
"ExitOnForwardFailure=yes",
|
|
4855
4964
|
"-R",
|
|
4856
4965
|
physicalDevice ? `127.0.0.1:${relayPort}:127.0.0.1:${options.port}` : `${options.port}:127.0.0.1:${options.port}`,
|
|
4966
|
+
...expo && options.metroPort !== undefined ? [
|
|
4967
|
+
"-R",
|
|
4968
|
+
physicalDevice ? `127.0.0.1:${metroRelayPort}:127.0.0.1:${options.metroPort}` : `${options.metroPort}:127.0.0.1:${options.metroPort}`
|
|
4969
|
+
] : [],
|
|
4857
4970
|
"/bin/sh -lc",
|
|
4858
4971
|
shellQuote(remoteCommand)
|
|
4859
4972
|
];
|
|
@@ -4879,11 +4992,19 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4879
4992
|
if (event.type === "native-log")
|
|
4880
4993
|
options.nativeLog?.(event.entry);
|
|
4881
4994
|
if (event.type === "state") {
|
|
4882
|
-
(
|
|
4883
|
-
|
|
4995
|
+
if (expo)
|
|
4996
|
+
options.onExpoStateChange?.(event.state);
|
|
4997
|
+
else {
|
|
4998
|
+
state = event.state;
|
|
4999
|
+
options.onStateChange?.(state);
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
if (event.type === "timing") {
|
|
5003
|
+
if (expo)
|
|
5004
|
+
options.onExpoPhaseTiming?.(event);
|
|
5005
|
+
else
|
|
5006
|
+
options.onPhaseTiming?.(event);
|
|
4884
5007
|
}
|
|
4885
|
-
if (event.type === "timing")
|
|
4886
|
-
options.onPhaseTiming?.(event);
|
|
4887
5008
|
if (event.type === "ready") {
|
|
4888
5009
|
ready = event;
|
|
4889
5010
|
resolveReady();
|
|
@@ -4925,9 +5046,26 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4925
5046
|
pending.clear();
|
|
4926
5047
|
return;
|
|
4927
5048
|
});
|
|
4928
|
-
|
|
5049
|
+
try {
|
|
5050
|
+
await readyPromise;
|
|
5051
|
+
} catch (error) {
|
|
5052
|
+
process2.stdin.end();
|
|
5053
|
+
process2.kill();
|
|
5054
|
+
await process2.exited.catch(() => {
|
|
5055
|
+
return;
|
|
5056
|
+
});
|
|
5057
|
+
throw error;
|
|
5058
|
+
}
|
|
4929
5059
|
if (!ready)
|
|
4930
5060
|
throw fatal ?? new Error("Remote Mac did not become ready.");
|
|
5061
|
+
if (ready.engine !== options.project.config.engine) {
|
|
5062
|
+
process2.stdin.end();
|
|
5063
|
+
process2.kill();
|
|
5064
|
+
await process2.exited.catch(() => {
|
|
5065
|
+
return;
|
|
5066
|
+
});
|
|
5067
|
+
throw new Error("Remote Mac executor returned the wrong mobile engine.");
|
|
5068
|
+
}
|
|
4931
5069
|
const totalDuration = performance.now() - startedAt;
|
|
4932
5070
|
let currentReady = {
|
|
4933
5071
|
...ready,
|
|
@@ -4939,11 +5077,11 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4939
5077
|
total: totalDuration
|
|
4940
5078
|
}
|
|
4941
5079
|
};
|
|
4942
|
-
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.`);
|
|
5080
|
+
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.`);
|
|
4943
5081
|
const request = (commandName) => {
|
|
4944
5082
|
const id = randomUUID4();
|
|
4945
5083
|
const response = new Promise((resolve9, reject) => pending.set(id, { reject, resolve: resolve9 }));
|
|
4946
|
-
process2.stdin.write(`${JSON.stringify({ command: commandName, id, v:
|
|
5084
|
+
process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION })}
|
|
4947
5085
|
`);
|
|
4948
5086
|
const flush = async () => {
|
|
4949
5087
|
for (let attempt = 0;attempt < REMOTE_STDIN_FLUSH_ATTEMPTS; attempt++) {
|
|
@@ -4962,13 +5100,24 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4962
5100
|
if (closed)
|
|
4963
5101
|
return;
|
|
4964
5102
|
closed = true;
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
5103
|
+
const timeout = () => new Promise((resolve9) => setTimeout(() => resolve9("timeout"), REMOTE_SESSION_CLOSE_TIMEOUT_MS));
|
|
5104
|
+
await Promise.race([
|
|
5105
|
+
request("close").catch(() => {
|
|
5106
|
+
return;
|
|
5107
|
+
}),
|
|
5108
|
+
timeout()
|
|
5109
|
+
]);
|
|
4968
5110
|
process2.stdin.end();
|
|
4969
|
-
await
|
|
4970
|
-
|
|
4971
|
-
|
|
5111
|
+
const exit = await Promise.race([
|
|
5112
|
+
process2.exited.then(() => "exited").catch(() => "exited"),
|
|
5113
|
+
timeout()
|
|
5114
|
+
]);
|
|
5115
|
+
if (exit === "timeout") {
|
|
5116
|
+
process2.kill();
|
|
5117
|
+
await process2.exited.catch(() => {
|
|
5118
|
+
return;
|
|
5119
|
+
});
|
|
5120
|
+
}
|
|
4972
5121
|
};
|
|
4973
5122
|
const makeSession = () => ({
|
|
4974
5123
|
close,
|
|
@@ -23303,6 +23452,7 @@ init_config();
|
|
|
23303
23452
|
init_expoProject();
|
|
23304
23453
|
|
|
23305
23454
|
// src/mobile/expoDevController.ts
|
|
23455
|
+
init_iosPhysicalDeviceTransport();
|
|
23306
23456
|
import { spawn } from "child_process";
|
|
23307
23457
|
import { access as access2 } from "fs/promises";
|
|
23308
23458
|
import { join as join6 } from "path";
|
|
@@ -23310,8 +23460,12 @@ var METRO_READY_TIMEOUT_MS = 60000;
|
|
|
23310
23460
|
var PROCESS_CLOSE_TIMEOUT_MS = 2000;
|
|
23311
23461
|
var commandEnvironment = (options) => ({
|
|
23312
23462
|
ABSOLUTE_EXPO_DEVELOPMENT: "1",
|
|
23463
|
+
...options.certificateAuthorityPath ? {
|
|
23464
|
+
ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH: options.certificateAuthorityPath
|
|
23465
|
+
} : {},
|
|
23313
23466
|
...options.androidOrigin ? { EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN: options.androidOrigin } : {},
|
|
23314
|
-
...options.iosOrigin ? { EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN: options.iosOrigin } : {}
|
|
23467
|
+
...options.iosOrigin ? { EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN: options.iosOrigin } : {},
|
|
23468
|
+
...options.metroHost ? { REACT_NATIVE_PACKAGER_HOSTNAME: options.metroHost } : {}
|
|
23315
23469
|
});
|
|
23316
23470
|
var absoluteExpoExecutable = async (project) => {
|
|
23317
23471
|
const executable = join6(project, "node_modules", ".bin", "expo");
|
|
@@ -23325,8 +23479,11 @@ var absoluteExpoExecutable = async (project) => {
|
|
|
23325
23479
|
var planAbsoluteExpoDevSession = (config, options) => {
|
|
23326
23480
|
if (config.engine !== "expo")
|
|
23327
23481
|
throw new TypeError("The Expo development controller requires Expo.");
|
|
23328
|
-
if (options.platforms.length === 0)
|
|
23329
|
-
throw new TypeError("Expo
|
|
23482
|
+
if (options.platforms.length === 0 && options.metro === "external")
|
|
23483
|
+
throw new TypeError("External Expo execution requires a target platform.");
|
|
23484
|
+
const secureOrigin = [options.androidOrigin, options.iosOrigin].some((origin) => origin && new URL(origin).protocol === "https:");
|
|
23485
|
+
if (secureOrigin && !options.certificateAuthorityPath)
|
|
23486
|
+
throw new TypeError("Expo HTTPS development requires the AbsoluteJS development CA certificate.");
|
|
23330
23487
|
const env = commandEnvironment(options);
|
|
23331
23488
|
const metro = {
|
|
23332
23489
|
args: [
|
|
@@ -23340,7 +23497,7 @@ var planAbsoluteExpoDevSession = (config, options) => {
|
|
|
23340
23497
|
env,
|
|
23341
23498
|
role: "metro"
|
|
23342
23499
|
};
|
|
23343
|
-
const prepare = {
|
|
23500
|
+
const prepare = options.platforms.length === 0 ? undefined : {
|
|
23344
23501
|
args: [
|
|
23345
23502
|
"prebuild",
|
|
23346
23503
|
"--clean",
|
|
@@ -23368,7 +23525,11 @@ var planAbsoluteExpoDevSession = (config, options) => {
|
|
|
23368
23525
|
return command;
|
|
23369
23526
|
});
|
|
23370
23527
|
return {
|
|
23371
|
-
commands: [
|
|
23528
|
+
commands: [
|
|
23529
|
+
...prepare ? [prepare] : [],
|
|
23530
|
+
...options.metro === "external" ? [] : [metro],
|
|
23531
|
+
...native
|
|
23532
|
+
],
|
|
23372
23533
|
metroPort: options.metroPort,
|
|
23373
23534
|
project: config.nativeProjectDirectory
|
|
23374
23535
|
};
|
|
@@ -23416,6 +23577,21 @@ var waitForExit = (process2) => new Promise((resolve4) => {
|
|
|
23416
23577
|
}
|
|
23417
23578
|
process2.once("exit", (code) => resolve4(code ?? 1));
|
|
23418
23579
|
});
|
|
23580
|
+
var runUtilityCommand = async (run, command, args, options) => {
|
|
23581
|
+
const child = run(command, args, {
|
|
23582
|
+
cwd: options.cwd,
|
|
23583
|
+
env: process.env,
|
|
23584
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
23585
|
+
});
|
|
23586
|
+
forwardLines(child, options.log);
|
|
23587
|
+
const abort = () => void stopProcess(child);
|
|
23588
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
23589
|
+
const exitCode = await waitForExit(child);
|
|
23590
|
+
options.signal?.removeEventListener("abort", abort);
|
|
23591
|
+
if (options.signal?.aborted)
|
|
23592
|
+
throw abortError();
|
|
23593
|
+
return exitCode;
|
|
23594
|
+
};
|
|
23419
23595
|
var startAbsoluteExpoDevSession = async (options) => {
|
|
23420
23596
|
const plan = planAbsoluteExpoDevSession(options.config, options);
|
|
23421
23597
|
const executable = options.executable ?? await absoluteExpoExecutable(plan.project);
|
|
@@ -23424,6 +23600,16 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
23424
23600
|
return;
|
|
23425
23601
|
});
|
|
23426
23602
|
const timings = {};
|
|
23603
|
+
let caEnrollmentServer = null;
|
|
23604
|
+
const closeEnrollmentServer = async () => {
|
|
23605
|
+
const server = caEnrollmentServer;
|
|
23606
|
+
caEnrollmentServer = null;
|
|
23607
|
+
await server?.close();
|
|
23608
|
+
};
|
|
23609
|
+
const publishTiming = (phase, durationMs) => {
|
|
23610
|
+
timings[phase] = (timings[phase] ?? 0) + durationMs;
|
|
23611
|
+
options.onPhaseTiming?.({ durationMs, phase });
|
|
23612
|
+
};
|
|
23427
23613
|
const setState = (state) => {
|
|
23428
23614
|
options.onStateChange?.(state);
|
|
23429
23615
|
};
|
|
@@ -23432,47 +23618,48 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
23432
23618
|
const prepareCommand = plan.commands.find((command) => command.role === "native-prepare");
|
|
23433
23619
|
const metroCommand = plan.commands.find((command) => command.role === "metro");
|
|
23434
23620
|
const nativeCommands = plan.commands.filter((command) => command.role === "native-build");
|
|
23435
|
-
|
|
23436
|
-
|
|
23437
|
-
|
|
23438
|
-
|
|
23439
|
-
|
|
23440
|
-
|
|
23441
|
-
|
|
23442
|
-
|
|
23443
|
-
|
|
23444
|
-
|
|
23445
|
-
|
|
23446
|
-
|
|
23447
|
-
|
|
23448
|
-
|
|
23449
|
-
|
|
23450
|
-
|
|
23451
|
-
|
|
23452
|
-
|
|
23453
|
-
|
|
23454
|
-
|
|
23455
|
-
|
|
23456
|
-
|
|
23457
|
-
|
|
23458
|
-
|
|
23459
|
-
|
|
23460
|
-
|
|
23461
|
-
|
|
23462
|
-
|
|
23463
|
-
|
|
23464
|
-
phase: "preparing-native"
|
|
23465
|
-
});
|
|
23466
|
-
setState("starting-metro");
|
|
23621
|
+
const runPrepareCommand = async (command) => {
|
|
23622
|
+
setState("preparing-native");
|
|
23623
|
+
const prepareStarted = performance.now();
|
|
23624
|
+
const prepareProcess = run(executable, command.args, {
|
|
23625
|
+
cwd: plan.project,
|
|
23626
|
+
env: { ...process.env, ...command.env },
|
|
23627
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
23628
|
+
});
|
|
23629
|
+
forwardLines(prepareProcess, (line) => {
|
|
23630
|
+
if (line)
|
|
23631
|
+
log(`[prebuild] ${line}`);
|
|
23632
|
+
});
|
|
23633
|
+
const abortPrepare = () => void stopProcess(prepareProcess);
|
|
23634
|
+
options.signal?.addEventListener("abort", abortPrepare, { once: true });
|
|
23635
|
+
const prepareExit = await waitForExit(prepareProcess);
|
|
23636
|
+
options.signal?.removeEventListener("abort", abortPrepare);
|
|
23637
|
+
if (options.signal?.aborted)
|
|
23638
|
+
throw abortError();
|
|
23639
|
+
if (prepareExit !== 0) {
|
|
23640
|
+
setState("failed");
|
|
23641
|
+
throw new Error(`Expo native preparation exited with status ${prepareExit}.`);
|
|
23642
|
+
}
|
|
23643
|
+
publishTiming("preparing-native", performance.now() - prepareStarted);
|
|
23644
|
+
};
|
|
23645
|
+
if (prepareCommand)
|
|
23646
|
+
await runPrepareCommand(prepareCommand);
|
|
23647
|
+
const managedMetro = metroCommand !== undefined;
|
|
23648
|
+
if (managedMetro)
|
|
23649
|
+
setState("starting-metro");
|
|
23467
23650
|
const metroStarted = performance.now();
|
|
23468
|
-
const metro = run(executable, metroCommand.args, {
|
|
23651
|
+
const metro = metroCommand ? run(executable, metroCommand.args, {
|
|
23469
23652
|
cwd: plan.project,
|
|
23470
23653
|
env: { ...process.env, ...metroCommand.env },
|
|
23471
23654
|
stdio: ["ignore", "pipe", "pipe"]
|
|
23472
|
-
});
|
|
23655
|
+
}) : undefined;
|
|
23473
23656
|
let metroReady = false;
|
|
23474
23657
|
let resolveMetro;
|
|
23475
23658
|
const metroPromise = new Promise((resolve4, reject) => {
|
|
23659
|
+
if (!metro) {
|
|
23660
|
+
resolve4();
|
|
23661
|
+
return;
|
|
23662
|
+
}
|
|
23476
23663
|
const timeout = setTimeout(() => {
|
|
23477
23664
|
reject(new Error("Expo Metro did not become ready within 60 seconds."));
|
|
23478
23665
|
}, METRO_READY_TIMEOUT_MS);
|
|
@@ -23487,15 +23674,19 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
23487
23674
|
}
|
|
23488
23675
|
});
|
|
23489
23676
|
});
|
|
23490
|
-
|
|
23491
|
-
|
|
23492
|
-
|
|
23493
|
-
|
|
23494
|
-
metroReady
|
|
23495
|
-
|
|
23496
|
-
|
|
23497
|
-
|
|
23498
|
-
|
|
23677
|
+
if (metro)
|
|
23678
|
+
forwardLines(metro, (line) => {
|
|
23679
|
+
if (line)
|
|
23680
|
+
log(`[metro] ${line}`);
|
|
23681
|
+
if (!metroReady && /(?:Waiting on|Metro waiting on|Dev server ready)/iu.test(line)) {
|
|
23682
|
+
metroReady = true;
|
|
23683
|
+
resolveMetro?.();
|
|
23684
|
+
}
|
|
23685
|
+
});
|
|
23686
|
+
const abort = () => {
|
|
23687
|
+
if (metro)
|
|
23688
|
+
stopProcess(metro);
|
|
23689
|
+
};
|
|
23499
23690
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
23500
23691
|
const runNativeCommand = async (command) => {
|
|
23501
23692
|
if (options.signal?.aborted)
|
|
@@ -23524,6 +23715,30 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
23524
23715
|
if (exitCode !== 0) {
|
|
23525
23716
|
throw new Error(`Expo ${platform2} development build exited with status ${exitCode}.`);
|
|
23526
23717
|
}
|
|
23718
|
+
if (platform2 === "ios" && options.certificateAuthorityPath && options.iosOrigin && new URL(options.iosOrigin).protocol === "https:" && !options.iosDevice) {
|
|
23719
|
+
setState("enrolling-trust");
|
|
23720
|
+
const trustStarted = performance.now();
|
|
23721
|
+
const utilityOptions = {
|
|
23722
|
+
cwd: plan.project,
|
|
23723
|
+
signal: options.signal,
|
|
23724
|
+
log: (line) => line && log(`[ios-trust] ${line}`)
|
|
23725
|
+
};
|
|
23726
|
+
const trustExit = await runUtilityCommand(run, "xcrun", [
|
|
23727
|
+
"simctl",
|
|
23728
|
+
"keychain",
|
|
23729
|
+
"booted",
|
|
23730
|
+
"add-root-cert",
|
|
23731
|
+
options.certificateAuthorityPath
|
|
23732
|
+
], utilityOptions);
|
|
23733
|
+
if (trustExit !== 0)
|
|
23734
|
+
throw new Error(`Expo iOS Simulator development CA trust exited with status ${trustExit}.`);
|
|
23735
|
+
await runUtilityCommand(run, "xcrun", ["simctl", "terminate", "booted", options.config.appId], utilityOptions);
|
|
23736
|
+
const launchExit = await runUtilityCommand(run, "xcrun", ["simctl", "launch", "booted", options.config.appId], utilityOptions);
|
|
23737
|
+
if (launchExit !== 0)
|
|
23738
|
+
throw new Error(`Expo iOS Simulator relaunch exited with status ${launchExit}.`);
|
|
23739
|
+
log("Installed the AbsoluteJS development CA into the Expo iOS Simulator and relaunched the app.");
|
|
23740
|
+
publishTiming("enrolling-trust", performance.now() - trustStarted);
|
|
23741
|
+
}
|
|
23527
23742
|
const durationMs = performance.now() - started;
|
|
23528
23743
|
timings[state] = durationMs;
|
|
23529
23744
|
options.onPhaseTiming?.({ durationMs, phase: state });
|
|
@@ -23535,14 +23750,25 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
23535
23750
|
await runNativeCommand(command);
|
|
23536
23751
|
await runNativeCommands(remaining);
|
|
23537
23752
|
};
|
|
23753
|
+
const startPhysicalIosEnrollment = async () => {
|
|
23754
|
+
if (!options.iosDevice || !options.certificateAuthorityPath || !options.iosOrigin || new URL(options.iosOrigin).protocol !== "https:") {
|
|
23755
|
+
return;
|
|
23756
|
+
}
|
|
23757
|
+
setState("enrolling-trust");
|
|
23758
|
+
const trustStarted = performance.now();
|
|
23759
|
+
const startEnrollment = options.startCaEnrollmentServer ?? startAbsoluteIosCaEnrollmentServer;
|
|
23760
|
+
caEnrollmentServer = await startEnrollment({
|
|
23761
|
+
certificateAuthorityPath: options.certificateAuthorityPath,
|
|
23762
|
+
displayHost: new URL(options.iosOrigin).hostname
|
|
23763
|
+
});
|
|
23764
|
+
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.`);
|
|
23765
|
+
publishTiming("enrolling-trust", performance.now() - trustStarted);
|
|
23766
|
+
};
|
|
23538
23767
|
try {
|
|
23768
|
+
await startPhysicalIosEnrollment();
|
|
23539
23769
|
await metroPromise;
|
|
23540
|
-
|
|
23541
|
-
|
|
23542
|
-
options.onPhaseTiming?.({
|
|
23543
|
-
durationMs: metroMs,
|
|
23544
|
-
phase: "starting-metro"
|
|
23545
|
-
});
|
|
23770
|
+
if (managedMetro)
|
|
23771
|
+
publishTiming("starting-metro", performance.now() - metroStarted);
|
|
23546
23772
|
await runNativeCommands(nativeCommands);
|
|
23547
23773
|
setState("ready");
|
|
23548
23774
|
return {
|
|
@@ -23551,13 +23777,17 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
23551
23777
|
timings,
|
|
23552
23778
|
close: async () => {
|
|
23553
23779
|
options.signal?.removeEventListener("abort", abort);
|
|
23554
|
-
|
|
23780
|
+
if (metro)
|
|
23781
|
+
await stopProcess(metro);
|
|
23782
|
+
await closeEnrollmentServer();
|
|
23555
23783
|
setState("closed");
|
|
23556
23784
|
}
|
|
23557
23785
|
};
|
|
23558
23786
|
} catch (error) {
|
|
23559
23787
|
options.signal?.removeEventListener("abort", abort);
|
|
23560
|
-
|
|
23788
|
+
if (metro)
|
|
23789
|
+
await stopProcess(metro);
|
|
23790
|
+
await closeEnrollmentServer();
|
|
23561
23791
|
setState("failed");
|
|
23562
23792
|
throw error;
|
|
23563
23793
|
}
|
|
@@ -23930,14 +24160,17 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
23930
24160
|
let resolvedDev;
|
|
23931
24161
|
let buildDirectory = resolvePath2(process.cwd(), "build");
|
|
23932
24162
|
let mobileConfig;
|
|
24163
|
+
let normalizedMobileConfig;
|
|
23933
24164
|
let iosPhysicalServerHost;
|
|
23934
24165
|
let selectedRemoteMacProfile;
|
|
23935
24166
|
let expoDevProject;
|
|
23936
24167
|
try {
|
|
23937
24168
|
const config = await loadConfig(configPath2);
|
|
23938
24169
|
mobileConfig = config?.mobile;
|
|
23939
|
-
if (mobileConfig)
|
|
23940
|
-
|
|
24170
|
+
if (mobileConfig) {
|
|
24171
|
+
normalizedMobileConfig = normalizeAbsoluteMobileConfig(mobileConfig, process.cwd());
|
|
24172
|
+
installAbsoluteMobileAuthEnvironment(process.cwd(), normalizedMobileConfig);
|
|
24173
|
+
}
|
|
23941
24174
|
resolvedDev = resolveDevConfig(config?.dev);
|
|
23942
24175
|
httpsEnabled = resolvedDev.https;
|
|
23943
24176
|
if (config?.buildDirectory) {
|
|
@@ -23960,9 +24193,6 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
23960
24193
|
throw new TypeError("--ios-device requires ios in mobile.platforms.");
|
|
23961
24194
|
}
|
|
23962
24195
|
if (options.iosDevice) {
|
|
23963
|
-
if (mobileConfig?.engine === "expo" && detectAbsoluteMobileHost() !== "macos") {
|
|
23964
|
-
throw new Error("Expo physical iOS development currently requires local macOS; Expo Remote Mac execution is the next adapter milestone.");
|
|
23965
|
-
}
|
|
23966
24196
|
if (detectAbsoluteMobileHost() === "macos")
|
|
23967
24197
|
iosPhysicalServerHost = mobileReachableHost(resolvedDev.host);
|
|
23968
24198
|
else {
|
|
@@ -23974,6 +24204,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
23974
24204
|
}
|
|
23975
24205
|
if (httpsEnabled) {
|
|
23976
24206
|
const certificateHosts = [
|
|
24207
|
+
...normalizedMobileConfig?.engine === "expo" && !options.androidDevice && normalizedMobileConfig.platforms.includes("android") ? ["10.0.2.2"] : [],
|
|
23977
24208
|
...options.androidDevice ? [mobileReachableHost(resolvedDev.host)] : [],
|
|
23978
24209
|
...iosPhysicalServerHost ? [iosPhysicalServerHost] : []
|
|
23979
24210
|
];
|
|
@@ -23982,13 +24213,10 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
23982
24213
|
let androidDevProject = null;
|
|
23983
24214
|
let iosDevProject = null;
|
|
23984
24215
|
const mobileInteractive = options.mobile !== false && process.env.ABSOLUTE_NO_MOBILE !== "1" && process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
23985
|
-
if (
|
|
24216
|
+
if (normalizedMobileConfig && mobileInteractive) {
|
|
23986
24217
|
try {
|
|
23987
|
-
const normalized =
|
|
24218
|
+
const normalized = normalizedMobileConfig;
|
|
23988
24219
|
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
24220
|
const generated = await writeAbsoluteExpoProject(normalized, {
|
|
23993
24221
|
projectRoot: process.cwd()
|
|
23994
24222
|
});
|
|
@@ -24008,6 +24236,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
24008
24236
|
}
|
|
24009
24237
|
}
|
|
24010
24238
|
const platforms = [];
|
|
24239
|
+
let remoteIos;
|
|
24011
24240
|
if (normalized.platforms.includes("android")) {
|
|
24012
24241
|
const target = options.androidDevice ? "device" : "emulator";
|
|
24013
24242
|
let ready = androidToolchainReady(await inspectAbsoluteMobileToolchain(), target);
|
|
@@ -24023,7 +24252,14 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
24023
24252
|
}
|
|
24024
24253
|
if (normalized.platforms.includes("ios")) {
|
|
24025
24254
|
if (detectAbsoluteMobileHost() !== "macos") {
|
|
24026
|
-
|
|
24255
|
+
const remote = selectedRemoteMacProfile ?? await getAbsoluteRemoteMacProfile();
|
|
24256
|
+
if (!remote) {
|
|
24257
|
+
console.log(cliTag("\x1B[33m", "Expo iOS target skipped. Pair a Mac with `absolute mobile pair mac <name> <user@host>`."));
|
|
24258
|
+
} else {
|
|
24259
|
+
selectedRemoteMacProfile = remote;
|
|
24260
|
+
remoteIos = createAbsoluteRemoteExpoIosDevProject(normalized, process.cwd(), remote);
|
|
24261
|
+
console.log(cliTag("\x1B[35m", `Using remote Mac ${remote.name} for Expo iOS development.`));
|
|
24262
|
+
}
|
|
24027
24263
|
} else {
|
|
24028
24264
|
const target = options.iosDevice ? "device" : "simulator";
|
|
24029
24265
|
let ready = iosToolchainReady(await inspectAbsoluteMobileToolchain(), target);
|
|
@@ -24038,13 +24274,17 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
24038
24274
|
platforms.push("ios");
|
|
24039
24275
|
}
|
|
24040
24276
|
}
|
|
24041
|
-
if (platforms.length > 0) {
|
|
24277
|
+
if (platforms.length > 0 || remoteIos) {
|
|
24042
24278
|
expoDevProject = {
|
|
24043
24279
|
executable: await absoluteExpoExecutable(generated.path),
|
|
24044
24280
|
mobile: normalized,
|
|
24045
|
-
platforms
|
|
24281
|
+
platforms,
|
|
24282
|
+
...remoteIos ? { remoteIos } : {}
|
|
24046
24283
|
};
|
|
24047
|
-
console.log(cliTag("\x1B[35m", `Expo development build queued for ${
|
|
24284
|
+
console.log(cliTag("\x1B[35m", `Expo development build queued for ${[
|
|
24285
|
+
...platforms,
|
|
24286
|
+
...remoteIos ? ["ios (Remote Mac)"] : []
|
|
24287
|
+
].join(" + ")}; Metro and native launch will start with the Bun server.`));
|
|
24048
24288
|
}
|
|
24049
24289
|
} else if (normalized.platforms.includes("android")) {
|
|
24050
24290
|
const androidTarget = options.androidDevice ? "device" : "emulator";
|
|
@@ -24461,7 +24701,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
24461
24701
|
const expoTelemetryPlatform = (phase) => {
|
|
24462
24702
|
if (phase.endsWith("android"))
|
|
24463
24703
|
return "android";
|
|
24464
|
-
if (phase.endsWith("ios"))
|
|
24704
|
+
if (phase.endsWith("ios") || phase === "enrolling-trust")
|
|
24465
24705
|
return "ios";
|
|
24466
24706
|
return "shared";
|
|
24467
24707
|
};
|
|
@@ -24471,53 +24711,166 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
24471
24711
|
return;
|
|
24472
24712
|
}
|
|
24473
24713
|
const androidHost = options.androidDevice ? mobileReachableHost(resolvedDev.host) : "10.0.2.2";
|
|
24474
|
-
const iosHost = options.iosDevice ? mobileReachableHost(resolvedDev.host) : "localhost";
|
|
24475
|
-
|
|
24476
|
-
|
|
24477
|
-
|
|
24714
|
+
const iosHost = options.iosDevice ? iosPhysicalServerHost ?? mobileReachableHost(resolvedDev.host) : "localhost";
|
|
24715
|
+
const androidOrigin = project.platforms.includes("android") ? absoluteDevOrigin(androidHost) : undefined;
|
|
24716
|
+
const iosOrigin = project.platforms.includes("ios") || project.remoteIos ? absoluteDevOrigin(iosHost) : undefined;
|
|
24717
|
+
let metroSession;
|
|
24718
|
+
let localNativeSession;
|
|
24719
|
+
let remoteSession;
|
|
24720
|
+
const metroStart = startAbsoluteExpoDevSession({
|
|
24721
|
+
androidOrigin,
|
|
24722
|
+
certificateAuthorityPath: devCertificateAuthorityPath ?? undefined,
|
|
24478
24723
|
config: project.mobile,
|
|
24479
24724
|
executable: project.executable,
|
|
24480
|
-
|
|
24481
|
-
iosOrigin: project.platforms.includes("ios") ? absoluteDevOrigin(iosHost) : undefined,
|
|
24725
|
+
iosOrigin,
|
|
24482
24726
|
metroPort: expoMetroPort,
|
|
24483
|
-
platforms:
|
|
24727
|
+
platforms: [],
|
|
24484
24728
|
signal: expoDevAbort.signal,
|
|
24485
24729
|
log: (message) => printNativeOutput(cliTag("\x1B[35m", `Expo ${message}`)),
|
|
24486
24730
|
onPhaseTiming: ({ durationMs, phase }) => {
|
|
24487
24731
|
sendTelemetryEvent("mobile:expo-dev-phase", {
|
|
24488
24732
|
durationMs: Math.round(durationMs),
|
|
24733
|
+
host: detectAbsoluteMobileHost(),
|
|
24489
24734
|
phase,
|
|
24490
24735
|
platform: expoTelemetryPlatform(phase),
|
|
24491
24736
|
provider: "expo"
|
|
24492
24737
|
});
|
|
24493
24738
|
},
|
|
24494
24739
|
onStateChange: (state) => {
|
|
24495
|
-
expoDevState = state;
|
|
24496
24740
|
if (state === "ready" || state === "closed")
|
|
24497
24741
|
return;
|
|
24742
|
+
expoDevState = state;
|
|
24498
24743
|
printNativeOutput(cliTag("\x1B[35m", `Expo development: ${state}.`));
|
|
24499
24744
|
}
|
|
24500
|
-
}).then(
|
|
24745
|
+
}).then((session) => {
|
|
24746
|
+
metroSession = session;
|
|
24747
|
+
return session;
|
|
24748
|
+
});
|
|
24749
|
+
const localNativeStart = metroStart.then(async () => {
|
|
24750
|
+
if (project.platforms.length === 0)
|
|
24751
|
+
return;
|
|
24752
|
+
const session = await startAbsoluteExpoDevSession({
|
|
24753
|
+
androidDevice: options.androidDevice,
|
|
24754
|
+
androidOrigin,
|
|
24755
|
+
certificateAuthorityPath: devCertificateAuthorityPath ?? undefined,
|
|
24756
|
+
config: project.mobile,
|
|
24757
|
+
executable: project.executable,
|
|
24758
|
+
iosDevice: options.iosDevice,
|
|
24759
|
+
iosOrigin,
|
|
24760
|
+
metro: "external",
|
|
24761
|
+
metroPort: expoMetroPort,
|
|
24762
|
+
platforms: project.platforms,
|
|
24763
|
+
signal: expoDevAbort.signal,
|
|
24764
|
+
log: (message) => printNativeOutput(cliTag("\x1B[35m", `Expo ${message}`)),
|
|
24765
|
+
onPhaseTiming: ({ durationMs, phase }) => {
|
|
24766
|
+
sendTelemetryEvent("mobile:expo-dev-phase", {
|
|
24767
|
+
durationMs: Math.round(durationMs),
|
|
24768
|
+
host: detectAbsoluteMobileHost(),
|
|
24769
|
+
phase,
|
|
24770
|
+
platform: expoTelemetryPlatform(phase),
|
|
24771
|
+
provider: "expo"
|
|
24772
|
+
});
|
|
24773
|
+
},
|
|
24774
|
+
onStateChange: (state) => {
|
|
24775
|
+
if (state === "ready" || state === "closed")
|
|
24776
|
+
return;
|
|
24777
|
+
expoDevState = state;
|
|
24778
|
+
printNativeOutput(cliTag("\x1B[35m", `Expo development: ${state}.`));
|
|
24779
|
+
}
|
|
24780
|
+
});
|
|
24781
|
+
localNativeSession = session;
|
|
24782
|
+
return session;
|
|
24783
|
+
});
|
|
24784
|
+
const remoteIosProject = project.remoteIos;
|
|
24785
|
+
const remoteStart = remoteIosProject ? metroStart.then(async () => {
|
|
24786
|
+
const session = await startAbsoluteRemoteExpoIosDevSession({
|
|
24787
|
+
certificateAuthorityPath: devCertificateAuthorityPath ?? undefined,
|
|
24788
|
+
deviceIdentifier: options.iosDevice,
|
|
24789
|
+
https: httpsEnabled,
|
|
24790
|
+
metroPort: expoMetroPort,
|
|
24791
|
+
port,
|
|
24792
|
+
project: remoteIosProject,
|
|
24793
|
+
serverHost: options.iosDevice ? iosPhysicalServerHost : undefined,
|
|
24794
|
+
signal: expoDevAbort.signal,
|
|
24795
|
+
log: (message) => printNativeOutput(cliTag("\x1B[35m", `Expo [remote-ios] ${message}`)),
|
|
24796
|
+
onPhaseTiming: ({ durationMs, phase }) => {
|
|
24797
|
+
sendTelemetryEvent("mobile:expo-dev-phase", {
|
|
24798
|
+
durationMs: Math.round(durationMs),
|
|
24799
|
+
host: "remote-macos",
|
|
24800
|
+
phase,
|
|
24801
|
+
platform: expoTelemetryPlatform(phase),
|
|
24802
|
+
provider: "expo"
|
|
24803
|
+
});
|
|
24804
|
+
},
|
|
24805
|
+
onStateChange: (state) => {
|
|
24806
|
+
if (state === "ready" || state === "closed")
|
|
24807
|
+
return;
|
|
24808
|
+
expoDevState = state;
|
|
24809
|
+
printNativeOutput(cliTag("\x1B[35m", `Expo Remote Mac iOS: ${state}.`));
|
|
24810
|
+
}
|
|
24811
|
+
});
|
|
24812
|
+
remoteSession = session;
|
|
24813
|
+
return session;
|
|
24814
|
+
}) : Promise.resolve(undefined);
|
|
24815
|
+
expoDevStart = Promise.all([metroStart, localNativeStart, remoteStart]).then(async ([metro, localNative, remote]) => {
|
|
24816
|
+
const session = {
|
|
24817
|
+
metroPort: metro.metroPort,
|
|
24818
|
+
platforms: [
|
|
24819
|
+
...new Set([
|
|
24820
|
+
...localNative?.platforms ?? [],
|
|
24821
|
+
...remote?.platforms ?? []
|
|
24822
|
+
])
|
|
24823
|
+
],
|
|
24824
|
+
timings: {
|
|
24825
|
+
...metro.timings,
|
|
24826
|
+
...localNative?.timings,
|
|
24827
|
+
...remote?.timings
|
|
24828
|
+
},
|
|
24829
|
+
close: async () => {
|
|
24830
|
+
await Promise.all([
|
|
24831
|
+
metro.close(),
|
|
24832
|
+
localNative?.close(),
|
|
24833
|
+
remote?.close()
|
|
24834
|
+
]);
|
|
24835
|
+
}
|
|
24836
|
+
};
|
|
24501
24837
|
if (cleaning) {
|
|
24502
24838
|
await session.close();
|
|
24503
24839
|
} else {
|
|
24504
24840
|
expoDevSession = session;
|
|
24841
|
+
expoDevState = "ready";
|
|
24505
24842
|
sendTelemetryEvent("mobile:expo-dev-ready", {
|
|
24506
24843
|
metroPort: session.metroPort,
|
|
24507
24844
|
platforms: session.platforms,
|
|
24508
24845
|
provider: "expo",
|
|
24846
|
+
remoteIos: project.remoteIos !== undefined,
|
|
24509
24847
|
timings: session.timings
|
|
24510
24848
|
});
|
|
24511
24849
|
printNativeOutput(cliTag("\x1B[35m", `Expo ${session.platforms.join(" + ")} connected; native Fast Refresh uses Metro on ${session.metroPort}, and AbsoluteJS pages use HMR on ${port}.`));
|
|
24512
24850
|
}
|
|
24513
24851
|
}).catch((error) => {
|
|
24852
|
+
expoDevAbort.abort();
|
|
24853
|
+
Promise.all([
|
|
24854
|
+
metroSession?.close().catch(() => {
|
|
24855
|
+
return;
|
|
24856
|
+
}),
|
|
24857
|
+
localNativeSession?.close().catch(() => {
|
|
24858
|
+
return;
|
|
24859
|
+
}),
|
|
24860
|
+
remoteSession?.close().catch(() => {
|
|
24861
|
+
return;
|
|
24862
|
+
})
|
|
24863
|
+
]);
|
|
24514
24864
|
expoDevState = "failed";
|
|
24515
24865
|
if (cleaning && error instanceof Error && error.name === "AbortError") {
|
|
24516
24866
|
return;
|
|
24517
24867
|
}
|
|
24518
24868
|
sendTelemetryEvent("mobile:expo-dev-failed", {
|
|
24519
24869
|
phase: expoDevState,
|
|
24520
|
-
platforms:
|
|
24870
|
+
platforms: [
|
|
24871
|
+
...project.platforms,
|
|
24872
|
+
...project.remoteIos ? ["ios"] : []
|
|
24873
|
+
],
|
|
24521
24874
|
provider: "expo"
|
|
24522
24875
|
});
|
|
24523
24876
|
console.error(cliTag("\x1B[31m", `Expo development failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
@@ -25055,7 +25408,10 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
25055
25408
|
console.log(cliTag("\x1B[35m", `iOS target: ${target}; HMR port ${port}.`));
|
|
25056
25409
|
}
|
|
25057
25410
|
if (expoDevProject) {
|
|
25058
|
-
console.log(cliTag("\x1B[35m", `Expo targets: ${
|
|
25411
|
+
console.log(cliTag("\x1B[35m", `Expo targets: ${[
|
|
25412
|
+
...expoDevProject.platforms,
|
|
25413
|
+
...expoDevProject.remoteIos ? ["ios (Remote Mac)"] : []
|
|
25414
|
+
].join(", ")}; state ${expoDevState}; Metro ${expoMetroPort ?? "not allocated"}; AbsoluteJS HMR ${port}.`));
|
|
25059
25415
|
}
|
|
25060
25416
|
},
|
|
25061
25417
|
relaunchDevice: async () => {
|