@absolutejs/absolute 0.20.0-beta.33 → 0.20.0-beta.34
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 +481 -144
- package/dist/mobile/index.js +429 -93
- package/dist/mobile/index.js.map +7 -6
- package/dist/mobile/remoteMacAgentEntry.js +14 -14
- package/dist/src/cli/scripts/dev.d.ts +1 -0
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/iosPhysicalDeviceTransport.d.ts +16 -0
- package/dist/src/mobile/iosSimulatorController.d.ts +12 -1
- package/dist/src/mobile/remoteMacProtocol.d.ts +3 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2844,17 +2844,109 @@ var init_iosRelease = __esm(() => {
|
|
|
2844
2844
|
]);
|
|
2845
2845
|
});
|
|
2846
2846
|
|
|
2847
|
+
// src/mobile/iosPhysicalDeviceTransport.ts
|
|
2848
|
+
import { randomUUID as randomUUID2, X509Certificate } from "crypto";
|
|
2849
|
+
import { createServer as createServer3 } from "http";
|
|
2850
|
+
import {
|
|
2851
|
+
connect as connectTcp,
|
|
2852
|
+
createServer as createTcpServer,
|
|
2853
|
+
isIP
|
|
2854
|
+
} from "net";
|
|
2855
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
2856
|
+
var closeServer = (server) => new Promise((resolve7, reject) => {
|
|
2857
|
+
server.close((error) => {
|
|
2858
|
+
if (error)
|
|
2859
|
+
reject(error);
|
|
2860
|
+
else
|
|
2861
|
+
resolve7();
|
|
2862
|
+
});
|
|
2863
|
+
}), listen = (server, port) => new Promise((resolve7, reject) => {
|
|
2864
|
+
server.once("error", reject);
|
|
2865
|
+
server.listen(port, "0.0.0.0", () => {
|
|
2866
|
+
server.off("error", reject);
|
|
2867
|
+
const address = server.address();
|
|
2868
|
+
if (!address || typeof address === "string") {
|
|
2869
|
+
reject(new Error("Could not determine the iOS device helper port."));
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
resolve7(address.port);
|
|
2873
|
+
});
|
|
2874
|
+
}), findEphemeralPort = async () => {
|
|
2875
|
+
const probe = createTcpServer();
|
|
2876
|
+
const port = await new Promise((resolve7, reject) => {
|
|
2877
|
+
probe.once("error", reject);
|
|
2878
|
+
probe.listen(0, "127.0.0.1", () => {
|
|
2879
|
+
const address = probe.address();
|
|
2880
|
+
if (!address || typeof address === "string") {
|
|
2881
|
+
reject(new Error("Could not allocate the iOS CA enrollment port."));
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
resolve7(address.port);
|
|
2885
|
+
});
|
|
2886
|
+
});
|
|
2887
|
+
await closeServer(probe);
|
|
2888
|
+
return port;
|
|
2889
|
+
}, normalizeAbsoluteIosDeviceHost = (value) => {
|
|
2890
|
+
const normalized = value.trim();
|
|
2891
|
+
if (!normalized || normalized.length > 253 || /[\0\s/?#]/u.test(normalized))
|
|
2892
|
+
throw new TypeError("Physical iOS development requires a valid LAN host.");
|
|
2893
|
+
return normalized;
|
|
2894
|
+
}, normalizeAbsoluteIosDeviceIdentifier = (value) => {
|
|
2895
|
+
const normalized = value.trim();
|
|
2896
|
+
if (!normalized || normalized.length > 256 || /[\0\r\n]/u.test(normalized))
|
|
2897
|
+
throw new TypeError("--ios-device requires a valid Xcode device identifier or name.");
|
|
2898
|
+
return normalized;
|
|
2899
|
+
}, urlForHost = (protocol, host, port) => {
|
|
2900
|
+
const url = new URL(`${protocol}://localhost:${port}`);
|
|
2901
|
+
const normalizedHost = normalizeAbsoluteIosDeviceHost(host);
|
|
2902
|
+
url.hostname = isIP(normalizedHost) === 6 ? `[${normalizedHost}]` : normalizedHost;
|
|
2903
|
+
return url;
|
|
2904
|
+
}, startAbsoluteIosCaEnrollmentServer = async (options) => {
|
|
2905
|
+
const certificate = new X509Certificate(await readFile5(options.certificateAuthorityPath));
|
|
2906
|
+
const certificateBytes = certificate.raw;
|
|
2907
|
+
const token = randomUUID2().replaceAll("-", "");
|
|
2908
|
+
const certificatePath = `/${token}/absolutejs-development-ca.cer`;
|
|
2909
|
+
const server = createServer3((request, response) => {
|
|
2910
|
+
if (request.method !== "GET" || request.url !== certificatePath) {
|
|
2911
|
+
response.writeHead(404, {
|
|
2912
|
+
"Cache-Control": "no-store",
|
|
2913
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
2914
|
+
});
|
|
2915
|
+
response.end("Not found.");
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
response.writeHead(200, {
|
|
2919
|
+
"Cache-Control": "no-store",
|
|
2920
|
+
"Content-Disposition": 'attachment; filename="absolutejs-development-ca.cer"',
|
|
2921
|
+
"Content-Length": String(certificateBytes.byteLength),
|
|
2922
|
+
"Content-Type": "application/x-x509-ca-cert",
|
|
2923
|
+
"X-Content-Type-Options": "nosniff"
|
|
2924
|
+
});
|
|
2925
|
+
response.end(certificateBytes);
|
|
2926
|
+
});
|
|
2927
|
+
const port = await findEphemeralPort();
|
|
2928
|
+
await listen(server, port);
|
|
2929
|
+
const url = urlForHost("http", options.displayHost, port);
|
|
2930
|
+
url.pathname = certificatePath;
|
|
2931
|
+
return {
|
|
2932
|
+
url: url.href,
|
|
2933
|
+
close: () => closeServer(server)
|
|
2934
|
+
};
|
|
2935
|
+
};
|
|
2936
|
+
var init_iosPhysicalDeviceTransport = () => {};
|
|
2937
|
+
|
|
2847
2938
|
// src/mobile/iosSimulatorController.ts
|
|
2848
|
-
import { createHash as createHash4, randomUUID as
|
|
2939
|
+
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
2849
2940
|
import {
|
|
2850
2941
|
access as access5,
|
|
2851
2942
|
copyFile as copyFile3,
|
|
2852
2943
|
mkdir as mkdir4,
|
|
2853
|
-
readFile as
|
|
2944
|
+
readFile as readFile6,
|
|
2854
2945
|
rename as rename4,
|
|
2855
2946
|
rm as rm4,
|
|
2856
2947
|
writeFile as writeFile4
|
|
2857
2948
|
} from "fs/promises";
|
|
2949
|
+
import { isIP as isIP2 } from "net";
|
|
2858
2950
|
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join10, relative as relative4, resolve as resolve7, sep as sep3 } from "path";
|
|
2859
2951
|
var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000, BOOT_POLL_MS = 1000, DEV_JOURNAL_FORMAT2 = 1, NATIVE_CACHE_FORMAT2 = 1, isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), pathExists4 = async (path) => {
|
|
2860
2952
|
try {
|
|
@@ -2965,7 +3057,24 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
2965
3057
|
options.certificateAuthorityPath
|
|
2966
3058
|
], "iOS Simulator development CA trust", run, { signal: options.signal });
|
|
2967
3059
|
log("Installed the AbsoluteJS development CA into this iOS Simulator trust store.");
|
|
2968
|
-
},
|
|
3060
|
+
}, iosLaunchCommand = (project, udid, physical) => physical ? [
|
|
3061
|
+
project.xcrun,
|
|
3062
|
+
"devicectl",
|
|
3063
|
+
"device",
|
|
3064
|
+
"process",
|
|
3065
|
+
"launch",
|
|
3066
|
+
"--terminate-existing",
|
|
3067
|
+
"--device",
|
|
3068
|
+
udid,
|
|
3069
|
+
project.config.appId
|
|
3070
|
+
] : [
|
|
3071
|
+
project.xcrun,
|
|
3072
|
+
"simctl",
|
|
3073
|
+
"launch",
|
|
3074
|
+
"--terminate-running-process",
|
|
3075
|
+
udid,
|
|
3076
|
+
project.config.appId
|
|
3077
|
+
], requireCapturedSuccess = (result, label) => {
|
|
2969
3078
|
if (result.exitCode !== 0) {
|
|
2970
3079
|
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
2971
3080
|
}
|
|
@@ -3088,7 +3197,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3088
3197
|
await rm4(paths.root, { force: true, recursive: true });
|
|
3089
3198
|
return false;
|
|
3090
3199
|
}
|
|
3091
|
-
const journal = await
|
|
3200
|
+
const journal = await readFile6(paths.journal, "utf8").then((source) => parseJournal2(JSON.parse(source))).catch(() => null);
|
|
3092
3201
|
if (!journal || !isInside2(projectRoot, journal.nativeConfigPath) || !isInside2(projectRoot, journal.infoPath) || !isInside2(paths.root, journal.configBackupPath) || !isInside2(paths.root, journal.infoBackupPath)) {
|
|
3093
3202
|
throw new Error(`Refusing unsafe or invalid iOS dev journal at ${paths.journal}.`);
|
|
3094
3203
|
}
|
|
@@ -3117,14 +3226,14 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3117
3226
|
<key>NSAllowsArbitraryLoads</key>
|
|
3118
3227
|
<true/>
|
|
3119
3228
|
</dict>`);
|
|
3120
|
-
}, writeDevProjection = async (project, port, https) => {
|
|
3229
|
+
}, writeDevProjection = async (project, port, https, serverHost = "localhost") => {
|
|
3121
3230
|
const paths = journalPaths2(project.projectRoot);
|
|
3122
3231
|
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
3123
3232
|
const nativeConfigPath = join10(project.nativeDirectory, "App", "App", "capacitor.config.json");
|
|
3124
3233
|
const infoPath = join10(project.nativeDirectory, "App", "App", "Info.plist");
|
|
3125
3234
|
const [configSource, infoSource] = await Promise.all([
|
|
3126
|
-
|
|
3127
|
-
|
|
3235
|
+
readFile6(nativeConfigPath, "utf8"),
|
|
3236
|
+
readFile6(infoPath, "utf8")
|
|
3128
3237
|
]);
|
|
3129
3238
|
const parsed = JSON.parse(configSource);
|
|
3130
3239
|
if (!isRecord3(parsed))
|
|
@@ -3146,6 +3255,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3146
3255
|
flag: "wx"
|
|
3147
3256
|
});
|
|
3148
3257
|
const developmentUrl = new URL(`${https ? "https" : "http"}://localhost:${port}${project.config.entry}`);
|
|
3258
|
+
developmentUrl.hostname = isIP2(serverHost) === 6 ? `[${serverHost}]` : serverHost;
|
|
3149
3259
|
developmentUrl.searchParams.set("__absolute_target", "capacitor-ios");
|
|
3150
3260
|
const existingServer = parsed.server;
|
|
3151
3261
|
parsed.server = {
|
|
@@ -3173,9 +3283,9 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3173
3283
|
String(identity)
|
|
3174
3284
|
]))
|
|
3175
3285
|
};
|
|
3176
|
-
}, readNativeCache2 = (projectRoot) =>
|
|
3286
|
+
}, readNativeCache2 = (projectRoot) => readFile6(nativeCachePath2(projectRoot), "utf8").then((source) => parseNativeCache2(JSON.parse(source))).catch(() => null), writeNativeCache2 = async (projectRoot, cache) => {
|
|
3177
3287
|
const destination = nativeCachePath2(projectRoot);
|
|
3178
|
-
const temporary = `${destination}.${process.pid}.${
|
|
3288
|
+
const temporary = `${destination}.${process.pid}.${randomUUID3()}.tmp`;
|
|
3179
3289
|
await mkdir4(dirname5(destination), { recursive: true });
|
|
3180
3290
|
try {
|
|
3181
3291
|
await writeFile4(temporary, `${JSON.stringify(cache, null, "\t")}
|
|
@@ -3257,6 +3367,29 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3257
3367
|
"app"
|
|
3258
3368
|
]);
|
|
3259
3369
|
return result.exitCode === 0 && result.stdout.trim() ? result.stdout.trim() : undefined;
|
|
3370
|
+
}, validatePhysicalIosDevice = (project, identifier, capture) => {
|
|
3371
|
+
const result = capture([
|
|
3372
|
+
project.xcrun,
|
|
3373
|
+
"devicectl",
|
|
3374
|
+
"device",
|
|
3375
|
+
"info",
|
|
3376
|
+
"details",
|
|
3377
|
+
"--device",
|
|
3378
|
+
identifier
|
|
3379
|
+
]);
|
|
3380
|
+
if (result.exitCode !== 0)
|
|
3381
|
+
throw new Error(`Physical iOS device ${JSON.stringify(identifier)} is unavailable: ${result.stderr.trim() || result.stdout.trim() || "pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode."}`);
|
|
3382
|
+
}, physicalIosAppIsInstalled = (project, identifier, capture) => {
|
|
3383
|
+
const result = capture([
|
|
3384
|
+
project.xcrun,
|
|
3385
|
+
"devicectl",
|
|
3386
|
+
"device",
|
|
3387
|
+
"info",
|
|
3388
|
+
"apps",
|
|
3389
|
+
"--device",
|
|
3390
|
+
identifier
|
|
3391
|
+
]);
|
|
3392
|
+
return result.exitCode === 0 && result.stdout.includes(project.config.appId);
|
|
3260
3393
|
}, buildIosDebugApp = async (project, udid, fingerprint, run, signal) => {
|
|
3261
3394
|
const derivedDataPath = join10(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash4("sha256").update(project.config.appId).digest("hex").slice(0, 16));
|
|
3262
3395
|
await mkdir4(derivedDataPath, { recursive: true });
|
|
@@ -3278,6 +3411,59 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3278
3411
|
if (!await pathExists4(appPath))
|
|
3279
3412
|
throw new Error(`Xcode did not produce the simulator app at ${appPath}.`);
|
|
3280
3413
|
return appPath;
|
|
3414
|
+
}, buildPhysicalIosDebugApp = async (project, identifier, run, signal) => {
|
|
3415
|
+
const derivedDataPath = join10(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash4("sha256").update(project.config.appId).digest("hex").slice(0, 16));
|
|
3416
|
+
await mkdir4(derivedDataPath, { recursive: true });
|
|
3417
|
+
await requireSuccess2([
|
|
3418
|
+
project.xcodebuild,
|
|
3419
|
+
"-workspace",
|
|
3420
|
+
join10(project.nativeDirectory, "App", "App.xcworkspace"),
|
|
3421
|
+
"-scheme",
|
|
3422
|
+
"App",
|
|
3423
|
+
"-configuration",
|
|
3424
|
+
"Debug",
|
|
3425
|
+
"-destination",
|
|
3426
|
+
`platform=iOS,id=${identifier}`,
|
|
3427
|
+
"-derivedDataPath",
|
|
3428
|
+
derivedDataPath,
|
|
3429
|
+
"-allowProvisioningUpdates",
|
|
3430
|
+
"build"
|
|
3431
|
+
], "iOS physical-device build (configure automatic signing and a Development Team in Xcode if this is the first run)", run, { cwd: project.nativeDirectory, signal });
|
|
3432
|
+
const appPath = join10(derivedDataPath, "Build", "Products", "Debug-iphoneos", "App.app");
|
|
3433
|
+
if (!await pathExists4(appPath))
|
|
3434
|
+
throw new Error(`Xcode did not produce the physical-device app at ${appPath}.`);
|
|
3435
|
+
return appPath;
|
|
3436
|
+
}, ensurePhysicalIosDebugApp = async (options) => {
|
|
3437
|
+
const cacheHit = options.cache?.appId === options.project.config.appId && options.cache.fingerprint === options.fingerprint && options.cache.installations[options.identifier] === options.fingerprint && physicalIosAppIsInstalled(options.project, options.identifier, options.capture);
|
|
3438
|
+
if (cacheHit) {
|
|
3439
|
+
options.log(`iOS native app is unchanged on the selected physical device; skipped Xcode build and install.`);
|
|
3440
|
+
return true;
|
|
3441
|
+
}
|
|
3442
|
+
options.log("iOS native inputs changed or the physical-device install is stale; rebuilding.");
|
|
3443
|
+
options.transition("building");
|
|
3444
|
+
const appPath = await buildPhysicalIosDebugApp(options.project, options.identifier, options.run, options.signal);
|
|
3445
|
+
throwIfAborted2(options.signal);
|
|
3446
|
+
options.transition("installing");
|
|
3447
|
+
await requireSuccess2([
|
|
3448
|
+
options.project.xcrun,
|
|
3449
|
+
"devicectl",
|
|
3450
|
+
"device",
|
|
3451
|
+
"install",
|
|
3452
|
+
"app",
|
|
3453
|
+
"--device",
|
|
3454
|
+
options.identifier,
|
|
3455
|
+
appPath
|
|
3456
|
+
], "iOS physical-device app installation", options.run, { signal: options.signal });
|
|
3457
|
+
await writeNativeCache2(options.project.projectRoot, {
|
|
3458
|
+
appId: options.project.config.appId,
|
|
3459
|
+
fingerprint: options.fingerprint,
|
|
3460
|
+
format: NATIVE_CACHE_FORMAT2,
|
|
3461
|
+
installations: {
|
|
3462
|
+
...options.cache?.appId === options.project.config.appId ? options.cache.installations : {},
|
|
3463
|
+
[options.identifier]: options.fingerprint
|
|
3464
|
+
}
|
|
3465
|
+
}).catch((error) => options.log(`iOS native cache could not be saved: ${error instanceof Error ? error.message : String(error)}`));
|
|
3466
|
+
return false;
|
|
3281
3467
|
}, ensureIosDebugApp = async (options) => {
|
|
3282
3468
|
const installed = installedAppIdentity(options.project, options.udid, options.capture);
|
|
3283
3469
|
const cacheHit = options.cache?.appId === options.project.config.appId && options.cache.fingerprint === options.fingerprint && installed !== undefined && options.cache.installations[options.udid] === installed;
|
|
@@ -3317,7 +3503,18 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3317
3503
|
if (!options.nativeLog)
|
|
3318
3504
|
return null;
|
|
3319
3505
|
const start = options.startNativeLogs ?? defaultStartNativeLogs;
|
|
3320
|
-
|
|
3506
|
+
const command = options.deviceIdentifier ? [
|
|
3507
|
+
project.xcrun,
|
|
3508
|
+
"devicectl",
|
|
3509
|
+
"device",
|
|
3510
|
+
"process",
|
|
3511
|
+
"launch",
|
|
3512
|
+
"--console",
|
|
3513
|
+
"--terminate-existing",
|
|
3514
|
+
"--device",
|
|
3515
|
+
udid,
|
|
3516
|
+
project.config.appId
|
|
3517
|
+
] : [
|
|
3321
3518
|
project.xcrun,
|
|
3322
3519
|
"simctl",
|
|
3323
3520
|
"spawn",
|
|
@@ -3330,7 +3527,8 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3330
3527
|
"debug",
|
|
3331
3528
|
"--predicate",
|
|
3332
3529
|
'process == "App"'
|
|
3333
|
-
]
|
|
3530
|
+
];
|
|
3531
|
+
return start(command, { signal: options.signal }, (line) => {
|
|
3334
3532
|
const entry = parseAbsoluteIosLogLine(line);
|
|
3335
3533
|
if (entry)
|
|
3336
3534
|
options.nativeLog?.(entry);
|
|
@@ -3340,12 +3538,13 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3340
3538
|
return duration === undefined ? null : `${label} ${getDurationString(duration)}`;
|
|
3341
3539
|
}).filter((value) => value !== null).join(", "), prepareAbsoluteIosDevProject = async (config, options) => {
|
|
3342
3540
|
if (detectAbsoluteMobileHost() !== "macos")
|
|
3343
|
-
throw new Error("iOS
|
|
3541
|
+
throw new Error("iOS development requires macOS and Xcode.");
|
|
3542
|
+
const target = options.target ?? "simulator";
|
|
3344
3543
|
const projectRoot = resolve7(options.projectRoot);
|
|
3345
3544
|
const checks = await inspectAbsoluteMobileToolchain({ host: "macos" });
|
|
3346
|
-
const failed = checks.filter((check) => check.platform === "ios" && (check.status === "fail" || check.status === "warn"));
|
|
3545
|
+
const failed = checks.filter((check) => check.platform === "ios" && !(target === "device" && check.id === "ios.runtime") && (check.status === "fail" || check.status === "warn"));
|
|
3347
3546
|
if (failed.length > 0)
|
|
3348
|
-
throw new Error(`iOS
|
|
3547
|
+
throw new Error(`iOS ${target} development is not ready: ${failed.map(({ label }) => label).join(", ")}.`);
|
|
3349
3548
|
const xcrun = checks.find((check) => check.id === "ios.xcrun")?.path;
|
|
3350
3549
|
const xcodebuild = checks.find((check) => check.id === "ios.xcodebuild")?.path;
|
|
3351
3550
|
if (!xcrun || !xcodebuild)
|
|
@@ -3375,8 +3574,53 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3375
3574
|
xcodebuild,
|
|
3376
3575
|
xcrun
|
|
3377
3576
|
};
|
|
3577
|
+
}, preparePhysicalIosTarget = async (options) => {
|
|
3578
|
+
options.transition("connecting");
|
|
3579
|
+
validatePhysicalIosDevice(options.project, options.deviceIdentifier, options.capture);
|
|
3580
|
+
if (!options.startOptions.https)
|
|
3581
|
+
return {
|
|
3582
|
+
caEnrollmentServer: null,
|
|
3583
|
+
startedSimulator: false,
|
|
3584
|
+
udid: options.deviceIdentifier
|
|
3585
|
+
};
|
|
3586
|
+
if (!options.startOptions.certificateAuthorityPath)
|
|
3587
|
+
throw new Error("Physical iOS HTTPS development requires the AbsoluteJS development CA certificate.");
|
|
3588
|
+
options.transition("enrolling-trust");
|
|
3589
|
+
const startEnrollment = options.startOptions.startCaEnrollmentServer ?? startAbsoluteIosCaEnrollmentServer;
|
|
3590
|
+
const caEnrollmentServer = await startEnrollment({
|
|
3591
|
+
certificateAuthorityPath: options.startOptions.certificateAuthorityPath,
|
|
3592
|
+
displayHost: options.serverHost
|
|
3593
|
+
});
|
|
3594
|
+
options.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.`);
|
|
3595
|
+
return {
|
|
3596
|
+
caEnrollmentServer,
|
|
3597
|
+
startedSimulator: false,
|
|
3598
|
+
udid: options.deviceIdentifier
|
|
3599
|
+
};
|
|
3600
|
+
}, prepareIosSimulatorTarget = async (options) => {
|
|
3601
|
+
options.transition("booting");
|
|
3602
|
+
const managed = await ensureManagedSimulator(options.project, options.capture);
|
|
3603
|
+
const { device } = managed;
|
|
3604
|
+
const startedSimulator = managed.created || device.state !== "Booted";
|
|
3605
|
+
bootSimulator(options.project, device, options.capture);
|
|
3606
|
+
options.spawn([
|
|
3607
|
+
"open",
|
|
3608
|
+
"-a",
|
|
3609
|
+
"Simulator",
|
|
3610
|
+
"--args",
|
|
3611
|
+
"-CurrentDeviceUDID",
|
|
3612
|
+
device.udid
|
|
3613
|
+
]);
|
|
3614
|
+
options.transition("connecting");
|
|
3615
|
+
await waitForBootedSimulator(options.project, device.udid, options.capture, options.sleep, options.startOptions.signal);
|
|
3616
|
+
await requireSuccess2([options.project.xcrun, "simctl", "bootstatus", device.udid, "-b"], "iOS simulator boot readiness", options.run, { signal: options.startOptions.signal });
|
|
3617
|
+
await trustIosSimulatorDevelopmentCa(options.startOptions, device.udid, options.run, options.log);
|
|
3618
|
+
return { caEnrollmentServer: null, startedSimulator, udid: device.udid };
|
|
3378
3619
|
}, startAbsoluteIosDevSession = async (options) => {
|
|
3379
3620
|
const { project } = options;
|
|
3621
|
+
const deviceIdentifier = options.deviceIdentifier ? normalizeAbsoluteIosDeviceIdentifier(options.deviceIdentifier) : undefined;
|
|
3622
|
+
const targetKind = deviceIdentifier ? "device" : "simulator";
|
|
3623
|
+
const serverHost = deviceIdentifier ? normalizeAbsoluteIosDeviceHost(options.serverHost ?? "") : "localhost";
|
|
3380
3624
|
const capture = options.capture ?? defaultCapture3;
|
|
3381
3625
|
const run = options.run ?? defaultRun3;
|
|
3382
3626
|
const sleep = options.sleep ?? Bun.sleep;
|
|
@@ -3404,6 +3648,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3404
3648
|
options.onStateChange?.(next);
|
|
3405
3649
|
};
|
|
3406
3650
|
let nativeLogs = null;
|
|
3651
|
+
let caEnrollmentServer = null;
|
|
3407
3652
|
const closeLogs = async () => {
|
|
3408
3653
|
const stream = nativeLogs;
|
|
3409
3654
|
nativeLogs = null;
|
|
@@ -3411,39 +3656,66 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3411
3656
|
return;
|
|
3412
3657
|
});
|
|
3413
3658
|
};
|
|
3659
|
+
const relaunchTarget = async (udid) => {
|
|
3660
|
+
if (deviceIdentifier && options.nativeLog) {
|
|
3661
|
+
await closeLogs();
|
|
3662
|
+
nativeLogs = attachNativeLogs(project, udid, options);
|
|
3663
|
+
return;
|
|
3664
|
+
}
|
|
3665
|
+
await requireSuccess2(iosLaunchCommand(project, udid, deviceIdentifier !== undefined), "iOS app relaunch", run, { signal: options.signal });
|
|
3666
|
+
};
|
|
3414
3667
|
try {
|
|
3415
3668
|
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
3416
3669
|
throwIfAborted2(options.signal);
|
|
3417
3670
|
transition("syncing");
|
|
3418
3671
|
await requireSuccess2([project.cap, "sync", "ios"], "Capacitor iOS synchronization", run, { cwd: project.projectRoot, signal: options.signal });
|
|
3419
3672
|
transition("configuring");
|
|
3420
|
-
await writeDevProjection(project, options.port, options.https === true);
|
|
3673
|
+
await writeDevProjection(project, options.port, options.https === true, serverHost);
|
|
3421
3674
|
throwIfAborted2(options.signal);
|
|
3422
3675
|
const fingerprintStartedAt = performance.now();
|
|
3423
3676
|
const fingerprintPromise = fingerprintAbsoluteIosDevProject(project).then((fingerprint2) => {
|
|
3424
3677
|
timings.fingerprinting = performance.now() - fingerprintStartedAt;
|
|
3425
3678
|
return fingerprint2;
|
|
3426
3679
|
});
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3680
|
+
const target = deviceIdentifier ? await preparePhysicalIosTarget({
|
|
3681
|
+
capture,
|
|
3682
|
+
deviceIdentifier,
|
|
3683
|
+
log,
|
|
3684
|
+
project,
|
|
3685
|
+
serverHost,
|
|
3686
|
+
startOptions: options,
|
|
3687
|
+
transition
|
|
3688
|
+
}) : await prepareIosSimulatorTarget({
|
|
3689
|
+
capture,
|
|
3690
|
+
log,
|
|
3691
|
+
project,
|
|
3692
|
+
run,
|
|
3693
|
+
sleep,
|
|
3694
|
+
spawn,
|
|
3695
|
+
startOptions: options,
|
|
3696
|
+
transition
|
|
3697
|
+
});
|
|
3698
|
+
const {
|
|
3699
|
+
caEnrollmentServer: targetCaEnrollmentServer,
|
|
3700
|
+
startedSimulator,
|
|
3701
|
+
udid
|
|
3702
|
+
} = target;
|
|
3703
|
+
caEnrollmentServer = targetCaEnrollmentServer;
|
|
3443
3704
|
const fingerprint = await fingerprintPromise;
|
|
3444
3705
|
transition("checking-native");
|
|
3445
|
-
const
|
|
3446
|
-
|
|
3706
|
+
const cache = await readNativeCache2(project.projectRoot);
|
|
3707
|
+
const nativeCacheHit = deviceIdentifier ? await ensurePhysicalIosDebugApp({
|
|
3708
|
+
cache,
|
|
3709
|
+
capture,
|
|
3710
|
+
fingerprint,
|
|
3711
|
+
identifier: deviceIdentifier,
|
|
3712
|
+
log,
|
|
3713
|
+
project,
|
|
3714
|
+
run,
|
|
3715
|
+
signal: options.signal,
|
|
3716
|
+
transition
|
|
3717
|
+
}) : await ensureIosDebugApp({
|
|
3718
|
+
cache,
|
|
3447
3719
|
capture,
|
|
3448
3720
|
fingerprint,
|
|
3449
3721
|
log,
|
|
@@ -3451,24 +3723,18 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3451
3723
|
run,
|
|
3452
3724
|
signal: options.signal,
|
|
3453
3725
|
transition,
|
|
3454
|
-
udid
|
|
3726
|
+
udid
|
|
3455
3727
|
});
|
|
3456
3728
|
throwIfAborted2(options.signal);
|
|
3457
3729
|
if (options.nativeLog)
|
|
3458
3730
|
transition("streaming-logs");
|
|
3459
|
-
nativeLogs = attachNativeLogs(project,
|
|
3731
|
+
nativeLogs = attachNativeLogs(project, udid, options);
|
|
3460
3732
|
transition("launching");
|
|
3461
|
-
|
|
3462
|
-
project.
|
|
3463
|
-
"simctl",
|
|
3464
|
-
"launch",
|
|
3465
|
-
"--terminate-running-process",
|
|
3466
|
-
device.udid,
|
|
3467
|
-
project.config.appId
|
|
3468
|
-
], "iOS app launch", run, { signal: options.signal });
|
|
3733
|
+
if (!deviceIdentifier || !nativeLogs)
|
|
3734
|
+
await requireSuccess2(iosLaunchCommand(project, udid, deviceIdentifier !== undefined), "iOS app launch", run, { signal: options.signal });
|
|
3469
3735
|
transition("ready");
|
|
3470
3736
|
timings.total = performance.now() - startedAt;
|
|
3471
|
-
log(`iOS
|
|
3737
|
+
log(`iOS ${targetKind} connected with HMR on port ${options.port} in ${getDurationString(timings.total)} (${nativeCacheHit ? "native cache hit" : "native build installed"}).`);
|
|
3472
3738
|
log(`iOS startup: ${timingSummary(timings)}.`);
|
|
3473
3739
|
let closed = false;
|
|
3474
3740
|
const close = async () => {
|
|
@@ -3477,6 +3743,10 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3477
3743
|
closed = true;
|
|
3478
3744
|
transition("closing");
|
|
3479
3745
|
await closeLogs();
|
|
3746
|
+
await caEnrollmentServer?.close().catch(() => {
|
|
3747
|
+
return;
|
|
3748
|
+
});
|
|
3749
|
+
caEnrollmentServer = null;
|
|
3480
3750
|
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
3481
3751
|
transition("closed");
|
|
3482
3752
|
};
|
|
@@ -3484,8 +3754,9 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3484
3754
|
close,
|
|
3485
3755
|
nativeCacheHit,
|
|
3486
3756
|
startedSimulator,
|
|
3757
|
+
targetKind,
|
|
3487
3758
|
timings: { ...timings },
|
|
3488
|
-
udid
|
|
3759
|
+
udid,
|
|
3489
3760
|
rebuild: async () => {
|
|
3490
3761
|
if (closed)
|
|
3491
3762
|
throw new Error("iOS development session is closed.");
|
|
@@ -3498,22 +3769,17 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3498
3769
|
throw new Error("iOS development session is closed.");
|
|
3499
3770
|
transition("launching");
|
|
3500
3771
|
try {
|
|
3501
|
-
await
|
|
3502
|
-
project.xcrun,
|
|
3503
|
-
"simctl",
|
|
3504
|
-
"launch",
|
|
3505
|
-
"--terminate-running-process",
|
|
3506
|
-
device.udid,
|
|
3507
|
-
project.config.appId
|
|
3508
|
-
], "iOS app relaunch", run, { signal: options.signal });
|
|
3772
|
+
await relaunchTarget(udid);
|
|
3509
3773
|
transition("ready");
|
|
3510
|
-
log(`iOS app relaunched on ${
|
|
3774
|
+
log(`iOS app relaunched on the selected ${targetKind}.`);
|
|
3511
3775
|
} catch (error) {
|
|
3512
3776
|
transition("failed");
|
|
3513
3777
|
throw error;
|
|
3514
3778
|
}
|
|
3515
3779
|
},
|
|
3516
3780
|
screenshot: async (destination) => {
|
|
3781
|
+
if (deviceIdentifier)
|
|
3782
|
+
throw new Error("Physical iOS screenshots are captured in Xcode Device Hub; the CLI never records a device screen automatically.");
|
|
3517
3783
|
const resolved = resolve7(project.projectRoot, destination);
|
|
3518
3784
|
if (!isInside2(project.projectRoot, resolved))
|
|
3519
3785
|
throw new Error("iOS screenshot destination must remain inside the project.");
|
|
@@ -3522,7 +3788,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3522
3788
|
project.xcrun,
|
|
3523
3789
|
"simctl",
|
|
3524
3790
|
"io",
|
|
3525
|
-
|
|
3791
|
+
udid,
|
|
3526
3792
|
"screenshot",
|
|
3527
3793
|
resolved
|
|
3528
3794
|
], "iOS simulator screenshot", run, { signal: options.signal });
|
|
@@ -3535,6 +3801,9 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3535
3801
|
} catch (error) {
|
|
3536
3802
|
transition("failed");
|
|
3537
3803
|
await closeLogs();
|
|
3804
|
+
await caEnrollmentServer?.close().catch(() => {
|
|
3805
|
+
return;
|
|
3806
|
+
});
|
|
3538
3807
|
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
3539
3808
|
throw error;
|
|
3540
3809
|
}
|
|
@@ -3543,6 +3812,7 @@ var init_iosSimulatorController = __esm(() => {
|
|
|
3543
3812
|
init_emulatorDoctor();
|
|
3544
3813
|
init_capacitorProject();
|
|
3545
3814
|
init_iosRelease();
|
|
3815
|
+
init_iosPhysicalDeviceTransport();
|
|
3546
3816
|
init_getDurationString();
|
|
3547
3817
|
SECRET_VALUE = /((?:authorization|cookie|password|secret|token|oauth[_-]?code)\s*[:=]\s*)([^\s,;]+)/giu;
|
|
3548
3818
|
BEARER_VALUE = new RegExp(String.raw`\bBearer\s+[A-Za-z0-9._~+/-]+=*`, "giu");
|
|
@@ -3554,6 +3824,7 @@ var init_iosSimulatorController = __esm(() => {
|
|
|
3554
3824
|
["fingerprinting", "fingerprint"],
|
|
3555
3825
|
["booting", "simulator"],
|
|
3556
3826
|
["connecting", "device ready"],
|
|
3827
|
+
["enrolling-trust", "HTTPS trust"],
|
|
3557
3828
|
["checking-native", "app check"],
|
|
3558
3829
|
["building", "Xcode"],
|
|
3559
3830
|
["installing", "install"],
|
|
@@ -3566,9 +3837,10 @@ var init_iosSimulatorController = __esm(() => {
|
|
|
3566
3837
|
var ABSOLUTE_REMOTE_MAC_EVENT_PREFIX = "ABSOLUTE_REMOTE_MAC\t", ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION = 1;
|
|
3567
3838
|
|
|
3568
3839
|
// src/mobile/remoteMacProtocol.ts
|
|
3569
|
-
import { createHash as createHash5, randomUUID as
|
|
3570
|
-
import { chmod, mkdir as mkdir5, readFile as
|
|
3840
|
+
import { createHash as createHash5, randomUUID as randomUUID4 } from "crypto";
|
|
3841
|
+
import { chmod, mkdir as mkdir5, readFile as readFile7, rename as rename5, writeFile as writeFile5 } from "fs/promises";
|
|
3571
3842
|
import { homedir as homedir4 } from "os";
|
|
3843
|
+
import { isIP as isIP3 } from "net";
|
|
3572
3844
|
import {
|
|
3573
3845
|
dirname as dirname6,
|
|
3574
3846
|
isAbsolute as isAbsolute4,
|
|
@@ -3583,7 +3855,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3583
3855
|
profiles: {}
|
|
3584
3856
|
}), loadStore = async (path = defaultProfilePath()) => {
|
|
3585
3857
|
try {
|
|
3586
|
-
const parsed = JSON.parse(await
|
|
3858
|
+
const parsed = JSON.parse(await readFile7(path, "utf8"));
|
|
3587
3859
|
if (parsed.format !== PROFILE_FORMAT || typeof parsed.profiles !== "object" || parsed.profiles === null || Array.isArray(parsed.profiles))
|
|
3588
3860
|
throw new Error("Unsupported remote Mac profile format.");
|
|
3589
3861
|
for (const [key, profile] of Object.entries(parsed.profiles)) {
|
|
@@ -3600,7 +3872,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3600
3872
|
}
|
|
3601
3873
|
}, saveStore = async (store, path = defaultProfilePath()) => {
|
|
3602
3874
|
await mkdir5(dirname6(path), { recursive: true });
|
|
3603
|
-
const temporary = `${path}.${
|
|
3875
|
+
const temporary = `${path}.${randomUUID4()}.tmp`;
|
|
3604
3876
|
await writeFile5(temporary, `${JSON.stringify(store, null, 2)}
|
|
3605
3877
|
`, {
|
|
3606
3878
|
mode: 384
|
|
@@ -3682,6 +3954,18 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3682
3954
|
if (!xcodeVersion?.startsWith("Xcode "))
|
|
3683
3955
|
throw new Error("The remote Mac must have full Xcode installed and selected.");
|
|
3684
3956
|
return { bunPath, home, os: operatingSystem, xcodeVersion };
|
|
3957
|
+
}, inspectAbsoluteRemoteMacLanHost = async (profile, transport) => {
|
|
3958
|
+
const capture = transport?.capture ?? defaultTransport.capture;
|
|
3959
|
+
const script = `default_interface="$(/sbin/route -n get default 2>/dev/null | /usr/bin/awk '/interface:/{print $2; exit}')"; for interface in "$default_interface" en0 en1; do [ -n "$interface" ] || continue; address="$(/usr/sbin/ipconfig getifaddr "$interface" 2>/dev/null || true)"; if [ -n "$address" ]; then printf '%s\\n' "$address"; exit 0; fi; done; exit 1`;
|
|
3960
|
+
const result = await capture([
|
|
3961
|
+
...absoluteRemoteMacSshBase(profile),
|
|
3962
|
+
"/bin/sh -lc",
|
|
3963
|
+
shellQuote(script)
|
|
3964
|
+
]);
|
|
3965
|
+
const host = requireRemoteSuccess(result, "Remote Mac LAN address discovery").trim();
|
|
3966
|
+
if (isIP3(host) === 0)
|
|
3967
|
+
throw new Error("The Remote Mac did not report a device-reachable LAN address.");
|
|
3968
|
+
return host;
|
|
3685
3969
|
}, listAbsoluteRemoteMacProfiles = async (profilePath) => {
|
|
3686
3970
|
const store = await loadStore(profilePath);
|
|
3687
3971
|
return {
|
|
@@ -3748,7 +4032,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3748
4032
|
]);
|
|
3749
4033
|
if (verified.exitCode === 0)
|
|
3750
4034
|
return { ...artifact, remotePath, uploaded: false };
|
|
3751
|
-
const temporary = posix.join(directory, `.agent-${
|
|
4035
|
+
const temporary = posix.join(directory, `.agent-${randomUUID4()}.tmp`);
|
|
3752
4036
|
const installScript = [
|
|
3753
4037
|
"set -eu",
|
|
3754
4038
|
"umask 077",
|
|
@@ -3835,7 +4119,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3835
4119
|
}), absoluteRemoteProjectSyncCommands = (project) => {
|
|
3836
4120
|
const current = project.remoteProjectRoot;
|
|
3837
4121
|
const parent = posix.dirname(current);
|
|
3838
|
-
const staging = posix.join(parent, `.incoming-${
|
|
4122
|
+
const staging = posix.join(parent, `.incoming-${randomUUID4()}`);
|
|
3839
4123
|
const previous = posix.join(parent, ".previous");
|
|
3840
4124
|
const script = [
|
|
3841
4125
|
"set -eu",
|
|
@@ -3924,7 +4208,13 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3924
4208
|
await syncProject(options.project);
|
|
3925
4209
|
const syncDuration = performance.now() - syncStartedAt;
|
|
3926
4210
|
const encodedConfig = Buffer.from(JSON.stringify(portableMobileConfig(options.project))).toString("base64url");
|
|
3927
|
-
const encodedCertificateAuthority = options.certificateAuthorityPath ? (await
|
|
4211
|
+
const encodedCertificateAuthority = options.certificateAuthorityPath ? (await readFile7(options.certificateAuthorityPath)).toString("base64url") : undefined;
|
|
4212
|
+
const physicalDevice = options.deviceIdentifier !== undefined;
|
|
4213
|
+
let relayPort;
|
|
4214
|
+
if (physicalDevice)
|
|
4215
|
+
relayPort = options.port <= 49151 ? options.port + 16384 : options.port - 16384;
|
|
4216
|
+
if (physicalDevice && !options.serverHost)
|
|
4217
|
+
throw new Error("Remote physical iOS development requires the Remote Mac LAN host.");
|
|
3928
4218
|
const remoteCommand = [
|
|
3929
4219
|
`cd ${shellQuote(options.project.remoteProjectRoot)}`,
|
|
3930
4220
|
"&&",
|
|
@@ -3939,14 +4229,22 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3939
4229
|
"--certificate-authority",
|
|
3940
4230
|
shellQuote(encodedCertificateAuthority)
|
|
3941
4231
|
] : [],
|
|
3942
|
-
...options.https ? ["--https"] : []
|
|
4232
|
+
...options.https ? ["--https"] : [],
|
|
4233
|
+
...options.deviceIdentifier ? [
|
|
4234
|
+
"--ios-device",
|
|
4235
|
+
shellQuote(options.deviceIdentifier),
|
|
4236
|
+
"--server-host",
|
|
4237
|
+
shellQuote(options.serverHost ?? ""),
|
|
4238
|
+
"--relay-port",
|
|
4239
|
+
String(relayPort)
|
|
4240
|
+
] : []
|
|
3943
4241
|
].join(" ");
|
|
3944
4242
|
const command = [
|
|
3945
4243
|
...absoluteRemoteMacSshBase(options.project.profile),
|
|
3946
4244
|
"-o",
|
|
3947
4245
|
"ExitOnForwardFailure=yes",
|
|
3948
4246
|
"-R",
|
|
3949
|
-
`${options.port}:127.0.0.1:${options.port}`,
|
|
4247
|
+
physicalDevice ? `127.0.0.1:${relayPort}:127.0.0.1:${options.port}` : `${options.port}:127.0.0.1:${options.port}`,
|
|
3950
4248
|
"/bin/sh -lc",
|
|
3951
4249
|
shellQuote(remoteCommand)
|
|
3952
4250
|
];
|
|
@@ -4034,7 +4332,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4034
4332
|
};
|
|
4035
4333
|
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.`);
|
|
4036
4334
|
const request = (commandName) => {
|
|
4037
|
-
const id =
|
|
4335
|
+
const id = randomUUID4();
|
|
4038
4336
|
const response = new Promise((resolve8, reject) => pending.set(id, { reject, resolve: resolve8 }));
|
|
4039
4337
|
process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: 1 })}
|
|
4040
4338
|
`);
|
|
@@ -4067,6 +4365,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4067
4365
|
close,
|
|
4068
4366
|
nativeCacheHit: currentReady.nativeCacheHit,
|
|
4069
4367
|
startedSimulator: currentReady.startedSimulator,
|
|
4368
|
+
targetKind: currentReady.targetKind,
|
|
4070
4369
|
timings: currentReady.timings,
|
|
4071
4370
|
udid: currentReady.udid,
|
|
4072
4371
|
rebuild: async () => {
|
|
@@ -4136,8 +4435,8 @@ import {
|
|
|
4136
4435
|
readFileSync as readFileSync7,
|
|
4137
4436
|
rmSync
|
|
4138
4437
|
} from "fs";
|
|
4139
|
-
import { X509Certificate } from "crypto";
|
|
4140
|
-
import { isIP } from "net";
|
|
4438
|
+
import { X509Certificate as X509Certificate2 } from "crypto";
|
|
4439
|
+
import { isIP as isIP4 } from "net";
|
|
4141
4440
|
import { platform as platform2 } from "os";
|
|
4142
4441
|
import { join as join12 } from "path";
|
|
4143
4442
|
var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE_HOSTS, CERTIFICATE_HOSTNAME_PATTERN, devLog = (msg) => console.log(`\x1B[2m${new Date().toLocaleTimeString()}\x1B[0m \x1B[36m[dev]\x1B[0m ${msg}`), devWarn = (msg) => console.log(`\x1B[2m${new Date().toLocaleTimeString()}\x1B[0m \x1B[33m[dev]\x1B[0m \x1B[33m${msg}\x1B[0m`), certFilesExist = () => existsSync4(CERT_PATH) && existsSync4(KEY_PATH), normalizeDevCertificateHosts = (hosts = []) => {
|
|
@@ -4146,7 +4445,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
|
|
|
4146
4445
|
const value = host.trim().toLowerCase();
|
|
4147
4446
|
if (!value || value === "0.0.0.0" || value === "::")
|
|
4148
4447
|
continue;
|
|
4149
|
-
if (
|
|
4448
|
+
if (isIP4(value) === 0 && !CERTIFICATE_HOSTNAME_PATTERN.test(value)) {
|
|
4150
4449
|
throw new TypeError(`Invalid development certificate host: ${host}`);
|
|
4151
4450
|
}
|
|
4152
4451
|
normalized.add(value);
|
|
@@ -4155,10 +4454,10 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
|
|
|
4155
4454
|
}, certificateIsUsable = (hosts) => {
|
|
4156
4455
|
try {
|
|
4157
4456
|
const certPem = readFileSync7(CERT_PATH, "utf-8");
|
|
4158
|
-
const certificate = new
|
|
4457
|
+
const certificate = new X509Certificate2(certPem);
|
|
4159
4458
|
if (new Date(certificate.validTo).getTime() <= Date.now())
|
|
4160
4459
|
return false;
|
|
4161
|
-
return normalizeDevCertificateHosts(hosts).every((host) =>
|
|
4460
|
+
return normalizeDevCertificateHosts(hosts).every((host) => isIP4(host) ? certificate.checkIP(host) !== undefined : certificate.checkHost(host) !== undefined);
|
|
4162
4461
|
} catch {
|
|
4163
4462
|
return false;
|
|
4164
4463
|
}
|
|
@@ -4186,7 +4485,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
|
|
|
4186
4485
|
throw new Error(`mkcert failed: ${err}`);
|
|
4187
4486
|
}
|
|
4188
4487
|
}, generateSelfSigned = (hosts = []) => {
|
|
4189
|
-
const subjectAlternativeNames = normalizeDevCertificateHosts(hosts).map((host) => `${
|
|
4488
|
+
const subjectAlternativeNames = normalizeDevCertificateHosts(hosts).map((host) => `${isIP4(host) ? "IP" : "DNS"}:${host}`).join(",");
|
|
4190
4489
|
const proc = Bun.spawnSync([
|
|
4191
4490
|
"openssl",
|
|
4192
4491
|
"req",
|
|
@@ -5834,7 +6133,7 @@ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-
|
|
|
5834
6133
|
|
|
5835
6134
|
// src/mobile/buildRelease.ts
|
|
5836
6135
|
import { createHash as createHash8 } from "crypto";
|
|
5837
|
-
import { mkdir as mkdir7, readFile as
|
|
6136
|
+
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
5838
6137
|
import { basename as basename6, dirname as dirname9, extname as extname3, join as join16, relative as relative9, resolve as resolve13 } from "path";
|
|
5839
6138
|
var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex"), STATIC_SCRIPT_PATTERN, rewriteStaticScriptPaths = (source, manifest) => source.replace(STATIC_SCRIPT_PATTERN, (match, prefix, path, suffix) => {
|
|
5840
6139
|
if (path.endsWith("/htmx.min.js"))
|
|
@@ -5856,7 +6155,7 @@ var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex"), STATI
|
|
|
5856
6155
|
}
|
|
5857
6156
|
let resolvedAssetPath = resolveAssetPath(buildDirectory, assetPath);
|
|
5858
6157
|
if (metadata.framework === "html" || metadata.framework === "htmx") {
|
|
5859
|
-
const source = await
|
|
6158
|
+
const source = await readFile8(resolvedAssetPath, "utf8");
|
|
5860
6159
|
const rewritten = rewriteStaticScriptPaths(source, manifest);
|
|
5861
6160
|
const documentHash = sha256(new TextEncoder().encode(rewritten));
|
|
5862
6161
|
resolvedAssetPath = join16(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
|
|
@@ -5870,8 +6169,8 @@ var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex"), STATI
|
|
|
5870
6169
|
].map((key) => manifest[key]).find((path) => typeof path === "string");
|
|
5871
6170
|
const resolvedStylePath = styleAssetPath ? resolveAssetPath(buildDirectory, styleAssetPath) : undefined;
|
|
5872
6171
|
const [bytes, styleBytes] = await Promise.all([
|
|
5873
|
-
|
|
5874
|
-
resolvedStylePath ?
|
|
6172
|
+
readFile8(resolvedAssetPath),
|
|
6173
|
+
resolvedStylePath ? readFile8(resolvedStylePath) : undefined
|
|
5875
6174
|
]);
|
|
5876
6175
|
const bundlePath = `/${relative9(resolve13(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
|
|
5877
6176
|
const styleBundlePath = resolvedStylePath ? `/${relative9(resolve13(buildDirectory), resolvedStylePath).replaceAll("\\", "/")}` : undefined;
|
|
@@ -5890,7 +6189,7 @@ var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex"), STATI
|
|
|
5890
6189
|
}, buildAbsoluteMobileCompatibilityRelease = async (options) => {
|
|
5891
6190
|
const [captured, producerBytes] = await Promise.all([
|
|
5892
6191
|
captureAbsoluteMobileRouteGraph(options.app),
|
|
5893
|
-
|
|
6192
|
+
readFile8(options.producerPath)
|
|
5894
6193
|
]);
|
|
5895
6194
|
if (captured.length === 0) {
|
|
5896
6195
|
throw new TypeError("No instrumented AbsoluteJS mobile page routes were found in the finalized Elysia route graph.");
|
|
@@ -6070,7 +6369,7 @@ import {
|
|
|
6070
6369
|
copyFile as copyFile4,
|
|
6071
6370
|
mkdir as mkdir8,
|
|
6072
6371
|
mkdtemp as mkdtemp3,
|
|
6073
|
-
readFile as
|
|
6372
|
+
readFile as readFile9,
|
|
6074
6373
|
rename as rename6,
|
|
6075
6374
|
rm as rm5,
|
|
6076
6375
|
writeFile as writeFile7
|
|
@@ -6128,7 +6427,7 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
|
|
|
6128
6427
|
const packageName = specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? "";
|
|
6129
6428
|
const subpath = specifier.slice(packageName.length);
|
|
6130
6429
|
const packageDirectory = join17(resolve14(projectRoot), "node_modules", packageName);
|
|
6131
|
-
const manifest = JSON.parse(await
|
|
6430
|
+
const manifest = JSON.parse(await readFile9(join17(packageDirectory, "package.json"), "utf8"));
|
|
6132
6431
|
const exports = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "exports") : undefined;
|
|
6133
6432
|
const entry = typeof exports === "object" && exports !== null ? Reflect.get(exports, subpath ? `.${subpath}` : ".") : undefined;
|
|
6134
6433
|
const target = importEntryTarget(entry);
|
|
@@ -6230,7 +6529,7 @@ void startAbsoluteMobileShell(${push ? `{ createAuth: (config, options) => creat
|
|
|
6230
6529
|
...localStylePath ? { localStylePath } : {}
|
|
6231
6530
|
};
|
|
6232
6531
|
}, absoluteClientImports = async (sourcePath, buildDirectory) => {
|
|
6233
|
-
const source = await
|
|
6532
|
+
const source = await readFile9(sourcePath, "utf8");
|
|
6234
6533
|
const extension = extname4(sourcePath).toLowerCase();
|
|
6235
6534
|
let scriptLoader;
|
|
6236
6535
|
if (extension === ".tsx")
|
|
@@ -6372,7 +6671,7 @@ import {
|
|
|
6372
6671
|
access as access6,
|
|
6373
6672
|
mkdir as mkdir9,
|
|
6374
6673
|
mkdtemp as mkdtemp4,
|
|
6375
|
-
readFile as
|
|
6674
|
+
readFile as readFile10,
|
|
6376
6675
|
rename as rename7,
|
|
6377
6676
|
rm as rm6,
|
|
6378
6677
|
writeFile as writeFile8
|
|
@@ -6472,7 +6771,7 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
|
|
|
6472
6771
|
}, readAbsoluteMobileMaterializedReleases = async (root) => {
|
|
6473
6772
|
const resolvedRoot = resolvePath2(root);
|
|
6474
6773
|
try {
|
|
6475
|
-
const serialized = await
|
|
6774
|
+
const serialized = await readFile10(join18(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
|
|
6476
6775
|
const parsed = JSON.parse(serialized);
|
|
6477
6776
|
const index = parseBundleIndex(parsed);
|
|
6478
6777
|
const bundleRoot = join18(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
@@ -7174,7 +7473,7 @@ var init_deviceCapabilities = __esm(() => {
|
|
|
7174
7473
|
});
|
|
7175
7474
|
|
|
7176
7475
|
// src/mobile/buildPipeline.ts
|
|
7177
|
-
import { readFile as
|
|
7476
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
7178
7477
|
import { join as join21, resolve as resolve17 } from "path";
|
|
7179
7478
|
import { pathToFileURL } from "url";
|
|
7180
7479
|
var isElysiaApp = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "compile") === "function" && Array.isArray(Reflect.get(value, "routes")), isStringRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string"), serverExportName = (loaded, app) => {
|
|
@@ -7208,7 +7507,7 @@ var isElysiaApp = (value) => typeof value === "object" && value !== null && type
|
|
|
7208
7507
|
const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
|
|
7209
7508
|
const root = join21(buildDirectory, ".absolutejs", "mobile-compatibility");
|
|
7210
7509
|
const [manifestSource, previous] = await Promise.all([
|
|
7211
|
-
|
|
7510
|
+
readFile11(join21(buildDirectory, "manifest.json"), "utf8"),
|
|
7212
7511
|
readAbsoluteMobileMaterializedReleases(root)
|
|
7213
7512
|
]);
|
|
7214
7513
|
const manifest = JSON.parse(manifestSource);
|
|
@@ -10914,8 +11213,8 @@ export { value };
|
|
|
10914
11213
|
host2.getSourceFile = (fileName, languageVersion, onError, shouldCreate) => fileName === virtualPath ? ts6.createSourceFile(fileName, source, languageVersion, true) : getSourceFile(fileName, languageVersion, onError, shouldCreate);
|
|
10915
11214
|
const fileExists = host2.fileExists.bind(host2);
|
|
10916
11215
|
host2.fileExists = (fileName) => fileName === virtualPath ? true : fileExists(fileName);
|
|
10917
|
-
const
|
|
10918
|
-
host2.readFile = (fileName) => fileName === virtualPath ? source :
|
|
11216
|
+
const readFile12 = host2.readFile.bind(host2);
|
|
11217
|
+
host2.readFile = (fileName) => fileName === virtualPath ? source : readFile12(fileName);
|
|
10919
11218
|
const program = ts6.createProgram([virtualPath], options, host2);
|
|
10920
11219
|
const checker = program.getTypeChecker();
|
|
10921
11220
|
const sourceFile = program.getSourceFile(virtualPath);
|
|
@@ -16538,10 +16837,10 @@ var init_compile = __esm(() => {
|
|
|
16538
16837
|
});
|
|
16539
16838
|
|
|
16540
16839
|
// src/mobile/nativeDeepLinks.ts
|
|
16541
|
-
import { readFile as
|
|
16840
|
+
import { readFile as readFile12, rename as rename8, writeFile as writeFile9 } from "fs/promises";
|
|
16542
16841
|
import { join as join46 } from "path";
|
|
16543
16842
|
var START_MARKER = "<!-- absolutejs:deep-links:start -->", END_MARKER = "<!-- absolutejs:deep-links:end -->", IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements", NOT_FOUND = -1, escapeXml = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">"), writeChangedFile = async (path, source) => {
|
|
16544
|
-
const current = await
|
|
16843
|
+
const current = await readFile12(path, "utf8");
|
|
16545
16844
|
if (current === source)
|
|
16546
16845
|
return false;
|
|
16547
16846
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
@@ -16589,7 +16888,7 @@ ${hosts}
|
|
|
16589
16888
|
`;
|
|
16590
16889
|
}, configureAndroid = async (config) => {
|
|
16591
16890
|
const path = join46(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
16592
|
-
const source = await
|
|
16891
|
+
const source = await readFile12(path, "utf8");
|
|
16593
16892
|
const mainActivity = source.indexOf('android:name=".MainActivity"');
|
|
16594
16893
|
if (mainActivity === NOT_FOUND) {
|
|
16595
16894
|
throw new TypeError("Android MainActivity was not found.");
|
|
@@ -16613,7 +16912,7 @@ ${hosts}
|
|
|
16613
16912
|
${END_MARKER}
|
|
16614
16913
|
`, configureIosInfo = async (config) => {
|
|
16615
16914
|
const path = join46(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
16616
|
-
const source = await
|
|
16915
|
+
const source = await readFile12(path, "utf8");
|
|
16617
16916
|
const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
|
|
16618
16917
|
${END_MARKER}
|
|
16619
16918
|
`;
|
|
@@ -16637,7 +16936,7 @@ ${domains}
|
|
|
16637
16936
|
const path = join46(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
16638
16937
|
let current = "";
|
|
16639
16938
|
try {
|
|
16640
|
-
current = await
|
|
16939
|
+
current = await readFile12(path, "utf8");
|
|
16641
16940
|
} catch (error) {
|
|
16642
16941
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
16643
16942
|
throw error;
|
|
@@ -16652,7 +16951,7 @@ ${domains}
|
|
|
16652
16951
|
return true;
|
|
16653
16952
|
}, configureIosProject = async (config) => {
|
|
16654
16953
|
const path = join46(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
16655
|
-
const source = await
|
|
16954
|
+
const source = await readFile12(path, "utf8");
|
|
16656
16955
|
const declarations = [
|
|
16657
16956
|
...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
|
|
16658
16957
|
].map((match) => match[1]);
|
|
@@ -16690,10 +16989,10 @@ ${domains}
|
|
|
16690
16989
|
var init_nativeDeepLinks = () => {};
|
|
16691
16990
|
|
|
16692
16991
|
// src/mobile/nativeDeviceCapabilities.ts
|
|
16693
|
-
import { readFile as
|
|
16992
|
+
import { readFile as readFile13, rename as rename9, writeFile as writeFile10 } from "fs/promises";
|
|
16694
16993
|
import { join as join47 } from "path";
|
|
16695
16994
|
var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->", NOT_FOUND2 = -1, IOS_PRIVACY_FILE_REFERENCE = "A85D0C000000000000000001", IOS_PRIVACY_BUILD_FILE = "A85D0C000000000000000002", PUSH_START_MARKER = "absolutejs:push-notifications:start", PUSH_END_MARKER = "absolutejs:push-notifications:end", escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">"), writeChangedFile2 = async (path, source) => {
|
|
16696
|
-
const current = await
|
|
16995
|
+
const current = await readFile13(path, "utf8");
|
|
16697
16996
|
if (current === source)
|
|
16698
16997
|
return false;
|
|
16699
16998
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
@@ -16711,7 +17010,7 @@ var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2
|
|
|
16711
17010
|
return writeChangedFile2(path, source);
|
|
16712
17011
|
}, optionalSource = async (path) => {
|
|
16713
17012
|
try {
|
|
16714
|
-
return await
|
|
17013
|
+
return await readFile13(path, "utf8");
|
|
16715
17014
|
} catch (error) {
|
|
16716
17015
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
16717
17016
|
return null;
|
|
@@ -16819,7 +17118,7 @@ ${entries}
|
|
|
16819
17118
|
if (requirements.iosPrivacyAccessedApis.length === 0)
|
|
16820
17119
|
return false;
|
|
16821
17120
|
const projectPath = join47(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
16822
|
-
const project = await
|
|
17121
|
+
const project = await readFile13(projectPath, "utf8");
|
|
16823
17122
|
return writeChangedFile2(projectPath, addIosPrivacyProjectReference(project));
|
|
16824
17123
|
}, addIosPrivacyProjectReference = (source) => {
|
|
16825
17124
|
const fileMatch = source.match(/([A-F0-9]{24}) \/\* PrivacyInfo\.xcprivacy \*\/ = \{isa = PBXFileReference;/u);
|
|
@@ -16870,7 +17169,7 @@ ${next.slice(index)}`;
|
|
|
16870
17169
|
return next;
|
|
16871
17170
|
}, configureIos2 = async (config, plan) => {
|
|
16872
17171
|
const path = join47(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
16873
|
-
const source = await
|
|
17172
|
+
const source = await readFile13(path, "utf8");
|
|
16874
17173
|
const requirements = absoluteDeviceNativeRequirements(plan);
|
|
16875
17174
|
const existingSystemBars = source.match(/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<(true|false)\s*\/>/u);
|
|
16876
17175
|
const ownedStart = source.indexOf(START_MARKER2);
|
|
@@ -16960,7 +17259,7 @@ ${content}
|
|
|
16960
17259
|
return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
|
|
16961
17260
|
}, configureAndroid2 = async (config, plan) => {
|
|
16962
17261
|
const path = join47(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
16963
|
-
const source = await
|
|
17262
|
+
const source = await readFile13(path, "utf8");
|
|
16964
17263
|
const permissions = absoluteDeviceNativeRequirements(plan).androidPermissions;
|
|
16965
17264
|
const content = permissions.map((permission) => ` <uses-permission android:name="${escapeXml2(permission)}" />`).join(`
|
|
16966
17265
|
`);
|
|
@@ -17019,10 +17318,10 @@ var init_nativeDeviceCapabilities = __esm(() => {
|
|
|
17019
17318
|
});
|
|
17020
17319
|
|
|
17021
17320
|
// src/mobile/nativeBackgroundSync.ts
|
|
17022
|
-
import { readFile as
|
|
17321
|
+
import { readFile as readFile14, rename as rename10, writeFile as writeFile11 } from "fs/promises";
|
|
17023
17322
|
import { join as join48 } from "path";
|
|
17024
17323
|
var writeChanged = async (path, source) => {
|
|
17025
|
-
const current = await
|
|
17324
|
+
const current = await readFile14(path, "utf8");
|
|
17026
17325
|
if (current === source)
|
|
17027
17326
|
return false;
|
|
17028
17327
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
@@ -17104,10 +17403,10 @@ ${makeRegion(values)} </array>
|
|
|
17104
17403
|
return { changed: false };
|
|
17105
17404
|
const identifier = `${config.appId}.absolutejs.background-sync`;
|
|
17106
17405
|
const infoPath = join48(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
17107
|
-
const info2 = await
|
|
17406
|
+
const info2 = await readFile14(infoPath, "utf8");
|
|
17108
17407
|
const nextInfo = ensurePlistArrayValues(ensurePlistArrayValues(info2, "BGTaskSchedulerPermittedIdentifiers", [identifier], "background-sync-identifiers"), "UIBackgroundModes", ["fetch", "processing"], "background-sync-modes");
|
|
17109
17408
|
const delegatePath = join48(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
17110
|
-
let delegate = await
|
|
17409
|
+
let delegate = await readFile14(delegatePath, "utf8");
|
|
17111
17410
|
if (!delegate.includes("import AbsoluteSyncCapacitor")) {
|
|
17112
17411
|
const importIndex = delegate.lastIndexOf("import Capacitor");
|
|
17113
17412
|
if (importIndex < 0)
|
|
@@ -17141,7 +17440,7 @@ var init_nativeBackgroundSync = __esm(() => {
|
|
|
17141
17440
|
import {
|
|
17142
17441
|
access as access7,
|
|
17143
17442
|
mkdir as mkdir10,
|
|
17144
|
-
readFile as
|
|
17443
|
+
readFile as readFile15,
|
|
17145
17444
|
rename as rename11,
|
|
17146
17445
|
rm as rm7,
|
|
17147
17446
|
writeFile as writeFile12
|
|
@@ -17199,7 +17498,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
|
|
|
17199
17498
|
}, writeAtomic = async (path, source) => {
|
|
17200
17499
|
let current;
|
|
17201
17500
|
try {
|
|
17202
|
-
current = await
|
|
17501
|
+
current = await readFile15(path, "utf8");
|
|
17203
17502
|
} catch (error) {
|
|
17204
17503
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
17205
17504
|
throw error;
|
|
@@ -17222,7 +17521,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
|
|
|
17222
17521
|
const path = resolve37(root, OWNERSHIP_FILE);
|
|
17223
17522
|
let ownership;
|
|
17224
17523
|
try {
|
|
17225
|
-
ownership = JSON.parse(await
|
|
17524
|
+
ownership = JSON.parse(await readFile15(path, "utf8"));
|
|
17226
17525
|
} catch {
|
|
17227
17526
|
throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
|
|
17228
17527
|
}
|
|
@@ -17735,7 +18034,7 @@ var DEFAULT_ROUTE_TIMEOUT_MS = 30000, DEFAULT_HMR_TIMEOUT_MS = 30000, routeExpre
|
|
|
17735
18034
|
};
|
|
17736
18035
|
|
|
17737
18036
|
// src/mobile/releaseDoctor.ts
|
|
17738
|
-
import { access as access8, readFile as
|
|
18037
|
+
import { access as access8, readFile as readFile16, readdir as readdir4 } from "fs/promises";
|
|
17739
18038
|
import { dirname as dirname29, extname as extname8, join as join49, relative as relative24 } from "path";
|
|
17740
18039
|
var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
17741
18040
|
try {
|
|
@@ -17749,7 +18048,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17749
18048
|
return findHmrAsset(path);
|
|
17750
18049
|
if (!isFile2 || !RELEASE_ASSET_EXTENSIONS.has(extname8(path)))
|
|
17751
18050
|
return;
|
|
17752
|
-
const source = await
|
|
18051
|
+
const source = await readFile16(path, "utf8");
|
|
17753
18052
|
return HMR_ASSET_PATTERN.test(source) ? path : undefined;
|
|
17754
18053
|
}, findHmrAsset = async (root) => {
|
|
17755
18054
|
if (!await pathExists5(root))
|
|
@@ -17788,18 +18087,18 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17788
18087
|
if (!await pathExists5(nativeConfigPath)) {
|
|
17789
18088
|
return fail5("android.capacitor-config", "The generated Android Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync android` before release validation.");
|
|
17790
18089
|
}
|
|
17791
|
-
const unsafe = isUnsafeCapacitorConfig(await
|
|
18090
|
+
const unsafe = isUnsafeCapacitorConfig(await readFile16(nativeConfigPath, "utf8"));
|
|
17792
18091
|
return unsafe ? fail5("android.capacitor-config", "Android Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync android`; do not ship development transport overrides.") : pass("android.capacitor-config", "Android Capacitor config contains no development transport overrides.", nativeConfigPath);
|
|
17793
18092
|
}, manifestReleaseCheck = async (manifestPath) => {
|
|
17794
18093
|
if (!await pathExists5(manifestPath)) {
|
|
17795
18094
|
return fail5("android.cleartext", "The Android manifest is missing.", manifestPath, "Run `absolute mobile sync android` before release validation.");
|
|
17796
18095
|
}
|
|
17797
|
-
const source = await
|
|
18096
|
+
const source = await readFile16(manifestPath, "utf8");
|
|
17798
18097
|
const cleartext = /android:usesCleartextTraffic=["']true["']/u.test(source);
|
|
17799
18098
|
const networkConfigName = source.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
|
|
17800
18099
|
const networkConfigPath = networkConfigName ? join49(dirname29(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
|
|
17801
18100
|
const developmentTrustReference = /android:networkSecurityConfig=["']@xml\/absolutejs_dev_network_security["']/u.test(source);
|
|
17802
|
-
const developmentTrustContents = networkConfigPath ? await
|
|
18101
|
+
const developmentTrustContents = networkConfigPath ? await readFile16(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
|
|
17803
18102
|
const developmentTrust = developmentTrustReference || developmentTrustContents;
|
|
17804
18103
|
return cleartext || developmentTrust ? fail5("android.cleartext", developmentTrust ? "Android still references the AbsoluteJS development certificate authority." : "Android explicitly permits cleartext traffic.", manifestPath, "Run `absolute mobile sync android`; do not ship development transport or trust overrides.") : pass("android.cleartext", "Android does not explicitly permit cleartext traffic.", manifestPath);
|
|
17805
18104
|
}, hmrAssetsReleaseCheck = async (publicRoot) => {
|
|
@@ -17831,7 +18130,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17831
18130
|
if (!config.platforms.includes("android") || permissions.length === 0)
|
|
17832
18131
|
return;
|
|
17833
18132
|
const path = join49(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
17834
|
-
const source = await
|
|
18133
|
+
const source = await readFile16(path, "utf8");
|
|
17835
18134
|
const missing = permissions.filter((permission) => !source.includes(`android:name="${permission}"`) && !source.includes(`android:name='${permission}'`));
|
|
17836
18135
|
if (missing.length === 0)
|
|
17837
18136
|
return;
|
|
@@ -17840,7 +18139,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17840
18139
|
if (!config.platforms.includes("ios") || purposes.length === 0)
|
|
17841
18140
|
return;
|
|
17842
18141
|
const path = join49(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
17843
|
-
const source = await
|
|
18142
|
+
const source = await readFile16(path, "utf8");
|
|
17844
18143
|
const missing = purposes.filter((purpose) => !source.includes(`<key>${IOS_USAGE_KEYS[purpose]}</key>`));
|
|
17845
18144
|
if (missing.length === 0)
|
|
17846
18145
|
return;
|
|
@@ -17893,7 +18192,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17893
18192
|
}
|
|
17894
18193
|
if (!await pathExists5(nativeConfigPath)) {
|
|
17895
18194
|
checks.push(fail5("ios.capacitor-config", "The generated iOS Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync ios` before release validation."));
|
|
17896
|
-
} else if (isUnsafeCapacitorConfig(await
|
|
18195
|
+
} else if (isUnsafeCapacitorConfig(await readFile16(nativeConfigPath, "utf8"))) {
|
|
17897
18196
|
checks.push(fail5("ios.capacitor-config", "iOS Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync ios`; do not ship development transport overrides."));
|
|
17898
18197
|
} else {
|
|
17899
18198
|
checks.push(pass("ios.capacitor-config", "iOS Capacitor config contains no development transport overrides.", nativeConfigPath));
|
|
@@ -17901,7 +18200,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17901
18200
|
if (!await pathExists5(infoPath)) {
|
|
17902
18201
|
checks.push(fail5("ios.transport-security", "The iOS Info.plist is missing.", infoPath, "Run `absolute mobile sync ios` before release validation."));
|
|
17903
18202
|
} else {
|
|
17904
|
-
const info2 = await
|
|
18203
|
+
const info2 = await readFile16(infoPath, "utf8");
|
|
17905
18204
|
checks.push(/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info2) ? fail5("ios.transport-security", "iOS App Transport Security permits arbitrary network loads.", infoPath, "Remove NSAllowsArbitraryLoads from the release Info.plist.") : pass("ios.transport-security", "iOS App Transport Security does not permit arbitrary loads.", infoPath));
|
|
17906
18205
|
}
|
|
17907
18206
|
const hmrAsset = await findHmrAsset(publicRoot);
|
|
@@ -17954,7 +18253,7 @@ import {
|
|
|
17954
18253
|
copyFile as copyFile5,
|
|
17955
18254
|
mkdir as mkdir12,
|
|
17956
18255
|
mkdtemp as mkdtemp5,
|
|
17957
|
-
readFile as
|
|
18256
|
+
readFile as readFile17,
|
|
17958
18257
|
rename as rename12,
|
|
17959
18258
|
rm as rm8,
|
|
17960
18259
|
stat as stat2,
|
|
@@ -18010,7 +18309,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
|
|
|
18010
18309
|
artifactPath
|
|
18011
18310
|
]);
|
|
18012
18311
|
return result.exitCode === 0 && /jar verified/iu.test(result.stdout);
|
|
18013
|
-
}, sha256File2 = async (path) => createHash12("sha256").update(await
|
|
18312
|
+
}, sha256File2 = async (path) => createHash12("sha256").update(await readFile17(path)).digest("hex"), safeOutputDirectory2 = (projectRoot, requested) => {
|
|
18014
18313
|
const root = resolve39(projectRoot);
|
|
18015
18314
|
const output = resolve39(root, requested ?? ".absolutejs/mobile/releases/android");
|
|
18016
18315
|
const projectRelative = relative25(root, output);
|
|
@@ -18023,7 +18322,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
|
|
|
18023
18322
|
const artifactName = "app-release.aab";
|
|
18024
18323
|
const destination = join50(releaseRoot, artifactName);
|
|
18025
18324
|
if (await pathExists6(releaseRoot)) {
|
|
18026
|
-
const existing = requireManifestIdentity(JSON.parse(await
|
|
18325
|
+
const existing = requireManifestIdentity(JSON.parse(await readFile17(join50(releaseRoot, "release.json"), "utf8")), metadata);
|
|
18027
18326
|
const [installedBytes, installedSha256] = await Promise.all([
|
|
18028
18327
|
stat2(destination).then(({ size }) => size),
|
|
18029
18328
|
sha256File2(destination)
|
|
@@ -18070,7 +18369,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
|
|
|
18070
18369
|
const host2 = options.host ?? detectAbsoluteMobileHost();
|
|
18071
18370
|
const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2);
|
|
18072
18371
|
const nativeDirectory = join50(options.config.nativeProjectDirectory, "android");
|
|
18073
|
-
const manifest = requireManifest2(JSON.parse(await
|
|
18372
|
+
const manifest = requireManifest2(JSON.parse(await readFile17(join50(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
18074
18373
|
if (manifest.appId !== options.config.appId) {
|
|
18075
18374
|
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
18076
18375
|
}
|
|
@@ -18134,7 +18433,7 @@ var init_androidRelease = __esm(() => {
|
|
|
18134
18433
|
});
|
|
18135
18434
|
|
|
18136
18435
|
// src/mobile/iosConformance.ts
|
|
18137
|
-
import { readFile as
|
|
18436
|
+
import { readFile as readFile18, stat as stat3 } from "fs/promises";
|
|
18138
18437
|
var HMR_LINE, parseAbsoluteIosHmrLog = (line) => {
|
|
18139
18438
|
const match = HMR_LINE.exec(line);
|
|
18140
18439
|
if (!match)
|
|
@@ -18169,7 +18468,7 @@ var HMR_LINE, parseAbsoluteIosHmrLog = (line) => {
|
|
|
18169
18468
|
if (Date.now() > deadline)
|
|
18170
18469
|
throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
|
|
18171
18470
|
options.signal?.throwIfAborted();
|
|
18172
|
-
const contents = await
|
|
18471
|
+
const contents = await readFile18(options.logPath).catch(() => Buffer.alloc(0));
|
|
18173
18472
|
if (contents.byteLength < offset) {
|
|
18174
18473
|
offset = 0;
|
|
18175
18474
|
buffered = "";
|
|
@@ -18193,7 +18492,7 @@ var init_iosConformance = __esm(() => {
|
|
|
18193
18492
|
});
|
|
18194
18493
|
|
|
18195
18494
|
// src/mobile/nativeTestReport.ts
|
|
18196
|
-
import { mkdir as mkdir13, readFile as
|
|
18495
|
+
import { mkdir as mkdir13, readFile as readFile19, writeFile as writeFile15 } from "fs/promises";
|
|
18197
18496
|
import { join as join51 } from "path";
|
|
18198
18497
|
var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sanitizeNativeReportText = (value) => value.replace(nativeCredentialPattern, "[REDACTED]").replace(bearerPattern, "Bearer [REDACTED]").replace(secretPattern, "$1$2[REDACTED]").replace(coordinatePattern, "$1$2[REDACTED]").replace(/(https?:\/\/[^\s?#]+)[?#][^\s]*/giu, "$1?[REDACTED]"), markdownCell = (value) => sanitizeNativeReportText(value).replaceAll("|", "\\|").replaceAll(`
|
|
18199
18498
|
`, "<br>"), createAbsoluteNativeAutomatedChecks = (run) => {
|
|
@@ -18272,7 +18571,7 @@ var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sa
|
|
|
18272
18571
|
reportVersion: 1,
|
|
18273
18572
|
run: options.run
|
|
18274
18573
|
}), readPackageVersionForNativeReport = async (packageJsonPath) => {
|
|
18275
|
-
const manifest = JSON.parse(await
|
|
18574
|
+
const manifest = JSON.parse(await readFile19(packageJsonPath, "utf8"));
|
|
18276
18575
|
if (typeof manifest !== "object" || manifest === null)
|
|
18277
18576
|
return "unknown";
|
|
18278
18577
|
const version2 = Reflect.get(manifest, "version");
|
|
@@ -18613,11 +18912,11 @@ var exports_mobile = {};
|
|
|
18613
18912
|
__export(exports_mobile, {
|
|
18614
18913
|
runMobile: () => runMobile
|
|
18615
18914
|
});
|
|
18616
|
-
import { access as access11, mkdir as mkdir14, readFile as
|
|
18915
|
+
import { access as access11, mkdir as mkdir14, readFile as readFile20, writeFile as writeFile16 } from "fs/promises";
|
|
18617
18916
|
import { join as join52, resolve as resolve41 } from "path";
|
|
18618
18917
|
import { createInterface } from "readline/promises";
|
|
18619
18918
|
var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
|
|
18620
|
-
const manifest = JSON.parse(await
|
|
18919
|
+
const manifest = JSON.parse(await readFile20(join52(projectRoot, "package.json"), "utf8"));
|
|
18621
18920
|
if (!isRecord15(manifest))
|
|
18622
18921
|
throw new TypeError("Application package.json must contain an object.");
|
|
18623
18922
|
const names = new Set;
|
|
@@ -18630,7 +18929,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
|
|
|
18630
18929
|
return names;
|
|
18631
18930
|
}, resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
18632
18931
|
try {
|
|
18633
|
-
const manifest = JSON.parse(await
|
|
18932
|
+
const manifest = JSON.parse(await readFile20(join52(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
18634
18933
|
return isRecord15(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
18635
18934
|
} catch {
|
|
18636
18935
|
return;
|
|
@@ -21208,13 +21507,15 @@ var androidToolchainReady = (checks, target = "emulator") => {
|
|
|
21208
21507
|
]);
|
|
21209
21508
|
return checks.every((check) => check.platform !== "android" || target === "device" && deviceOnlyChecks.has(check.id) || check.status !== "fail" && check.status !== "warn");
|
|
21210
21509
|
};
|
|
21211
|
-
var iosToolchainReady = (checks) => checks.every((check) => check.platform !== "ios" || check.status !== "fail" && check.status !== "warn");
|
|
21510
|
+
var iosToolchainReady = (checks, target = "simulator") => checks.every((check) => check.platform !== "ios" || target === "device" && check.id === "ios.runtime" || check.status !== "fail" && check.status !== "warn");
|
|
21212
21511
|
var dev = async (serverEntry, configPath2, options = {}) => {
|
|
21213
21512
|
let httpsEnabled = false;
|
|
21214
21513
|
let devCertificateAuthorityPath = null;
|
|
21215
21514
|
let resolvedDev;
|
|
21216
21515
|
let buildDirectory = resolve8(process.cwd(), "build");
|
|
21217
21516
|
let mobileConfig;
|
|
21517
|
+
let iosPhysicalServerHost;
|
|
21518
|
+
let selectedRemoteMacProfile;
|
|
21218
21519
|
try {
|
|
21219
21520
|
const config = await loadConfig(configPath2);
|
|
21220
21521
|
mobileConfig = config?.mobile;
|
|
@@ -21229,17 +21530,35 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21229
21530
|
resolvedDev = resolveDevConfig(undefined);
|
|
21230
21531
|
httpsEnabled = resolvedDev.https;
|
|
21231
21532
|
}
|
|
21232
|
-
if (options.androidDevice && ["localhost", "127.0.0.1", "::1"].includes(resolvedDev.host)) {
|
|
21533
|
+
if ((options.androidDevice || options.iosDevice && detectAbsoluteMobileHost() === "macos") && ["localhost", "127.0.0.1", "::1"].includes(resolvedDev.host)) {
|
|
21233
21534
|
resolvedDev.host = "0.0.0.0";
|
|
21234
21535
|
}
|
|
21235
|
-
if (
|
|
21236
|
-
|
|
21237
|
-
if (options.androidDevice && !mobileConfig) {
|
|
21238
|
-
throw new TypeError("--android-device requires an absolute.config.ts mobile configuration.");
|
|
21536
|
+
if ((options.androidDevice || options.iosDevice) && !mobileConfig) {
|
|
21537
|
+
throw new TypeError("Physical-device development requires an absolute.config.ts mobile configuration.");
|
|
21239
21538
|
}
|
|
21240
21539
|
if (options.androidDevice && mobileConfig?.platforms && !mobileConfig.platforms.includes("android")) {
|
|
21241
21540
|
throw new TypeError("--android-device requires android in mobile.platforms.");
|
|
21242
21541
|
}
|
|
21542
|
+
if (options.iosDevice && mobileConfig?.platforms && !mobileConfig.platforms.includes("ios")) {
|
|
21543
|
+
throw new TypeError("--ios-device requires ios in mobile.platforms.");
|
|
21544
|
+
}
|
|
21545
|
+
if (options.iosDevice) {
|
|
21546
|
+
if (detectAbsoluteMobileHost() === "macos")
|
|
21547
|
+
iosPhysicalServerHost = mobileReachableHost(resolvedDev.host);
|
|
21548
|
+
else {
|
|
21549
|
+
selectedRemoteMacProfile = await getAbsoluteRemoteMacProfile();
|
|
21550
|
+
if (!selectedRemoteMacProfile)
|
|
21551
|
+
throw new Error("--ios-device requires macOS or a paired Remote Mac.");
|
|
21552
|
+
iosPhysicalServerHost = await inspectAbsoluteRemoteMacLanHost(selectedRemoteMacProfile);
|
|
21553
|
+
}
|
|
21554
|
+
}
|
|
21555
|
+
if (httpsEnabled) {
|
|
21556
|
+
const certificateHosts = [
|
|
21557
|
+
...options.androidDevice ? [mobileReachableHost(resolvedDev.host)] : [],
|
|
21558
|
+
...iosPhysicalServerHost ? [iosPhysicalServerHost] : []
|
|
21559
|
+
];
|
|
21560
|
+
devCertificateAuthorityPath = await setupHttpsCert(certificateHosts.length > 0 ? certificateHosts : [resolvedDev.host]);
|
|
21561
|
+
}
|
|
21243
21562
|
let androidDevProject = null;
|
|
21244
21563
|
let iosDevProject = null;
|
|
21245
21564
|
const mobileInteractive = options.mobile !== false && process.env.ABSOLUTE_NO_MOBILE !== "1" && process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
@@ -21275,9 +21594,9 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21275
21594
|
}
|
|
21276
21595
|
if (normalized.platforms.includes("ios")) {
|
|
21277
21596
|
if (detectAbsoluteMobileHost() !== "macos") {
|
|
21278
|
-
const remote = await getAbsoluteRemoteMacProfile();
|
|
21597
|
+
const remote = selectedRemoteMacProfile ?? await getAbsoluteRemoteMacProfile();
|
|
21279
21598
|
if (!remote) {
|
|
21280
|
-
console.log(cliTag("\x1B[33m", "iOS
|
|
21599
|
+
console.log(cliTag("\x1B[33m", "iOS target skipped. Pair a Mac with `absolute mobile pair mac <name> <user@host>`."));
|
|
21281
21600
|
} else {
|
|
21282
21601
|
const nativeDirectory = join13(normalized.nativeProjectDirectory, "ios");
|
|
21283
21602
|
if (!existsSync5(nativeDirectory)) {
|
|
@@ -21288,12 +21607,13 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21288
21607
|
}
|
|
21289
21608
|
}
|
|
21290
21609
|
} else {
|
|
21291
|
-
|
|
21610
|
+
const iosTarget = options.iosDevice ? "device" : "simulator";
|
|
21611
|
+
let ready = iosToolchainReady(await inspectAbsoluteMobileToolchain(), iosTarget);
|
|
21292
21612
|
if (!ready) {
|
|
21293
|
-
const install = await confirmPrompt("iOS simulation is not configured. Install the missing simulator runtime now?");
|
|
21613
|
+
const install = await confirmPrompt(options.iosDevice ? "Physical iOS development is not configured. Open the guided Xcode setup now?" : "iOS simulation is not configured. Install the missing simulator runtime now?");
|
|
21294
21614
|
if (install) {
|
|
21295
21615
|
await fixAbsoluteMobileEmulatorToolchain("ios");
|
|
21296
|
-
ready = iosToolchainReady(await inspectAbsoluteMobileToolchain());
|
|
21616
|
+
ready = iosToolchainReady(await inspectAbsoluteMobileToolchain(), iosTarget);
|
|
21297
21617
|
} else {
|
|
21298
21618
|
console.log(cliTag("\x1B[33m", "Mobile simulator skipped. Run `absolute mobile doctor ios --fix` when ready."));
|
|
21299
21619
|
}
|
|
@@ -21307,7 +21627,8 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21307
21627
|
if (existsSync5(nativeDirectory) || createNativeProject) {
|
|
21308
21628
|
iosDevProject = await prepareAbsoluteIosDevProject(normalized, {
|
|
21309
21629
|
createNativeProject,
|
|
21310
|
-
projectRoot: process.cwd()
|
|
21630
|
+
projectRoot: process.cwd(),
|
|
21631
|
+
target: iosTarget
|
|
21311
21632
|
});
|
|
21312
21633
|
}
|
|
21313
21634
|
}
|
|
@@ -21351,7 +21672,8 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21351
21672
|
serverEntry,
|
|
21352
21673
|
...configPath2 ? ["--config", configPath2] : [],
|
|
21353
21674
|
...options.mobile === false ? ["--no-mobile"] : [],
|
|
21354
|
-
...options.androidDevice ? ["--android-device", options.androidDevice] : []
|
|
21675
|
+
...options.androidDevice ? ["--android-device", options.androidDevice] : [],
|
|
21676
|
+
...options.iosDevice ? ["--ios-device", options.iosDevice] : []
|
|
21355
21677
|
].filter((part) => part.length > 0);
|
|
21356
21678
|
registerInstance({
|
|
21357
21679
|
command: relaunchCommand,
|
|
@@ -21523,6 +21845,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21523
21845
|
platform: "ios",
|
|
21524
21846
|
provider: "capacitor",
|
|
21525
21847
|
startedSimulator: session.startedSimulator,
|
|
21848
|
+
target: session.targetKind,
|
|
21526
21849
|
timings: session.timings
|
|
21527
21850
|
});
|
|
21528
21851
|
if (session.timings.building === undefined)
|
|
@@ -21533,14 +21856,17 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21533
21856
|
installMs: session.timings.installing,
|
|
21534
21857
|
platform: "ios",
|
|
21535
21858
|
provider: "capacitor",
|
|
21536
|
-
success: true
|
|
21859
|
+
success: true,
|
|
21860
|
+
target: session.targetKind
|
|
21537
21861
|
});
|
|
21538
21862
|
};
|
|
21539
21863
|
const openIosDevSession = (iosProject) => {
|
|
21540
21864
|
const sessionOptions = {
|
|
21541
21865
|
certificateAuthorityPath: devCertificateAuthorityPath ?? undefined,
|
|
21866
|
+
deviceIdentifier: options.iosDevice,
|
|
21542
21867
|
https: httpsEnabled,
|
|
21543
21868
|
port,
|
|
21869
|
+
serverHost: iosPhysicalServerHost,
|
|
21544
21870
|
signal: iosDevAbort.signal,
|
|
21545
21871
|
log: (message) => printNativeOutput(cliTag("\x1B[35m", message)),
|
|
21546
21872
|
nativeLog: (entry) => printNativeOutput(iosLogTag(entry)),
|
|
@@ -21554,7 +21880,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21554
21880
|
iosDevState = state;
|
|
21555
21881
|
if (state === "ready" || state === "closed")
|
|
21556
21882
|
return;
|
|
21557
|
-
printNativeOutput(cliTag("\x1B[35m", `iOS simulator: ${state}.`));
|
|
21883
|
+
printNativeOutput(cliTag("\x1B[35m", `iOS ${options.iosDevice ? "device" : "simulator"}: ${state}.`));
|
|
21558
21884
|
}
|
|
21559
21885
|
};
|
|
21560
21886
|
if (iosProject.remote)
|
|
@@ -21593,6 +21919,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21593
21919
|
provider: "capacitor",
|
|
21594
21920
|
rootInputChanged: change.rootInputChanged,
|
|
21595
21921
|
success: true,
|
|
21922
|
+
target: replacement.targetKind,
|
|
21596
21923
|
timings: replacement.timings
|
|
21597
21924
|
});
|
|
21598
21925
|
},
|
|
@@ -21603,7 +21930,8 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21603
21930
|
host: iosTelemetryHost(iosProject),
|
|
21604
21931
|
platform: "ios",
|
|
21605
21932
|
provider: "capacitor",
|
|
21606
|
-
success: false
|
|
21933
|
+
success: false,
|
|
21934
|
+
target: options.iosDevice ? "device" : "simulator"
|
|
21607
21935
|
});
|
|
21608
21936
|
}
|
|
21609
21937
|
});
|
|
@@ -21630,9 +21958,10 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21630
21958
|
phase: iosDevState,
|
|
21631
21959
|
platform: "ios",
|
|
21632
21960
|
provider: "capacitor",
|
|
21961
|
+
target: options.iosDevice ? "device" : "simulator",
|
|
21633
21962
|
timings: iosPhaseTimings
|
|
21634
21963
|
});
|
|
21635
|
-
console.error(cliTag("\x1B[31m", `iOS simulator failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
21964
|
+
console.error(cliTag("\x1B[31m", `iOS ${options.iosDevice ? "device" : "simulator"} failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
21636
21965
|
}).finally(() => {
|
|
21637
21966
|
iosDevStart = null;
|
|
21638
21967
|
});
|
|
@@ -23982,16 +24311,24 @@ if (command === "dev") {
|
|
|
23982
24311
|
sendTelemetryEvent("cli:command", { command });
|
|
23983
24312
|
const configPath2 = parseNamedArg("--config");
|
|
23984
24313
|
const androidDevice = parseNamedArg("--android-device");
|
|
24314
|
+
const iosDevice = parseNamedArg("--ios-device");
|
|
23985
24315
|
if (args.includes("--android-device") && !androidDevice) {
|
|
23986
24316
|
throw new TypeError("--android-device requires an ADB device serial.");
|
|
23987
24317
|
}
|
|
23988
24318
|
if (androidDevice && args.includes("--no-mobile")) {
|
|
23989
24319
|
throw new TypeError("--android-device cannot be combined with --no-mobile.");
|
|
23990
24320
|
}
|
|
23991
|
-
|
|
24321
|
+
if (args.includes("--ios-device") && !iosDevice) {
|
|
24322
|
+
throw new TypeError("--ios-device requires an Xcode device identifier or name.");
|
|
24323
|
+
}
|
|
24324
|
+
if (iosDevice && args.includes("--no-mobile")) {
|
|
24325
|
+
throw new TypeError("--ios-device cannot be combined with --no-mobile.");
|
|
24326
|
+
}
|
|
24327
|
+
const positionalArgs2 = stripNamedArgs("--config", "--android-device", "--ios-device").filter((arg) => arg !== "--no-mobile");
|
|
23992
24328
|
const serverEntry = positionalArgs2[0] ?? DEFAULT_SERVER_ENTRY;
|
|
23993
24329
|
await dev(serverEntry, configPath2, {
|
|
23994
24330
|
androidDevice,
|
|
24331
|
+
iosDevice,
|
|
23995
24332
|
mobile: !args.includes("--no-mobile")
|
|
23996
24333
|
});
|
|
23997
24334
|
} else if (command === "start") {
|
|
@@ -24150,13 +24487,13 @@ if (command === "dev") {
|
|
|
24150
24487
|
console.error(message);
|
|
24151
24488
|
console.error("Usage: absolute <command>");
|
|
24152
24489
|
console.error("Commands:");
|
|
24153
|
-
console.error(" dev [entry] [--no-mobile] [--android-device serial] Start web and configured mobile development");
|
|
24490
|
+
console.error(" dev [entry] [--no-mobile] [--android-device serial] [--ios-device identifier] Start web and configured mobile development");
|
|
24154
24491
|
console.error(" workspace dev [--no-tui] Start multi-service workspace dev");
|
|
24155
24492
|
console.error(" build [--outdir dir] [--profile] Build production assets");
|
|
24156
24493
|
console.error(" prepare [entry] [--outdir dir] Build production assets and server without launching");
|
|
24157
24494
|
console.error(" start [entry] [--outdir dir] [--prebuilt] Start production server");
|
|
24158
24495
|
console.error(" compile [entry] [--outdir dir] [--outfile path] Compile standalone executable");
|
|
24159
|
-
console.error(" mobile <init|sync|pair|remotes|doctor|test> Manage Capacitor projects,
|
|
24496
|
+
console.error(" mobile <init|sync|pair|remotes|doctor|test> Manage Capacitor projects, simulators, physical devices, Remote Macs, guided setup, and deep links");
|
|
24160
24497
|
console.error(" config [--port n] Open the unified config UI (ESLint, tsconfig, Prettier)");
|
|
24161
24498
|
console.error(" db <backup|restore|seed> Backup/restore any Postgres DB (ORM-agnostic, upsert by PK) or run the seed script");
|
|
24162
24499
|
console.error(" doctor [--fix] [--json] Diagnose the project (bun, type graph, config, framework dirs, env, port)");
|