@absolutejs/absolute 0.20.0-beta.33 → 0.20.0-beta.35
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/angular/index.js +17 -9
- package/dist/angular/index.js.map +3 -3
- package/dist/angular/server.js +17 -9
- package/dist/angular/server.js.map +3 -3
- package/dist/build.js +30 -10
- package/dist/build.js.map +6 -6
- package/dist/cli/index.js +762 -158
- package/dist/index.js +33 -10
- package/dist/index.js.map +8 -8
- package/dist/mobile/index.js +483 -93
- package/dist/mobile/index.js.map +8 -6
- package/dist/mobile/remoteMacAgentEntry.js +14 -14
- package/dist/src/cli/instanceStatus.d.ts +1 -0
- package/dist/src/cli/scripts/dev.d.ts +1 -0
- package/dist/src/core/prepare.d.ts +12 -0
- package/dist/src/dev/clientManager.d.ts +1 -0
- package/dist/src/mobile/index.d.ts +2 -0
- package/dist/src/mobile/iosDeviceAcceptance.d.ts +32 -0
- package/dist/src/mobile/iosPhysicalDeviceTransport.d.ts +16 -0
- package/dist/src/mobile/iosSimulatorController.d.ts +12 -1
- package/dist/src/mobile/iosTestReport.d.ts +2 -1
- package/dist/src/mobile/nativeTestReport.d.ts +6 -0
- package/dist/src/mobile/remoteMacProtocol.d.ts +10 -0
- package/dist/src/plugins/hmr.d.ts +3 -0
- package/dist/types/cli.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -458,6 +458,7 @@ var registeredPids, exitHandlerRegistered = false, instanceFilePath = (pid) => j
|
|
|
458
458
|
frameworks: toStringArray(parsed.frameworks),
|
|
459
459
|
host: typeof parsed.host === "string" ? parsed.host : "localhost",
|
|
460
460
|
https: parsed.https === true,
|
|
461
|
+
...typeof parsed.iosRemoteMac === "string" ? { iosRemoteMac: parsed.iosRemoteMac } : {},
|
|
461
462
|
logFile: typeof parsed.logFile === "string" ? parsed.logFile : null,
|
|
462
463
|
name: typeof parsed.name === "string" ? parsed.name : "unknown",
|
|
463
464
|
pid: parsed.pid,
|
|
@@ -2844,17 +2845,109 @@ var init_iosRelease = __esm(() => {
|
|
|
2844
2845
|
]);
|
|
2845
2846
|
});
|
|
2846
2847
|
|
|
2848
|
+
// src/mobile/iosPhysicalDeviceTransport.ts
|
|
2849
|
+
import { randomUUID as randomUUID2, X509Certificate } from "crypto";
|
|
2850
|
+
import { createServer as createServer3 } from "http";
|
|
2851
|
+
import {
|
|
2852
|
+
connect as connectTcp,
|
|
2853
|
+
createServer as createTcpServer,
|
|
2854
|
+
isIP
|
|
2855
|
+
} from "net";
|
|
2856
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
2857
|
+
var closeServer = (server) => new Promise((resolve7, reject) => {
|
|
2858
|
+
server.close((error) => {
|
|
2859
|
+
if (error)
|
|
2860
|
+
reject(error);
|
|
2861
|
+
else
|
|
2862
|
+
resolve7();
|
|
2863
|
+
});
|
|
2864
|
+
}), listen = (server, port) => new Promise((resolve7, reject) => {
|
|
2865
|
+
server.once("error", reject);
|
|
2866
|
+
server.listen(port, "0.0.0.0", () => {
|
|
2867
|
+
server.off("error", reject);
|
|
2868
|
+
const address = server.address();
|
|
2869
|
+
if (!address || typeof address === "string") {
|
|
2870
|
+
reject(new Error("Could not determine the iOS device helper port."));
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
resolve7(address.port);
|
|
2874
|
+
});
|
|
2875
|
+
}), findEphemeralPort = async () => {
|
|
2876
|
+
const probe = createTcpServer();
|
|
2877
|
+
const port = await new Promise((resolve7, reject) => {
|
|
2878
|
+
probe.once("error", reject);
|
|
2879
|
+
probe.listen(0, "127.0.0.1", () => {
|
|
2880
|
+
const address = probe.address();
|
|
2881
|
+
if (!address || typeof address === "string") {
|
|
2882
|
+
reject(new Error("Could not allocate the iOS CA enrollment port."));
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
resolve7(address.port);
|
|
2886
|
+
});
|
|
2887
|
+
});
|
|
2888
|
+
await closeServer(probe);
|
|
2889
|
+
return port;
|
|
2890
|
+
}, normalizeAbsoluteIosDeviceHost = (value) => {
|
|
2891
|
+
const normalized = value.trim();
|
|
2892
|
+
if (!normalized || normalized.length > 253 || /[\0\s/?#]/u.test(normalized))
|
|
2893
|
+
throw new TypeError("Physical iOS development requires a valid LAN host.");
|
|
2894
|
+
return normalized;
|
|
2895
|
+
}, normalizeAbsoluteIosDeviceIdentifier = (value) => {
|
|
2896
|
+
const normalized = value.trim();
|
|
2897
|
+
if (!normalized || normalized.length > 256 || /[\0\r\n]/u.test(normalized))
|
|
2898
|
+
throw new TypeError("--ios-device requires a valid Xcode device identifier or name.");
|
|
2899
|
+
return normalized;
|
|
2900
|
+
}, urlForHost = (protocol, host, port) => {
|
|
2901
|
+
const url = new URL(`${protocol}://localhost:${port}`);
|
|
2902
|
+
const normalizedHost = normalizeAbsoluteIosDeviceHost(host);
|
|
2903
|
+
url.hostname = isIP(normalizedHost) === 6 ? `[${normalizedHost}]` : normalizedHost;
|
|
2904
|
+
return url;
|
|
2905
|
+
}, startAbsoluteIosCaEnrollmentServer = async (options) => {
|
|
2906
|
+
const certificate = new X509Certificate(await readFile5(options.certificateAuthorityPath));
|
|
2907
|
+
const certificateBytes = certificate.raw;
|
|
2908
|
+
const token = randomUUID2().replaceAll("-", "");
|
|
2909
|
+
const certificatePath = `/${token}/absolutejs-development-ca.cer`;
|
|
2910
|
+
const server = createServer3((request, response) => {
|
|
2911
|
+
if (request.method !== "GET" || request.url !== certificatePath) {
|
|
2912
|
+
response.writeHead(404, {
|
|
2913
|
+
"Cache-Control": "no-store",
|
|
2914
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
2915
|
+
});
|
|
2916
|
+
response.end("Not found.");
|
|
2917
|
+
return;
|
|
2918
|
+
}
|
|
2919
|
+
response.writeHead(200, {
|
|
2920
|
+
"Cache-Control": "no-store",
|
|
2921
|
+
"Content-Disposition": 'attachment; filename="absolutejs-development-ca.cer"',
|
|
2922
|
+
"Content-Length": String(certificateBytes.byteLength),
|
|
2923
|
+
"Content-Type": "application/x-x509-ca-cert",
|
|
2924
|
+
"X-Content-Type-Options": "nosniff"
|
|
2925
|
+
});
|
|
2926
|
+
response.end(certificateBytes);
|
|
2927
|
+
});
|
|
2928
|
+
const port = await findEphemeralPort();
|
|
2929
|
+
await listen(server, port);
|
|
2930
|
+
const url = urlForHost("http", options.displayHost, port);
|
|
2931
|
+
url.pathname = certificatePath;
|
|
2932
|
+
return {
|
|
2933
|
+
url: url.href,
|
|
2934
|
+
close: () => closeServer(server)
|
|
2935
|
+
};
|
|
2936
|
+
};
|
|
2937
|
+
var init_iosPhysicalDeviceTransport = () => {};
|
|
2938
|
+
|
|
2847
2939
|
// src/mobile/iosSimulatorController.ts
|
|
2848
|
-
import { createHash as createHash4, randomUUID as
|
|
2940
|
+
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
2849
2941
|
import {
|
|
2850
2942
|
access as access5,
|
|
2851
2943
|
copyFile as copyFile3,
|
|
2852
2944
|
mkdir as mkdir4,
|
|
2853
|
-
readFile as
|
|
2945
|
+
readFile as readFile6,
|
|
2854
2946
|
rename as rename4,
|
|
2855
2947
|
rm as rm4,
|
|
2856
2948
|
writeFile as writeFile4
|
|
2857
2949
|
} from "fs/promises";
|
|
2950
|
+
import { isIP as isIP2 } from "net";
|
|
2858
2951
|
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join10, relative as relative4, resolve as resolve7, sep as sep3 } from "path";
|
|
2859
2952
|
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
2953
|
try {
|
|
@@ -2965,7 +3058,24 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
2965
3058
|
options.certificateAuthorityPath
|
|
2966
3059
|
], "iOS Simulator development CA trust", run, { signal: options.signal });
|
|
2967
3060
|
log("Installed the AbsoluteJS development CA into this iOS Simulator trust store.");
|
|
2968
|
-
},
|
|
3061
|
+
}, iosLaunchCommand = (project, udid, physical) => physical ? [
|
|
3062
|
+
project.xcrun,
|
|
3063
|
+
"devicectl",
|
|
3064
|
+
"device",
|
|
3065
|
+
"process",
|
|
3066
|
+
"launch",
|
|
3067
|
+
"--terminate-existing",
|
|
3068
|
+
"--device",
|
|
3069
|
+
udid,
|
|
3070
|
+
project.config.appId
|
|
3071
|
+
] : [
|
|
3072
|
+
project.xcrun,
|
|
3073
|
+
"simctl",
|
|
3074
|
+
"launch",
|
|
3075
|
+
"--terminate-running-process",
|
|
3076
|
+
udid,
|
|
3077
|
+
project.config.appId
|
|
3078
|
+
], requireCapturedSuccess = (result, label) => {
|
|
2969
3079
|
if (result.exitCode !== 0) {
|
|
2970
3080
|
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
2971
3081
|
}
|
|
@@ -3088,7 +3198,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3088
3198
|
await rm4(paths.root, { force: true, recursive: true });
|
|
3089
3199
|
return false;
|
|
3090
3200
|
}
|
|
3091
|
-
const journal = await
|
|
3201
|
+
const journal = await readFile6(paths.journal, "utf8").then((source) => parseJournal2(JSON.parse(source))).catch(() => null);
|
|
3092
3202
|
if (!journal || !isInside2(projectRoot, journal.nativeConfigPath) || !isInside2(projectRoot, journal.infoPath) || !isInside2(paths.root, journal.configBackupPath) || !isInside2(paths.root, journal.infoBackupPath)) {
|
|
3093
3203
|
throw new Error(`Refusing unsafe or invalid iOS dev journal at ${paths.journal}.`);
|
|
3094
3204
|
}
|
|
@@ -3117,14 +3227,14 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3117
3227
|
<key>NSAllowsArbitraryLoads</key>
|
|
3118
3228
|
<true/>
|
|
3119
3229
|
</dict>`);
|
|
3120
|
-
}, writeDevProjection = async (project, port, https) => {
|
|
3230
|
+
}, writeDevProjection = async (project, port, https, serverHost = "localhost") => {
|
|
3121
3231
|
const paths = journalPaths2(project.projectRoot);
|
|
3122
3232
|
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
3123
3233
|
const nativeConfigPath = join10(project.nativeDirectory, "App", "App", "capacitor.config.json");
|
|
3124
3234
|
const infoPath = join10(project.nativeDirectory, "App", "App", "Info.plist");
|
|
3125
3235
|
const [configSource, infoSource] = await Promise.all([
|
|
3126
|
-
|
|
3127
|
-
|
|
3236
|
+
readFile6(nativeConfigPath, "utf8"),
|
|
3237
|
+
readFile6(infoPath, "utf8")
|
|
3128
3238
|
]);
|
|
3129
3239
|
const parsed = JSON.parse(configSource);
|
|
3130
3240
|
if (!isRecord3(parsed))
|
|
@@ -3146,6 +3256,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3146
3256
|
flag: "wx"
|
|
3147
3257
|
});
|
|
3148
3258
|
const developmentUrl = new URL(`${https ? "https" : "http"}://localhost:${port}${project.config.entry}`);
|
|
3259
|
+
developmentUrl.hostname = isIP2(serverHost) === 6 ? `[${serverHost}]` : serverHost;
|
|
3149
3260
|
developmentUrl.searchParams.set("__absolute_target", "capacitor-ios");
|
|
3150
3261
|
const existingServer = parsed.server;
|
|
3151
3262
|
parsed.server = {
|
|
@@ -3173,9 +3284,9 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3173
3284
|
String(identity)
|
|
3174
3285
|
]))
|
|
3175
3286
|
};
|
|
3176
|
-
}, readNativeCache2 = (projectRoot) =>
|
|
3287
|
+
}, readNativeCache2 = (projectRoot) => readFile6(nativeCachePath2(projectRoot), "utf8").then((source) => parseNativeCache2(JSON.parse(source))).catch(() => null), writeNativeCache2 = async (projectRoot, cache) => {
|
|
3177
3288
|
const destination = nativeCachePath2(projectRoot);
|
|
3178
|
-
const temporary = `${destination}.${process.pid}.${
|
|
3289
|
+
const temporary = `${destination}.${process.pid}.${randomUUID3()}.tmp`;
|
|
3179
3290
|
await mkdir4(dirname5(destination), { recursive: true });
|
|
3180
3291
|
try {
|
|
3181
3292
|
await writeFile4(temporary, `${JSON.stringify(cache, null, "\t")}
|
|
@@ -3257,6 +3368,29 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3257
3368
|
"app"
|
|
3258
3369
|
]);
|
|
3259
3370
|
return result.exitCode === 0 && result.stdout.trim() ? result.stdout.trim() : undefined;
|
|
3371
|
+
}, validatePhysicalIosDevice = (project, identifier, capture) => {
|
|
3372
|
+
const result = capture([
|
|
3373
|
+
project.xcrun,
|
|
3374
|
+
"devicectl",
|
|
3375
|
+
"device",
|
|
3376
|
+
"info",
|
|
3377
|
+
"details",
|
|
3378
|
+
"--device",
|
|
3379
|
+
identifier
|
|
3380
|
+
]);
|
|
3381
|
+
if (result.exitCode !== 0)
|
|
3382
|
+
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."}`);
|
|
3383
|
+
}, physicalIosAppIsInstalled = (project, identifier, capture) => {
|
|
3384
|
+
const result = capture([
|
|
3385
|
+
project.xcrun,
|
|
3386
|
+
"devicectl",
|
|
3387
|
+
"device",
|
|
3388
|
+
"info",
|
|
3389
|
+
"apps",
|
|
3390
|
+
"--device",
|
|
3391
|
+
identifier
|
|
3392
|
+
]);
|
|
3393
|
+
return result.exitCode === 0 && result.stdout.includes(project.config.appId);
|
|
3260
3394
|
}, buildIosDebugApp = async (project, udid, fingerprint, run, signal) => {
|
|
3261
3395
|
const derivedDataPath = join10(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash4("sha256").update(project.config.appId).digest("hex").slice(0, 16));
|
|
3262
3396
|
await mkdir4(derivedDataPath, { recursive: true });
|
|
@@ -3278,6 +3412,59 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3278
3412
|
if (!await pathExists4(appPath))
|
|
3279
3413
|
throw new Error(`Xcode did not produce the simulator app at ${appPath}.`);
|
|
3280
3414
|
return appPath;
|
|
3415
|
+
}, buildPhysicalIosDebugApp = async (project, identifier, run, signal) => {
|
|
3416
|
+
const derivedDataPath = join10(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash4("sha256").update(project.config.appId).digest("hex").slice(0, 16));
|
|
3417
|
+
await mkdir4(derivedDataPath, { recursive: true });
|
|
3418
|
+
await requireSuccess2([
|
|
3419
|
+
project.xcodebuild,
|
|
3420
|
+
"-workspace",
|
|
3421
|
+
join10(project.nativeDirectory, "App", "App.xcworkspace"),
|
|
3422
|
+
"-scheme",
|
|
3423
|
+
"App",
|
|
3424
|
+
"-configuration",
|
|
3425
|
+
"Debug",
|
|
3426
|
+
"-destination",
|
|
3427
|
+
`platform=iOS,id=${identifier}`,
|
|
3428
|
+
"-derivedDataPath",
|
|
3429
|
+
derivedDataPath,
|
|
3430
|
+
"-allowProvisioningUpdates",
|
|
3431
|
+
"build"
|
|
3432
|
+
], "iOS physical-device build (configure automatic signing and a Development Team in Xcode if this is the first run)", run, { cwd: project.nativeDirectory, signal });
|
|
3433
|
+
const appPath = join10(derivedDataPath, "Build", "Products", "Debug-iphoneos", "App.app");
|
|
3434
|
+
if (!await pathExists4(appPath))
|
|
3435
|
+
throw new Error(`Xcode did not produce the physical-device app at ${appPath}.`);
|
|
3436
|
+
return appPath;
|
|
3437
|
+
}, ensurePhysicalIosDebugApp = async (options) => {
|
|
3438
|
+
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);
|
|
3439
|
+
if (cacheHit) {
|
|
3440
|
+
options.log(`iOS native app is unchanged on the selected physical device; skipped Xcode build and install.`);
|
|
3441
|
+
return true;
|
|
3442
|
+
}
|
|
3443
|
+
options.log("iOS native inputs changed or the physical-device install is stale; rebuilding.");
|
|
3444
|
+
options.transition("building");
|
|
3445
|
+
const appPath = await buildPhysicalIosDebugApp(options.project, options.identifier, options.run, options.signal);
|
|
3446
|
+
throwIfAborted2(options.signal);
|
|
3447
|
+
options.transition("installing");
|
|
3448
|
+
await requireSuccess2([
|
|
3449
|
+
options.project.xcrun,
|
|
3450
|
+
"devicectl",
|
|
3451
|
+
"device",
|
|
3452
|
+
"install",
|
|
3453
|
+
"app",
|
|
3454
|
+
"--device",
|
|
3455
|
+
options.identifier,
|
|
3456
|
+
appPath
|
|
3457
|
+
], "iOS physical-device app installation", options.run, { signal: options.signal });
|
|
3458
|
+
await writeNativeCache2(options.project.projectRoot, {
|
|
3459
|
+
appId: options.project.config.appId,
|
|
3460
|
+
fingerprint: options.fingerprint,
|
|
3461
|
+
format: NATIVE_CACHE_FORMAT2,
|
|
3462
|
+
installations: {
|
|
3463
|
+
...options.cache?.appId === options.project.config.appId ? options.cache.installations : {},
|
|
3464
|
+
[options.identifier]: options.fingerprint
|
|
3465
|
+
}
|
|
3466
|
+
}).catch((error) => options.log(`iOS native cache could not be saved: ${error instanceof Error ? error.message : String(error)}`));
|
|
3467
|
+
return false;
|
|
3281
3468
|
}, ensureIosDebugApp = async (options) => {
|
|
3282
3469
|
const installed = installedAppIdentity(options.project, options.udid, options.capture);
|
|
3283
3470
|
const cacheHit = options.cache?.appId === options.project.config.appId && options.cache.fingerprint === options.fingerprint && installed !== undefined && options.cache.installations[options.udid] === installed;
|
|
@@ -3317,7 +3504,18 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3317
3504
|
if (!options.nativeLog)
|
|
3318
3505
|
return null;
|
|
3319
3506
|
const start = options.startNativeLogs ?? defaultStartNativeLogs;
|
|
3320
|
-
|
|
3507
|
+
const command = options.deviceIdentifier ? [
|
|
3508
|
+
project.xcrun,
|
|
3509
|
+
"devicectl",
|
|
3510
|
+
"device",
|
|
3511
|
+
"process",
|
|
3512
|
+
"launch",
|
|
3513
|
+
"--console",
|
|
3514
|
+
"--terminate-existing",
|
|
3515
|
+
"--device",
|
|
3516
|
+
udid,
|
|
3517
|
+
project.config.appId
|
|
3518
|
+
] : [
|
|
3321
3519
|
project.xcrun,
|
|
3322
3520
|
"simctl",
|
|
3323
3521
|
"spawn",
|
|
@@ -3330,7 +3528,8 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3330
3528
|
"debug",
|
|
3331
3529
|
"--predicate",
|
|
3332
3530
|
'process == "App"'
|
|
3333
|
-
]
|
|
3531
|
+
];
|
|
3532
|
+
return start(command, { signal: options.signal }, (line) => {
|
|
3334
3533
|
const entry = parseAbsoluteIosLogLine(line);
|
|
3335
3534
|
if (entry)
|
|
3336
3535
|
options.nativeLog?.(entry);
|
|
@@ -3340,12 +3539,13 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3340
3539
|
return duration === undefined ? null : `${label} ${getDurationString(duration)}`;
|
|
3341
3540
|
}).filter((value) => value !== null).join(", "), prepareAbsoluteIosDevProject = async (config, options) => {
|
|
3342
3541
|
if (detectAbsoluteMobileHost() !== "macos")
|
|
3343
|
-
throw new Error("iOS
|
|
3542
|
+
throw new Error("iOS development requires macOS and Xcode.");
|
|
3543
|
+
const target = options.target ?? "simulator";
|
|
3344
3544
|
const projectRoot = resolve7(options.projectRoot);
|
|
3345
3545
|
const checks = await inspectAbsoluteMobileToolchain({ host: "macos" });
|
|
3346
|
-
const failed = checks.filter((check) => check.platform === "ios" && (check.status === "fail" || check.status === "warn"));
|
|
3546
|
+
const failed = checks.filter((check) => check.platform === "ios" && !(target === "device" && check.id === "ios.runtime") && (check.status === "fail" || check.status === "warn"));
|
|
3347
3547
|
if (failed.length > 0)
|
|
3348
|
-
throw new Error(`iOS
|
|
3548
|
+
throw new Error(`iOS ${target} development is not ready: ${failed.map(({ label }) => label).join(", ")}.`);
|
|
3349
3549
|
const xcrun = checks.find((check) => check.id === "ios.xcrun")?.path;
|
|
3350
3550
|
const xcodebuild = checks.find((check) => check.id === "ios.xcodebuild")?.path;
|
|
3351
3551
|
if (!xcrun || !xcodebuild)
|
|
@@ -3375,8 +3575,53 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3375
3575
|
xcodebuild,
|
|
3376
3576
|
xcrun
|
|
3377
3577
|
};
|
|
3578
|
+
}, preparePhysicalIosTarget = async (options) => {
|
|
3579
|
+
options.transition("connecting");
|
|
3580
|
+
validatePhysicalIosDevice(options.project, options.deviceIdentifier, options.capture);
|
|
3581
|
+
if (!options.startOptions.https)
|
|
3582
|
+
return {
|
|
3583
|
+
caEnrollmentServer: null,
|
|
3584
|
+
startedSimulator: false,
|
|
3585
|
+
udid: options.deviceIdentifier
|
|
3586
|
+
};
|
|
3587
|
+
if (!options.startOptions.certificateAuthorityPath)
|
|
3588
|
+
throw new Error("Physical iOS HTTPS development requires the AbsoluteJS development CA certificate.");
|
|
3589
|
+
options.transition("enrolling-trust");
|
|
3590
|
+
const startEnrollment = options.startOptions.startCaEnrollmentServer ?? startAbsoluteIosCaEnrollmentServer;
|
|
3591
|
+
const caEnrollmentServer = await startEnrollment({
|
|
3592
|
+
certificateAuthorityPath: options.startOptions.certificateAuthorityPath,
|
|
3593
|
+
displayHost: options.serverHost
|
|
3594
|
+
});
|
|
3595
|
+
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.`);
|
|
3596
|
+
return {
|
|
3597
|
+
caEnrollmentServer,
|
|
3598
|
+
startedSimulator: false,
|
|
3599
|
+
udid: options.deviceIdentifier
|
|
3600
|
+
};
|
|
3601
|
+
}, prepareIosSimulatorTarget = async (options) => {
|
|
3602
|
+
options.transition("booting");
|
|
3603
|
+
const managed = await ensureManagedSimulator(options.project, options.capture);
|
|
3604
|
+
const { device } = managed;
|
|
3605
|
+
const startedSimulator = managed.created || device.state !== "Booted";
|
|
3606
|
+
bootSimulator(options.project, device, options.capture);
|
|
3607
|
+
options.spawn([
|
|
3608
|
+
"open",
|
|
3609
|
+
"-a",
|
|
3610
|
+
"Simulator",
|
|
3611
|
+
"--args",
|
|
3612
|
+
"-CurrentDeviceUDID",
|
|
3613
|
+
device.udid
|
|
3614
|
+
]);
|
|
3615
|
+
options.transition("connecting");
|
|
3616
|
+
await waitForBootedSimulator(options.project, device.udid, options.capture, options.sleep, options.startOptions.signal);
|
|
3617
|
+
await requireSuccess2([options.project.xcrun, "simctl", "bootstatus", device.udid, "-b"], "iOS simulator boot readiness", options.run, { signal: options.startOptions.signal });
|
|
3618
|
+
await trustIosSimulatorDevelopmentCa(options.startOptions, device.udid, options.run, options.log);
|
|
3619
|
+
return { caEnrollmentServer: null, startedSimulator, udid: device.udid };
|
|
3378
3620
|
}, startAbsoluteIosDevSession = async (options) => {
|
|
3379
3621
|
const { project } = options;
|
|
3622
|
+
const deviceIdentifier = options.deviceIdentifier ? normalizeAbsoluteIosDeviceIdentifier(options.deviceIdentifier) : undefined;
|
|
3623
|
+
const targetKind = deviceIdentifier ? "device" : "simulator";
|
|
3624
|
+
const serverHost = deviceIdentifier ? normalizeAbsoluteIosDeviceHost(options.serverHost ?? "") : "localhost";
|
|
3380
3625
|
const capture = options.capture ?? defaultCapture3;
|
|
3381
3626
|
const run = options.run ?? defaultRun3;
|
|
3382
3627
|
const sleep = options.sleep ?? Bun.sleep;
|
|
@@ -3404,6 +3649,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3404
3649
|
options.onStateChange?.(next);
|
|
3405
3650
|
};
|
|
3406
3651
|
let nativeLogs = null;
|
|
3652
|
+
let caEnrollmentServer = null;
|
|
3407
3653
|
const closeLogs = async () => {
|
|
3408
3654
|
const stream = nativeLogs;
|
|
3409
3655
|
nativeLogs = null;
|
|
@@ -3411,39 +3657,66 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3411
3657
|
return;
|
|
3412
3658
|
});
|
|
3413
3659
|
};
|
|
3660
|
+
const relaunchTarget = async (udid) => {
|
|
3661
|
+
if (deviceIdentifier && options.nativeLog) {
|
|
3662
|
+
await closeLogs();
|
|
3663
|
+
nativeLogs = attachNativeLogs(project, udid, options);
|
|
3664
|
+
return;
|
|
3665
|
+
}
|
|
3666
|
+
await requireSuccess2(iosLaunchCommand(project, udid, deviceIdentifier !== undefined), "iOS app relaunch", run, { signal: options.signal });
|
|
3667
|
+
};
|
|
3414
3668
|
try {
|
|
3415
3669
|
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
3416
3670
|
throwIfAborted2(options.signal);
|
|
3417
3671
|
transition("syncing");
|
|
3418
3672
|
await requireSuccess2([project.cap, "sync", "ios"], "Capacitor iOS synchronization", run, { cwd: project.projectRoot, signal: options.signal });
|
|
3419
3673
|
transition("configuring");
|
|
3420
|
-
await writeDevProjection(project, options.port, options.https === true);
|
|
3674
|
+
await writeDevProjection(project, options.port, options.https === true, serverHost);
|
|
3421
3675
|
throwIfAborted2(options.signal);
|
|
3422
3676
|
const fingerprintStartedAt = performance.now();
|
|
3423
3677
|
const fingerprintPromise = fingerprintAbsoluteIosDevProject(project).then((fingerprint2) => {
|
|
3424
3678
|
timings.fingerprinting = performance.now() - fingerprintStartedAt;
|
|
3425
3679
|
return fingerprint2;
|
|
3426
3680
|
});
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3681
|
+
const target = deviceIdentifier ? await preparePhysicalIosTarget({
|
|
3682
|
+
capture,
|
|
3683
|
+
deviceIdentifier,
|
|
3684
|
+
log,
|
|
3685
|
+
project,
|
|
3686
|
+
serverHost,
|
|
3687
|
+
startOptions: options,
|
|
3688
|
+
transition
|
|
3689
|
+
}) : await prepareIosSimulatorTarget({
|
|
3690
|
+
capture,
|
|
3691
|
+
log,
|
|
3692
|
+
project,
|
|
3693
|
+
run,
|
|
3694
|
+
sleep,
|
|
3695
|
+
spawn,
|
|
3696
|
+
startOptions: options,
|
|
3697
|
+
transition
|
|
3698
|
+
});
|
|
3699
|
+
const {
|
|
3700
|
+
caEnrollmentServer: targetCaEnrollmentServer,
|
|
3701
|
+
startedSimulator,
|
|
3702
|
+
udid
|
|
3703
|
+
} = target;
|
|
3704
|
+
caEnrollmentServer = targetCaEnrollmentServer;
|
|
3443
3705
|
const fingerprint = await fingerprintPromise;
|
|
3444
3706
|
transition("checking-native");
|
|
3445
|
-
const
|
|
3446
|
-
|
|
3707
|
+
const cache = await readNativeCache2(project.projectRoot);
|
|
3708
|
+
const nativeCacheHit = deviceIdentifier ? await ensurePhysicalIosDebugApp({
|
|
3709
|
+
cache,
|
|
3710
|
+
capture,
|
|
3711
|
+
fingerprint,
|
|
3712
|
+
identifier: deviceIdentifier,
|
|
3713
|
+
log,
|
|
3714
|
+
project,
|
|
3715
|
+
run,
|
|
3716
|
+
signal: options.signal,
|
|
3717
|
+
transition
|
|
3718
|
+
}) : await ensureIosDebugApp({
|
|
3719
|
+
cache,
|
|
3447
3720
|
capture,
|
|
3448
3721
|
fingerprint,
|
|
3449
3722
|
log,
|
|
@@ -3451,24 +3724,18 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3451
3724
|
run,
|
|
3452
3725
|
signal: options.signal,
|
|
3453
3726
|
transition,
|
|
3454
|
-
udid
|
|
3727
|
+
udid
|
|
3455
3728
|
});
|
|
3456
3729
|
throwIfAborted2(options.signal);
|
|
3457
3730
|
if (options.nativeLog)
|
|
3458
3731
|
transition("streaming-logs");
|
|
3459
|
-
nativeLogs = attachNativeLogs(project,
|
|
3732
|
+
nativeLogs = attachNativeLogs(project, udid, options);
|
|
3460
3733
|
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 });
|
|
3734
|
+
if (!deviceIdentifier || !nativeLogs)
|
|
3735
|
+
await requireSuccess2(iosLaunchCommand(project, udid, deviceIdentifier !== undefined), "iOS app launch", run, { signal: options.signal });
|
|
3469
3736
|
transition("ready");
|
|
3470
3737
|
timings.total = performance.now() - startedAt;
|
|
3471
|
-
log(`iOS
|
|
3738
|
+
log(`iOS ${targetKind} connected with HMR on port ${options.port} in ${getDurationString(timings.total)} (${nativeCacheHit ? "native cache hit" : "native build installed"}).`);
|
|
3472
3739
|
log(`iOS startup: ${timingSummary(timings)}.`);
|
|
3473
3740
|
let closed = false;
|
|
3474
3741
|
const close = async () => {
|
|
@@ -3477,6 +3744,10 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3477
3744
|
closed = true;
|
|
3478
3745
|
transition("closing");
|
|
3479
3746
|
await closeLogs();
|
|
3747
|
+
await caEnrollmentServer?.close().catch(() => {
|
|
3748
|
+
return;
|
|
3749
|
+
});
|
|
3750
|
+
caEnrollmentServer = null;
|
|
3480
3751
|
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
3481
3752
|
transition("closed");
|
|
3482
3753
|
};
|
|
@@ -3484,8 +3755,9 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3484
3755
|
close,
|
|
3485
3756
|
nativeCacheHit,
|
|
3486
3757
|
startedSimulator,
|
|
3758
|
+
targetKind,
|
|
3487
3759
|
timings: { ...timings },
|
|
3488
|
-
udid
|
|
3760
|
+
udid,
|
|
3489
3761
|
rebuild: async () => {
|
|
3490
3762
|
if (closed)
|
|
3491
3763
|
throw new Error("iOS development session is closed.");
|
|
@@ -3498,22 +3770,17 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3498
3770
|
throw new Error("iOS development session is closed.");
|
|
3499
3771
|
transition("launching");
|
|
3500
3772
|
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 });
|
|
3773
|
+
await relaunchTarget(udid);
|
|
3509
3774
|
transition("ready");
|
|
3510
|
-
log(`iOS app relaunched on ${
|
|
3775
|
+
log(`iOS app relaunched on the selected ${targetKind}.`);
|
|
3511
3776
|
} catch (error) {
|
|
3512
3777
|
transition("failed");
|
|
3513
3778
|
throw error;
|
|
3514
3779
|
}
|
|
3515
3780
|
},
|
|
3516
3781
|
screenshot: async (destination) => {
|
|
3782
|
+
if (deviceIdentifier)
|
|
3783
|
+
throw new Error("Physical iOS screenshots are captured in Xcode Device Hub; the CLI never records a device screen automatically.");
|
|
3517
3784
|
const resolved = resolve7(project.projectRoot, destination);
|
|
3518
3785
|
if (!isInside2(project.projectRoot, resolved))
|
|
3519
3786
|
throw new Error("iOS screenshot destination must remain inside the project.");
|
|
@@ -3522,7 +3789,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3522
3789
|
project.xcrun,
|
|
3523
3790
|
"simctl",
|
|
3524
3791
|
"io",
|
|
3525
|
-
|
|
3792
|
+
udid,
|
|
3526
3793
|
"screenshot",
|
|
3527
3794
|
resolved
|
|
3528
3795
|
], "iOS simulator screenshot", run, { signal: options.signal });
|
|
@@ -3535,6 +3802,9 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
3535
3802
|
} catch (error) {
|
|
3536
3803
|
transition("failed");
|
|
3537
3804
|
await closeLogs();
|
|
3805
|
+
await caEnrollmentServer?.close().catch(() => {
|
|
3806
|
+
return;
|
|
3807
|
+
});
|
|
3538
3808
|
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
3539
3809
|
throw error;
|
|
3540
3810
|
}
|
|
@@ -3543,6 +3813,7 @@ var init_iosSimulatorController = __esm(() => {
|
|
|
3543
3813
|
init_emulatorDoctor();
|
|
3544
3814
|
init_capacitorProject();
|
|
3545
3815
|
init_iosRelease();
|
|
3816
|
+
init_iosPhysicalDeviceTransport();
|
|
3546
3817
|
init_getDurationString();
|
|
3547
3818
|
SECRET_VALUE = /((?:authorization|cookie|password|secret|token|oauth[_-]?code)\s*[:=]\s*)([^\s,;]+)/giu;
|
|
3548
3819
|
BEARER_VALUE = new RegExp(String.raw`\bBearer\s+[A-Za-z0-9._~+/-]+=*`, "giu");
|
|
@@ -3554,6 +3825,7 @@ var init_iosSimulatorController = __esm(() => {
|
|
|
3554
3825
|
["fingerprinting", "fingerprint"],
|
|
3555
3826
|
["booting", "simulator"],
|
|
3556
3827
|
["connecting", "device ready"],
|
|
3828
|
+
["enrolling-trust", "HTTPS trust"],
|
|
3557
3829
|
["checking-native", "app check"],
|
|
3558
3830
|
["building", "Xcode"],
|
|
3559
3831
|
["installing", "install"],
|
|
@@ -3566,9 +3838,10 @@ var init_iosSimulatorController = __esm(() => {
|
|
|
3566
3838
|
var ABSOLUTE_REMOTE_MAC_EVENT_PREFIX = "ABSOLUTE_REMOTE_MAC\t", ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION = 1;
|
|
3567
3839
|
|
|
3568
3840
|
// src/mobile/remoteMacProtocol.ts
|
|
3569
|
-
import { createHash as createHash5, randomUUID as
|
|
3570
|
-
import { chmod, mkdir as mkdir5, readFile as
|
|
3841
|
+
import { createHash as createHash5, randomUUID as randomUUID4 } from "crypto";
|
|
3842
|
+
import { chmod, mkdir as mkdir5, readFile as readFile7, rename as rename5, writeFile as writeFile5 } from "fs/promises";
|
|
3571
3843
|
import { homedir as homedir4 } from "os";
|
|
3844
|
+
import { isIP as isIP3 } from "net";
|
|
3572
3845
|
import {
|
|
3573
3846
|
dirname as dirname6,
|
|
3574
3847
|
isAbsolute as isAbsolute4,
|
|
@@ -3583,7 +3856,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3583
3856
|
profiles: {}
|
|
3584
3857
|
}), loadStore = async (path = defaultProfilePath()) => {
|
|
3585
3858
|
try {
|
|
3586
|
-
const parsed = JSON.parse(await
|
|
3859
|
+
const parsed = JSON.parse(await readFile7(path, "utf8"));
|
|
3587
3860
|
if (parsed.format !== PROFILE_FORMAT || typeof parsed.profiles !== "object" || parsed.profiles === null || Array.isArray(parsed.profiles))
|
|
3588
3861
|
throw new Error("Unsupported remote Mac profile format.");
|
|
3589
3862
|
for (const [key, profile] of Object.entries(parsed.profiles)) {
|
|
@@ -3600,7 +3873,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3600
3873
|
}
|
|
3601
3874
|
}, saveStore = async (store, path = defaultProfilePath()) => {
|
|
3602
3875
|
await mkdir5(dirname6(path), { recursive: true });
|
|
3603
|
-
const temporary = `${path}.${
|
|
3876
|
+
const temporary = `${path}.${randomUUID4()}.tmp`;
|
|
3604
3877
|
await writeFile5(temporary, `${JSON.stringify(store, null, 2)}
|
|
3605
3878
|
`, {
|
|
3606
3879
|
mode: 384
|
|
@@ -3651,6 +3924,17 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3651
3924
|
if (result.exitCode !== 0)
|
|
3652
3925
|
throw new Error(`${label} failed: ${(result.stderr || result.stdout).trim() || `status ${result.exitCode}`}`);
|
|
3653
3926
|
return result.stdout.trim();
|
|
3927
|
+
}, captureAbsoluteRemoteMacCommand = async (profile, command, transport) => {
|
|
3928
|
+
if (command.length === 0)
|
|
3929
|
+
throw new TypeError("A Remote Mac command cannot be empty.");
|
|
3930
|
+
if (command.some((argument) => /[\r\n\0]/u.test(argument)))
|
|
3931
|
+
throw new TypeError("Remote Mac command arguments cannot contain controls.");
|
|
3932
|
+
const capture = transport?.capture ?? defaultTransport.capture;
|
|
3933
|
+
return capture([
|
|
3934
|
+
...absoluteRemoteMacSshBase(profile),
|
|
3935
|
+
"/bin/sh -lc",
|
|
3936
|
+
shellQuote(command.map((argument) => shellQuote(argument)).join(" "))
|
|
3937
|
+
]);
|
|
3654
3938
|
}, getAbsoluteRemoteMacProfile = async (name, profilePath) => {
|
|
3655
3939
|
const store = await loadStore(profilePath);
|
|
3656
3940
|
const selected = name ?? process.env.ABSOLUTE_IOS_REMOTE ?? store.defaultProfile;
|
|
@@ -3682,6 +3966,18 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3682
3966
|
if (!xcodeVersion?.startsWith("Xcode "))
|
|
3683
3967
|
throw new Error("The remote Mac must have full Xcode installed and selected.");
|
|
3684
3968
|
return { bunPath, home, os: operatingSystem, xcodeVersion };
|
|
3969
|
+
}, inspectAbsoluteRemoteMacLanHost = async (profile, transport) => {
|
|
3970
|
+
const capture = transport?.capture ?? defaultTransport.capture;
|
|
3971
|
+
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`;
|
|
3972
|
+
const result = await capture([
|
|
3973
|
+
...absoluteRemoteMacSshBase(profile),
|
|
3974
|
+
"/bin/sh -lc",
|
|
3975
|
+
shellQuote(script)
|
|
3976
|
+
]);
|
|
3977
|
+
const host = requireRemoteSuccess(result, "Remote Mac LAN address discovery").trim();
|
|
3978
|
+
if (isIP3(host) === 0)
|
|
3979
|
+
throw new Error("The Remote Mac did not report a device-reachable LAN address.");
|
|
3980
|
+
return host;
|
|
3685
3981
|
}, listAbsoluteRemoteMacProfiles = async (profilePath) => {
|
|
3686
3982
|
const store = await loadStore(profilePath);
|
|
3687
3983
|
return {
|
|
@@ -3748,7 +4044,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3748
4044
|
]);
|
|
3749
4045
|
if (verified.exitCode === 0)
|
|
3750
4046
|
return { ...artifact, remotePath, uploaded: false };
|
|
3751
|
-
const temporary = posix.join(directory, `.agent-${
|
|
4047
|
+
const temporary = posix.join(directory, `.agent-${randomUUID4()}.tmp`);
|
|
3752
4048
|
const installScript = [
|
|
3753
4049
|
"set -eu",
|
|
3754
4050
|
"umask 077",
|
|
@@ -3835,7 +4131,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3835
4131
|
}), absoluteRemoteProjectSyncCommands = (project) => {
|
|
3836
4132
|
const current = project.remoteProjectRoot;
|
|
3837
4133
|
const parent = posix.dirname(current);
|
|
3838
|
-
const staging = posix.join(parent, `.incoming-${
|
|
4134
|
+
const staging = posix.join(parent, `.incoming-${randomUUID4()}`);
|
|
3839
4135
|
const previous = posix.join(parent, ".previous");
|
|
3840
4136
|
const script = [
|
|
3841
4137
|
"set -eu",
|
|
@@ -3924,7 +4220,13 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3924
4220
|
await syncProject(options.project);
|
|
3925
4221
|
const syncDuration = performance.now() - syncStartedAt;
|
|
3926
4222
|
const encodedConfig = Buffer.from(JSON.stringify(portableMobileConfig(options.project))).toString("base64url");
|
|
3927
|
-
const encodedCertificateAuthority = options.certificateAuthorityPath ? (await
|
|
4223
|
+
const encodedCertificateAuthority = options.certificateAuthorityPath ? (await readFile7(options.certificateAuthorityPath)).toString("base64url") : undefined;
|
|
4224
|
+
const physicalDevice = options.deviceIdentifier !== undefined;
|
|
4225
|
+
let relayPort;
|
|
4226
|
+
if (physicalDevice)
|
|
4227
|
+
relayPort = options.port <= 49151 ? options.port + 16384 : options.port - 16384;
|
|
4228
|
+
if (physicalDevice && !options.serverHost)
|
|
4229
|
+
throw new Error("Remote physical iOS development requires the Remote Mac LAN host.");
|
|
3928
4230
|
const remoteCommand = [
|
|
3929
4231
|
`cd ${shellQuote(options.project.remoteProjectRoot)}`,
|
|
3930
4232
|
"&&",
|
|
@@ -3939,14 +4241,22 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
3939
4241
|
"--certificate-authority",
|
|
3940
4242
|
shellQuote(encodedCertificateAuthority)
|
|
3941
4243
|
] : [],
|
|
3942
|
-
...options.https ? ["--https"] : []
|
|
4244
|
+
...options.https ? ["--https"] : [],
|
|
4245
|
+
...options.deviceIdentifier ? [
|
|
4246
|
+
"--ios-device",
|
|
4247
|
+
shellQuote(options.deviceIdentifier),
|
|
4248
|
+
"--server-host",
|
|
4249
|
+
shellQuote(options.serverHost ?? ""),
|
|
4250
|
+
"--relay-port",
|
|
4251
|
+
String(relayPort)
|
|
4252
|
+
] : []
|
|
3943
4253
|
].join(" ");
|
|
3944
4254
|
const command = [
|
|
3945
4255
|
...absoluteRemoteMacSshBase(options.project.profile),
|
|
3946
4256
|
"-o",
|
|
3947
4257
|
"ExitOnForwardFailure=yes",
|
|
3948
4258
|
"-R",
|
|
3949
|
-
`${options.port}:127.0.0.1:${options.port}`,
|
|
4259
|
+
physicalDevice ? `127.0.0.1:${relayPort}:127.0.0.1:${options.port}` : `${options.port}:127.0.0.1:${options.port}`,
|
|
3950
4260
|
"/bin/sh -lc",
|
|
3951
4261
|
shellQuote(remoteCommand)
|
|
3952
4262
|
];
|
|
@@ -4034,7 +4344,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4034
4344
|
};
|
|
4035
4345
|
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
4346
|
const request = (commandName) => {
|
|
4037
|
-
const id =
|
|
4347
|
+
const id = randomUUID4();
|
|
4038
4348
|
const response = new Promise((resolve8, reject) => pending.set(id, { reject, resolve: resolve8 }));
|
|
4039
4349
|
process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: 1 })}
|
|
4040
4350
|
`);
|
|
@@ -4067,6 +4377,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, PROFILE_NAME, SSH_DESTI
|
|
|
4067
4377
|
close,
|
|
4068
4378
|
nativeCacheHit: currentReady.nativeCacheHit,
|
|
4069
4379
|
startedSimulator: currentReady.startedSimulator,
|
|
4380
|
+
targetKind: currentReady.targetKind,
|
|
4070
4381
|
timings: currentReady.timings,
|
|
4071
4382
|
udid: currentReady.udid,
|
|
4072
4383
|
rebuild: async () => {
|
|
@@ -4136,8 +4447,8 @@ import {
|
|
|
4136
4447
|
readFileSync as readFileSync7,
|
|
4137
4448
|
rmSync
|
|
4138
4449
|
} from "fs";
|
|
4139
|
-
import { X509Certificate } from "crypto";
|
|
4140
|
-
import { isIP } from "net";
|
|
4450
|
+
import { X509Certificate as X509Certificate2 } from "crypto";
|
|
4451
|
+
import { isIP as isIP4 } from "net";
|
|
4141
4452
|
import { platform as platform2 } from "os";
|
|
4142
4453
|
import { join as join12 } from "path";
|
|
4143
4454
|
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 +4457,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
|
|
|
4146
4457
|
const value = host.trim().toLowerCase();
|
|
4147
4458
|
if (!value || value === "0.0.0.0" || value === "::")
|
|
4148
4459
|
continue;
|
|
4149
|
-
if (
|
|
4460
|
+
if (isIP4(value) === 0 && !CERTIFICATE_HOSTNAME_PATTERN.test(value)) {
|
|
4150
4461
|
throw new TypeError(`Invalid development certificate host: ${host}`);
|
|
4151
4462
|
}
|
|
4152
4463
|
normalized.add(value);
|
|
@@ -4155,10 +4466,10 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
|
|
|
4155
4466
|
}, certificateIsUsable = (hosts) => {
|
|
4156
4467
|
try {
|
|
4157
4468
|
const certPem = readFileSync7(CERT_PATH, "utf-8");
|
|
4158
|
-
const certificate = new
|
|
4469
|
+
const certificate = new X509Certificate2(certPem);
|
|
4159
4470
|
if (new Date(certificate.validTo).getTime() <= Date.now())
|
|
4160
4471
|
return false;
|
|
4161
|
-
return normalizeDevCertificateHosts(hosts).every((host) =>
|
|
4472
|
+
return normalizeDevCertificateHosts(hosts).every((host) => isIP4(host) ? certificate.checkIP(host) !== undefined : certificate.checkHost(host) !== undefined);
|
|
4162
4473
|
} catch {
|
|
4163
4474
|
return false;
|
|
4164
4475
|
}
|
|
@@ -4186,7 +4497,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
|
|
|
4186
4497
|
throw new Error(`mkcert failed: ${err}`);
|
|
4187
4498
|
}
|
|
4188
4499
|
}, generateSelfSigned = (hosts = []) => {
|
|
4189
|
-
const subjectAlternativeNames = normalizeDevCertificateHosts(hosts).map((host) => `${
|
|
4500
|
+
const subjectAlternativeNames = normalizeDevCertificateHosts(hosts).map((host) => `${isIP4(host) ? "IP" : "DNS"}:${host}`).join(",");
|
|
4190
4501
|
const proc = Bun.spawnSync([
|
|
4191
4502
|
"openssl",
|
|
4192
4503
|
"req",
|
|
@@ -5834,7 +6145,7 @@ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-
|
|
|
5834
6145
|
|
|
5835
6146
|
// src/mobile/buildRelease.ts
|
|
5836
6147
|
import { createHash as createHash8 } from "crypto";
|
|
5837
|
-
import { mkdir as mkdir7, readFile as
|
|
6148
|
+
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
5838
6149
|
import { basename as basename6, dirname as dirname9, extname as extname3, join as join16, relative as relative9, resolve as resolve13 } from "path";
|
|
5839
6150
|
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
6151
|
if (path.endsWith("/htmx.min.js"))
|
|
@@ -5856,7 +6167,7 @@ var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex"), STATI
|
|
|
5856
6167
|
}
|
|
5857
6168
|
let resolvedAssetPath = resolveAssetPath(buildDirectory, assetPath);
|
|
5858
6169
|
if (metadata.framework === "html" || metadata.framework === "htmx") {
|
|
5859
|
-
const source = await
|
|
6170
|
+
const source = await readFile8(resolvedAssetPath, "utf8");
|
|
5860
6171
|
const rewritten = rewriteStaticScriptPaths(source, manifest);
|
|
5861
6172
|
const documentHash = sha256(new TextEncoder().encode(rewritten));
|
|
5862
6173
|
resolvedAssetPath = join16(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
|
|
@@ -5870,8 +6181,8 @@ var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex"), STATI
|
|
|
5870
6181
|
].map((key) => manifest[key]).find((path) => typeof path === "string");
|
|
5871
6182
|
const resolvedStylePath = styleAssetPath ? resolveAssetPath(buildDirectory, styleAssetPath) : undefined;
|
|
5872
6183
|
const [bytes, styleBytes] = await Promise.all([
|
|
5873
|
-
|
|
5874
|
-
resolvedStylePath ?
|
|
6184
|
+
readFile8(resolvedAssetPath),
|
|
6185
|
+
resolvedStylePath ? readFile8(resolvedStylePath) : undefined
|
|
5875
6186
|
]);
|
|
5876
6187
|
const bundlePath = `/${relative9(resolve13(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
|
|
5877
6188
|
const styleBundlePath = resolvedStylePath ? `/${relative9(resolve13(buildDirectory), resolvedStylePath).replaceAll("\\", "/")}` : undefined;
|
|
@@ -5890,7 +6201,7 @@ var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex"), STATI
|
|
|
5890
6201
|
}, buildAbsoluteMobileCompatibilityRelease = async (options) => {
|
|
5891
6202
|
const [captured, producerBytes] = await Promise.all([
|
|
5892
6203
|
captureAbsoluteMobileRouteGraph(options.app),
|
|
5893
|
-
|
|
6204
|
+
readFile8(options.producerPath)
|
|
5894
6205
|
]);
|
|
5895
6206
|
if (captured.length === 0) {
|
|
5896
6207
|
throw new TypeError("No instrumented AbsoluteJS mobile page routes were found in the finalized Elysia route graph.");
|
|
@@ -6070,7 +6381,7 @@ import {
|
|
|
6070
6381
|
copyFile as copyFile4,
|
|
6071
6382
|
mkdir as mkdir8,
|
|
6072
6383
|
mkdtemp as mkdtemp3,
|
|
6073
|
-
readFile as
|
|
6384
|
+
readFile as readFile9,
|
|
6074
6385
|
rename as rename6,
|
|
6075
6386
|
rm as rm5,
|
|
6076
6387
|
writeFile as writeFile7
|
|
@@ -6128,7 +6439,7 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
|
|
|
6128
6439
|
const packageName = specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? "";
|
|
6129
6440
|
const subpath = specifier.slice(packageName.length);
|
|
6130
6441
|
const packageDirectory = join17(resolve14(projectRoot), "node_modules", packageName);
|
|
6131
|
-
const manifest = JSON.parse(await
|
|
6442
|
+
const manifest = JSON.parse(await readFile9(join17(packageDirectory, "package.json"), "utf8"));
|
|
6132
6443
|
const exports = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "exports") : undefined;
|
|
6133
6444
|
const entry = typeof exports === "object" && exports !== null ? Reflect.get(exports, subpath ? `.${subpath}` : ".") : undefined;
|
|
6134
6445
|
const target = importEntryTarget(entry);
|
|
@@ -6230,7 +6541,7 @@ void startAbsoluteMobileShell(${push ? `{ createAuth: (config, options) => creat
|
|
|
6230
6541
|
...localStylePath ? { localStylePath } : {}
|
|
6231
6542
|
};
|
|
6232
6543
|
}, absoluteClientImports = async (sourcePath, buildDirectory) => {
|
|
6233
|
-
const source = await
|
|
6544
|
+
const source = await readFile9(sourcePath, "utf8");
|
|
6234
6545
|
const extension = extname4(sourcePath).toLowerCase();
|
|
6235
6546
|
let scriptLoader;
|
|
6236
6547
|
if (extension === ".tsx")
|
|
@@ -6372,7 +6683,7 @@ import {
|
|
|
6372
6683
|
access as access6,
|
|
6373
6684
|
mkdir as mkdir9,
|
|
6374
6685
|
mkdtemp as mkdtemp4,
|
|
6375
|
-
readFile as
|
|
6686
|
+
readFile as readFile10,
|
|
6376
6687
|
rename as rename7,
|
|
6377
6688
|
rm as rm6,
|
|
6378
6689
|
writeFile as writeFile8
|
|
@@ -6472,7 +6783,7 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
|
|
|
6472
6783
|
}, readAbsoluteMobileMaterializedReleases = async (root) => {
|
|
6473
6784
|
const resolvedRoot = resolvePath2(root);
|
|
6474
6785
|
try {
|
|
6475
|
-
const serialized = await
|
|
6786
|
+
const serialized = await readFile10(join18(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
|
|
6476
6787
|
const parsed = JSON.parse(serialized);
|
|
6477
6788
|
const index = parseBundleIndex(parsed);
|
|
6478
6789
|
const bundleRoot = join18(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
@@ -7174,7 +7485,7 @@ var init_deviceCapabilities = __esm(() => {
|
|
|
7174
7485
|
});
|
|
7175
7486
|
|
|
7176
7487
|
// src/mobile/buildPipeline.ts
|
|
7177
|
-
import { readFile as
|
|
7488
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
7178
7489
|
import { join as join21, resolve as resolve17 } from "path";
|
|
7179
7490
|
import { pathToFileURL } from "url";
|
|
7180
7491
|
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 +7519,7 @@ var isElysiaApp = (value) => typeof value === "object" && value !== null && type
|
|
|
7208
7519
|
const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
|
|
7209
7520
|
const root = join21(buildDirectory, ".absolutejs", "mobile-compatibility");
|
|
7210
7521
|
const [manifestSource, previous] = await Promise.all([
|
|
7211
|
-
|
|
7522
|
+
readFile11(join21(buildDirectory, "manifest.json"), "utf8"),
|
|
7212
7523
|
readAbsoluteMobileMaterializedReleases(root)
|
|
7213
7524
|
]);
|
|
7214
7525
|
const manifest = JSON.parse(manifestSource);
|
|
@@ -10914,8 +11225,8 @@ export { value };
|
|
|
10914
11225
|
host2.getSourceFile = (fileName, languageVersion, onError, shouldCreate) => fileName === virtualPath ? ts6.createSourceFile(fileName, source, languageVersion, true) : getSourceFile(fileName, languageVersion, onError, shouldCreate);
|
|
10915
11226
|
const fileExists = host2.fileExists.bind(host2);
|
|
10916
11227
|
host2.fileExists = (fileName) => fileName === virtualPath ? true : fileExists(fileName);
|
|
10917
|
-
const
|
|
10918
|
-
host2.readFile = (fileName) => fileName === virtualPath ? source :
|
|
11228
|
+
const readFile12 = host2.readFile.bind(host2);
|
|
11229
|
+
host2.readFile = (fileName) => fileName === virtualPath ? source : readFile12(fileName);
|
|
10919
11230
|
const program = ts6.createProgram([virtualPath], options, host2);
|
|
10920
11231
|
const checker = program.getTypeChecker();
|
|
10921
11232
|
const sourceFile = program.getSourceFile(virtualPath);
|
|
@@ -16538,10 +16849,10 @@ var init_compile = __esm(() => {
|
|
|
16538
16849
|
});
|
|
16539
16850
|
|
|
16540
16851
|
// src/mobile/nativeDeepLinks.ts
|
|
16541
|
-
import { readFile as
|
|
16852
|
+
import { readFile as readFile12, rename as rename8, writeFile as writeFile9 } from "fs/promises";
|
|
16542
16853
|
import { join as join46 } from "path";
|
|
16543
16854
|
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
|
|
16855
|
+
const current = await readFile12(path, "utf8");
|
|
16545
16856
|
if (current === source)
|
|
16546
16857
|
return false;
|
|
16547
16858
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
@@ -16589,7 +16900,7 @@ ${hosts}
|
|
|
16589
16900
|
`;
|
|
16590
16901
|
}, configureAndroid = async (config) => {
|
|
16591
16902
|
const path = join46(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
16592
|
-
const source = await
|
|
16903
|
+
const source = await readFile12(path, "utf8");
|
|
16593
16904
|
const mainActivity = source.indexOf('android:name=".MainActivity"');
|
|
16594
16905
|
if (mainActivity === NOT_FOUND) {
|
|
16595
16906
|
throw new TypeError("Android MainActivity was not found.");
|
|
@@ -16613,7 +16924,7 @@ ${hosts}
|
|
|
16613
16924
|
${END_MARKER}
|
|
16614
16925
|
`, configureIosInfo = async (config) => {
|
|
16615
16926
|
const path = join46(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
16616
|
-
const source = await
|
|
16927
|
+
const source = await readFile12(path, "utf8");
|
|
16617
16928
|
const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
|
|
16618
16929
|
${END_MARKER}
|
|
16619
16930
|
`;
|
|
@@ -16637,7 +16948,7 @@ ${domains}
|
|
|
16637
16948
|
const path = join46(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
16638
16949
|
let current = "";
|
|
16639
16950
|
try {
|
|
16640
|
-
current = await
|
|
16951
|
+
current = await readFile12(path, "utf8");
|
|
16641
16952
|
} catch (error) {
|
|
16642
16953
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
16643
16954
|
throw error;
|
|
@@ -16652,7 +16963,7 @@ ${domains}
|
|
|
16652
16963
|
return true;
|
|
16653
16964
|
}, configureIosProject = async (config) => {
|
|
16654
16965
|
const path = join46(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
16655
|
-
const source = await
|
|
16966
|
+
const source = await readFile12(path, "utf8");
|
|
16656
16967
|
const declarations = [
|
|
16657
16968
|
...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
|
|
16658
16969
|
].map((match) => match[1]);
|
|
@@ -16690,10 +17001,10 @@ ${domains}
|
|
|
16690
17001
|
var init_nativeDeepLinks = () => {};
|
|
16691
17002
|
|
|
16692
17003
|
// src/mobile/nativeDeviceCapabilities.ts
|
|
16693
|
-
import { readFile as
|
|
17004
|
+
import { readFile as readFile13, rename as rename9, writeFile as writeFile10 } from "fs/promises";
|
|
16694
17005
|
import { join as join47 } from "path";
|
|
16695
17006
|
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
|
|
17007
|
+
const current = await readFile13(path, "utf8");
|
|
16697
17008
|
if (current === source)
|
|
16698
17009
|
return false;
|
|
16699
17010
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
@@ -16711,7 +17022,7 @@ var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2
|
|
|
16711
17022
|
return writeChangedFile2(path, source);
|
|
16712
17023
|
}, optionalSource = async (path) => {
|
|
16713
17024
|
try {
|
|
16714
|
-
return await
|
|
17025
|
+
return await readFile13(path, "utf8");
|
|
16715
17026
|
} catch (error) {
|
|
16716
17027
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
16717
17028
|
return null;
|
|
@@ -16819,7 +17130,7 @@ ${entries}
|
|
|
16819
17130
|
if (requirements.iosPrivacyAccessedApis.length === 0)
|
|
16820
17131
|
return false;
|
|
16821
17132
|
const projectPath = join47(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
16822
|
-
const project = await
|
|
17133
|
+
const project = await readFile13(projectPath, "utf8");
|
|
16823
17134
|
return writeChangedFile2(projectPath, addIosPrivacyProjectReference(project));
|
|
16824
17135
|
}, addIosPrivacyProjectReference = (source) => {
|
|
16825
17136
|
const fileMatch = source.match(/([A-F0-9]{24}) \/\* PrivacyInfo\.xcprivacy \*\/ = \{isa = PBXFileReference;/u);
|
|
@@ -16870,7 +17181,7 @@ ${next.slice(index)}`;
|
|
|
16870
17181
|
return next;
|
|
16871
17182
|
}, configureIos2 = async (config, plan) => {
|
|
16872
17183
|
const path = join47(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
16873
|
-
const source = await
|
|
17184
|
+
const source = await readFile13(path, "utf8");
|
|
16874
17185
|
const requirements = absoluteDeviceNativeRequirements(plan);
|
|
16875
17186
|
const existingSystemBars = source.match(/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<(true|false)\s*\/>/u);
|
|
16876
17187
|
const ownedStart = source.indexOf(START_MARKER2);
|
|
@@ -16960,7 +17271,7 @@ ${content}
|
|
|
16960
17271
|
return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
|
|
16961
17272
|
}, configureAndroid2 = async (config, plan) => {
|
|
16962
17273
|
const path = join47(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
16963
|
-
const source = await
|
|
17274
|
+
const source = await readFile13(path, "utf8");
|
|
16964
17275
|
const permissions = absoluteDeviceNativeRequirements(plan).androidPermissions;
|
|
16965
17276
|
const content = permissions.map((permission) => ` <uses-permission android:name="${escapeXml2(permission)}" />`).join(`
|
|
16966
17277
|
`);
|
|
@@ -17019,10 +17330,10 @@ var init_nativeDeviceCapabilities = __esm(() => {
|
|
|
17019
17330
|
});
|
|
17020
17331
|
|
|
17021
17332
|
// src/mobile/nativeBackgroundSync.ts
|
|
17022
|
-
import { readFile as
|
|
17333
|
+
import { readFile as readFile14, rename as rename10, writeFile as writeFile11 } from "fs/promises";
|
|
17023
17334
|
import { join as join48 } from "path";
|
|
17024
17335
|
var writeChanged = async (path, source) => {
|
|
17025
|
-
const current = await
|
|
17336
|
+
const current = await readFile14(path, "utf8");
|
|
17026
17337
|
if (current === source)
|
|
17027
17338
|
return false;
|
|
17028
17339
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
@@ -17104,10 +17415,10 @@ ${makeRegion(values)} </array>
|
|
|
17104
17415
|
return { changed: false };
|
|
17105
17416
|
const identifier = `${config.appId}.absolutejs.background-sync`;
|
|
17106
17417
|
const infoPath = join48(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
17107
|
-
const info2 = await
|
|
17418
|
+
const info2 = await readFile14(infoPath, "utf8");
|
|
17108
17419
|
const nextInfo = ensurePlistArrayValues(ensurePlistArrayValues(info2, "BGTaskSchedulerPermittedIdentifiers", [identifier], "background-sync-identifiers"), "UIBackgroundModes", ["fetch", "processing"], "background-sync-modes");
|
|
17109
17420
|
const delegatePath = join48(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
17110
|
-
let delegate = await
|
|
17421
|
+
let delegate = await readFile14(delegatePath, "utf8");
|
|
17111
17422
|
if (!delegate.includes("import AbsoluteSyncCapacitor")) {
|
|
17112
17423
|
const importIndex = delegate.lastIndexOf("import Capacitor");
|
|
17113
17424
|
if (importIndex < 0)
|
|
@@ -17141,7 +17452,7 @@ var init_nativeBackgroundSync = __esm(() => {
|
|
|
17141
17452
|
import {
|
|
17142
17453
|
access as access7,
|
|
17143
17454
|
mkdir as mkdir10,
|
|
17144
|
-
readFile as
|
|
17455
|
+
readFile as readFile15,
|
|
17145
17456
|
rename as rename11,
|
|
17146
17457
|
rm as rm7,
|
|
17147
17458
|
writeFile as writeFile12
|
|
@@ -17199,7 +17510,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
|
|
|
17199
17510
|
}, writeAtomic = async (path, source) => {
|
|
17200
17511
|
let current;
|
|
17201
17512
|
try {
|
|
17202
|
-
current = await
|
|
17513
|
+
current = await readFile15(path, "utf8");
|
|
17203
17514
|
} catch (error) {
|
|
17204
17515
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
17205
17516
|
throw error;
|
|
@@ -17222,7 +17533,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
|
|
|
17222
17533
|
const path = resolve37(root, OWNERSHIP_FILE);
|
|
17223
17534
|
let ownership;
|
|
17224
17535
|
try {
|
|
17225
|
-
ownership = JSON.parse(await
|
|
17536
|
+
ownership = JSON.parse(await readFile15(path, "utf8"));
|
|
17226
17537
|
} catch {
|
|
17227
17538
|
throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
|
|
17228
17539
|
}
|
|
@@ -17735,7 +18046,7 @@ var DEFAULT_ROUTE_TIMEOUT_MS = 30000, DEFAULT_HMR_TIMEOUT_MS = 30000, routeExpre
|
|
|
17735
18046
|
};
|
|
17736
18047
|
|
|
17737
18048
|
// src/mobile/releaseDoctor.ts
|
|
17738
|
-
import { access as access8, readFile as
|
|
18049
|
+
import { access as access8, readFile as readFile16, readdir as readdir4 } from "fs/promises";
|
|
17739
18050
|
import { dirname as dirname29, extname as extname8, join as join49, relative as relative24 } from "path";
|
|
17740
18051
|
var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
17741
18052
|
try {
|
|
@@ -17749,7 +18060,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17749
18060
|
return findHmrAsset(path);
|
|
17750
18061
|
if (!isFile2 || !RELEASE_ASSET_EXTENSIONS.has(extname8(path)))
|
|
17751
18062
|
return;
|
|
17752
|
-
const source = await
|
|
18063
|
+
const source = await readFile16(path, "utf8");
|
|
17753
18064
|
return HMR_ASSET_PATTERN.test(source) ? path : undefined;
|
|
17754
18065
|
}, findHmrAsset = async (root) => {
|
|
17755
18066
|
if (!await pathExists5(root))
|
|
@@ -17788,18 +18099,18 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17788
18099
|
if (!await pathExists5(nativeConfigPath)) {
|
|
17789
18100
|
return fail5("android.capacitor-config", "The generated Android Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync android` before release validation.");
|
|
17790
18101
|
}
|
|
17791
|
-
const unsafe = isUnsafeCapacitorConfig(await
|
|
18102
|
+
const unsafe = isUnsafeCapacitorConfig(await readFile16(nativeConfigPath, "utf8"));
|
|
17792
18103
|
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
18104
|
}, manifestReleaseCheck = async (manifestPath) => {
|
|
17794
18105
|
if (!await pathExists5(manifestPath)) {
|
|
17795
18106
|
return fail5("android.cleartext", "The Android manifest is missing.", manifestPath, "Run `absolute mobile sync android` before release validation.");
|
|
17796
18107
|
}
|
|
17797
|
-
const source = await
|
|
18108
|
+
const source = await readFile16(manifestPath, "utf8");
|
|
17798
18109
|
const cleartext = /android:usesCleartextTraffic=["']true["']/u.test(source);
|
|
17799
18110
|
const networkConfigName = source.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
|
|
17800
18111
|
const networkConfigPath = networkConfigName ? join49(dirname29(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
|
|
17801
18112
|
const developmentTrustReference = /android:networkSecurityConfig=["']@xml\/absolutejs_dev_network_security["']/u.test(source);
|
|
17802
|
-
const developmentTrustContents = networkConfigPath ? await
|
|
18113
|
+
const developmentTrustContents = networkConfigPath ? await readFile16(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
|
|
17803
18114
|
const developmentTrust = developmentTrustReference || developmentTrustContents;
|
|
17804
18115
|
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
18116
|
}, hmrAssetsReleaseCheck = async (publicRoot) => {
|
|
@@ -17831,7 +18142,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17831
18142
|
if (!config.platforms.includes("android") || permissions.length === 0)
|
|
17832
18143
|
return;
|
|
17833
18144
|
const path = join49(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
17834
|
-
const source = await
|
|
18145
|
+
const source = await readFile16(path, "utf8");
|
|
17835
18146
|
const missing = permissions.filter((permission) => !source.includes(`android:name="${permission}"`) && !source.includes(`android:name='${permission}'`));
|
|
17836
18147
|
if (missing.length === 0)
|
|
17837
18148
|
return;
|
|
@@ -17840,7 +18151,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17840
18151
|
if (!config.platforms.includes("ios") || purposes.length === 0)
|
|
17841
18152
|
return;
|
|
17842
18153
|
const path = join49(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
17843
|
-
const source = await
|
|
18154
|
+
const source = await readFile16(path, "utf8");
|
|
17844
18155
|
const missing = purposes.filter((purpose) => !source.includes(`<key>${IOS_USAGE_KEYS[purpose]}</key>`));
|
|
17845
18156
|
if (missing.length === 0)
|
|
17846
18157
|
return;
|
|
@@ -17893,7 +18204,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17893
18204
|
}
|
|
17894
18205
|
if (!await pathExists5(nativeConfigPath)) {
|
|
17895
18206
|
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
|
|
18207
|
+
} else if (isUnsafeCapacitorConfig(await readFile16(nativeConfigPath, "utf8"))) {
|
|
17897
18208
|
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
18209
|
} else {
|
|
17899
18210
|
checks.push(pass("ios.capacitor-config", "iOS Capacitor config contains no development transport overrides.", nativeConfigPath));
|
|
@@ -17901,7 +18212,7 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
17901
18212
|
if (!await pathExists5(infoPath)) {
|
|
17902
18213
|
checks.push(fail5("ios.transport-security", "The iOS Info.plist is missing.", infoPath, "Run `absolute mobile sync ios` before release validation."));
|
|
17903
18214
|
} else {
|
|
17904
|
-
const info2 = await
|
|
18215
|
+
const info2 = await readFile16(infoPath, "utf8");
|
|
17905
18216
|
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
18217
|
}
|
|
17907
18218
|
const hmrAsset = await findHmrAsset(publicRoot);
|
|
@@ -17954,7 +18265,7 @@ import {
|
|
|
17954
18265
|
copyFile as copyFile5,
|
|
17955
18266
|
mkdir as mkdir12,
|
|
17956
18267
|
mkdtemp as mkdtemp5,
|
|
17957
|
-
readFile as
|
|
18268
|
+
readFile as readFile17,
|
|
17958
18269
|
rename as rename12,
|
|
17959
18270
|
rm as rm8,
|
|
17960
18271
|
stat as stat2,
|
|
@@ -18010,7 +18321,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
|
|
|
18010
18321
|
artifactPath
|
|
18011
18322
|
]);
|
|
18012
18323
|
return result.exitCode === 0 && /jar verified/iu.test(result.stdout);
|
|
18013
|
-
}, sha256File2 = async (path) => createHash12("sha256").update(await
|
|
18324
|
+
}, sha256File2 = async (path) => createHash12("sha256").update(await readFile17(path)).digest("hex"), safeOutputDirectory2 = (projectRoot, requested) => {
|
|
18014
18325
|
const root = resolve39(projectRoot);
|
|
18015
18326
|
const output = resolve39(root, requested ?? ".absolutejs/mobile/releases/android");
|
|
18016
18327
|
const projectRelative = relative25(root, output);
|
|
@@ -18023,7 +18334,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
|
|
|
18023
18334
|
const artifactName = "app-release.aab";
|
|
18024
18335
|
const destination = join50(releaseRoot, artifactName);
|
|
18025
18336
|
if (await pathExists6(releaseRoot)) {
|
|
18026
|
-
const existing = requireManifestIdentity(JSON.parse(await
|
|
18337
|
+
const existing = requireManifestIdentity(JSON.parse(await readFile17(join50(releaseRoot, "release.json"), "utf8")), metadata);
|
|
18027
18338
|
const [installedBytes, installedSha256] = await Promise.all([
|
|
18028
18339
|
stat2(destination).then(({ size }) => size),
|
|
18029
18340
|
sha256File2(destination)
|
|
@@ -18070,7 +18381,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
|
|
|
18070
18381
|
const host2 = options.host ?? detectAbsoluteMobileHost();
|
|
18071
18382
|
const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2);
|
|
18072
18383
|
const nativeDirectory = join50(options.config.nativeProjectDirectory, "android");
|
|
18073
|
-
const manifest = requireManifest2(JSON.parse(await
|
|
18384
|
+
const manifest = requireManifest2(JSON.parse(await readFile17(join50(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
18074
18385
|
if (manifest.appId !== options.config.appId) {
|
|
18075
18386
|
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
18076
18387
|
}
|
|
@@ -18133,8 +18444,46 @@ var init_androidRelease = __esm(() => {
|
|
|
18133
18444
|
init_androidEmulatorController();
|
|
18134
18445
|
});
|
|
18135
18446
|
|
|
18447
|
+
// src/mobile/iosDeviceAcceptance.ts
|
|
18448
|
+
var absoluteIosDeviceAcceptanceCommands = (options) => {
|
|
18449
|
+
const xcrun = options.xcrun ?? "/usr/bin/xcrun";
|
|
18450
|
+
const prefix = [xcrun, "devicectl", "device"];
|
|
18451
|
+
return {
|
|
18452
|
+
apps: [...prefix, "info", "apps", "--device", options.device],
|
|
18453
|
+
details: [...prefix, "info", "details", "--device", options.device],
|
|
18454
|
+
launch: [
|
|
18455
|
+
...prefix,
|
|
18456
|
+
"process",
|
|
18457
|
+
"launch",
|
|
18458
|
+
"--terminate-existing",
|
|
18459
|
+
"--device",
|
|
18460
|
+
options.device,
|
|
18461
|
+
options.appId
|
|
18462
|
+
]
|
|
18463
|
+
};
|
|
18464
|
+
}, requireSuccess3 = (result, message) => {
|
|
18465
|
+
if (result.exitCode !== 0)
|
|
18466
|
+
throw new Error(message);
|
|
18467
|
+
return result;
|
|
18468
|
+
}, testAbsoluteIosPhysicalDevice = async (options) => {
|
|
18469
|
+
const commands = absoluteIosDeviceAcceptanceCommands(options);
|
|
18470
|
+
requireSuccess3(await options.capture(commands.details), "The selected physical iOS device is unavailable. Pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode.");
|
|
18471
|
+
const apps = requireSuccess3(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
|
|
18472
|
+
if (!apps.stdout.includes(options.appId))
|
|
18473
|
+
throw new Error("The AbsoluteJS app is not installed on the selected physical iOS device. Start bun dev with the same --ios-device value first.");
|
|
18474
|
+
const now = options.now ?? performance.now.bind(performance);
|
|
18475
|
+
const startedAt = now();
|
|
18476
|
+
requireSuccess3(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
|
|
18477
|
+
await options.waitForHmr();
|
|
18478
|
+
return {
|
|
18479
|
+
hmrConnected: true,
|
|
18480
|
+
installed: true,
|
|
18481
|
+
relaunchMs: Math.round(now() - startedAt)
|
|
18482
|
+
};
|
|
18483
|
+
};
|
|
18484
|
+
|
|
18136
18485
|
// src/mobile/iosConformance.ts
|
|
18137
|
-
import { readFile as
|
|
18486
|
+
import { readFile as readFile18, stat as stat3 } from "fs/promises";
|
|
18138
18487
|
var HMR_LINE, parseAbsoluteIosHmrLog = (line) => {
|
|
18139
18488
|
const match = HMR_LINE.exec(line);
|
|
18140
18489
|
if (!match)
|
|
@@ -18169,7 +18518,7 @@ var HMR_LINE, parseAbsoluteIosHmrLog = (line) => {
|
|
|
18169
18518
|
if (Date.now() > deadline)
|
|
18170
18519
|
throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
|
|
18171
18520
|
options.signal?.throwIfAborted();
|
|
18172
|
-
const contents = await
|
|
18521
|
+
const contents = await readFile18(options.logPath).catch(() => Buffer.alloc(0));
|
|
18173
18522
|
if (contents.byteLength < offset) {
|
|
18174
18523
|
offset = 0;
|
|
18175
18524
|
buffered = "";
|
|
@@ -18193,7 +18542,7 @@ var init_iosConformance = __esm(() => {
|
|
|
18193
18542
|
});
|
|
18194
18543
|
|
|
18195
18544
|
// src/mobile/nativeTestReport.ts
|
|
18196
|
-
import { mkdir as mkdir13, readFile as
|
|
18545
|
+
import { mkdir as mkdir13, readFile as readFile19, writeFile as writeFile15 } from "fs/promises";
|
|
18197
18546
|
import { join as join51 } from "path";
|
|
18198
18547
|
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
18548
|
`, "<br>"), createAbsoluteNativeAutomatedChecks = (run) => {
|
|
@@ -18203,6 +18552,15 @@ var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sa
|
|
|
18203
18552
|
if (run.hmr)
|
|
18204
18553
|
hmrResult = run.hmr.outcome === "failed" ? "FAIL" : "PASS";
|
|
18205
18554
|
const routeDetails = run.routes?.length ? ` Routes: ${run.routes.join(", ")}.` : "";
|
|
18555
|
+
let artifactDetails = "No target screenshot was captured.";
|
|
18556
|
+
let artifactResult = "FAIL";
|
|
18557
|
+
if (run.screenshot) {
|
|
18558
|
+
artifactDetails = "A target screenshot was captured. Visually review it before sharing this directory.";
|
|
18559
|
+
artifactResult = "PASS";
|
|
18560
|
+
} else if (run.targetKind === "device") {
|
|
18561
|
+
artifactDetails = "Physical-device screen capture was intentionally skipped; use Xcode Device Hub for manual visual evidence.";
|
|
18562
|
+
artifactResult = "SKIPPED";
|
|
18563
|
+
}
|
|
18206
18564
|
const checks = [
|
|
18207
18565
|
{
|
|
18208
18566
|
details: `Captured host, toolchain, Bun, and AbsoluteJS metadata for ${target}.`,
|
|
@@ -18221,10 +18579,10 @@ var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sa
|
|
|
18221
18579
|
result: hmrResult
|
|
18222
18580
|
},
|
|
18223
18581
|
{
|
|
18224
|
-
details:
|
|
18582
|
+
details: artifactDetails,
|
|
18225
18583
|
...evidence ? { evidence } : {},
|
|
18226
18584
|
id: "AUTO-ARTIFACT-01",
|
|
18227
|
-
result:
|
|
18585
|
+
result: artifactResult
|
|
18228
18586
|
}
|
|
18229
18587
|
];
|
|
18230
18588
|
if (run.upgrade) {
|
|
@@ -18257,22 +18615,37 @@ var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sa
|
|
|
18257
18615
|
result: Object.values(syncMigration.state).every(Boolean) ? "PASS" : "FAIL"
|
|
18258
18616
|
});
|
|
18259
18617
|
}
|
|
18618
|
+
if (run.deviceAcceptance) {
|
|
18619
|
+
checks.push({
|
|
18620
|
+
details: `The installed physical-device app relaunched and reconnected to native HMR in ${run.deviceAcceptance.relaunchMs}ms.`,
|
|
18621
|
+
id: "AUTO-DEVICE-01",
|
|
18622
|
+
result: "PASS"
|
|
18623
|
+
}, {
|
|
18624
|
+
details: run.deviceAcceptance.https ? "The physical app reached the HTTPS development server and established native HMR; this proves the active app session accepted the development trust path." : "Physical-device acceptance did not use HTTPS.",
|
|
18625
|
+
id: "AUTO-DEVICE-HTTPS-01",
|
|
18626
|
+
result: run.deviceAcceptance.https ? "PASS" : "FAIL"
|
|
18627
|
+
}, {
|
|
18628
|
+
details: run.deviceAcceptance.remote ? "The lifecycle commands ran on the paired Remote Mac and HMR returned through the active relay." : "The acceptance run used the local Mac.",
|
|
18629
|
+
id: "AUTO-DEVICE-REMOTE-01",
|
|
18630
|
+
result: run.deviceAcceptance.remote ? "PASS" : "SKIPPED"
|
|
18631
|
+
});
|
|
18632
|
+
}
|
|
18260
18633
|
return checks;
|
|
18261
18634
|
}, createAbsoluteNativeTestReport = (options) => ({
|
|
18262
18635
|
automatedChecks: options.automatedChecks ?? createAbsoluteNativeAutomatedChecks(options.run),
|
|
18263
18636
|
generatedAt: options.generatedAt ?? new Date().toISOString(),
|
|
18264
|
-
manualChecks: options.manualChecks.map(([id, details]) =>
|
|
18637
|
+
manualChecks: options.manualChecks.map(([id, details]) => options.manualCheckResults?.[id] ?? {
|
|
18265
18638
|
details,
|
|
18266
18639
|
id,
|
|
18267
18640
|
result: "NOT_RUN"
|
|
18268
|
-
})
|
|
18641
|
+
}),
|
|
18269
18642
|
metadata: options.metadata,
|
|
18270
18643
|
overallResult: options.run.status === "fail" ? "FAIL" : "INCOMPLETE",
|
|
18271
18644
|
platform: options.run.platform,
|
|
18272
18645
|
reportVersion: 1,
|
|
18273
18646
|
run: options.run
|
|
18274
18647
|
}), readPackageVersionForNativeReport = async (packageJsonPath) => {
|
|
18275
|
-
const manifest = JSON.parse(await
|
|
18648
|
+
const manifest = JSON.parse(await readFile19(packageJsonPath, "utf8"));
|
|
18276
18649
|
if (typeof manifest !== "object" || manifest === null)
|
|
18277
18650
|
return "unknown";
|
|
18278
18651
|
const version2 = Reflect.get(manifest, "version");
|
|
@@ -18327,10 +18700,18 @@ var init_nativeTestReport = __esm(() => {
|
|
|
18327
18700
|
|
|
18328
18701
|
// src/mobile/iosTestReport.ts
|
|
18329
18702
|
var MANUAL_CHECKS, readPackageVersionForIosReport, sanitizeIosReportText, writeAbsoluteIosPartnerReport, createAbsoluteIosPartnerReport = (options) => {
|
|
18330
|
-
const {
|
|
18703
|
+
const { targetId, targetKind, ...run } = options.run;
|
|
18704
|
+
const physicalResults = targetKind === "device" && run.deviceAcceptance ? {
|
|
18705
|
+
"DEVICEDEV-01": {
|
|
18706
|
+
details: "AbsoluteJS selected a physical target from the running development session without using Simulator.",
|
|
18707
|
+
id: "DEVICEDEV-01",
|
|
18708
|
+
result: "PASS"
|
|
18709
|
+
}
|
|
18710
|
+
} : undefined;
|
|
18331
18711
|
return createAbsoluteNativeTestReport({
|
|
18332
18712
|
...options.generatedAt ? { generatedAt: options.generatedAt } : {},
|
|
18333
18713
|
manualChecks: MANUAL_CHECKS,
|
|
18714
|
+
...physicalResults ? { manualCheckResults: physicalResults } : {},
|
|
18334
18715
|
metadata: {
|
|
18335
18716
|
absolutejsVersion: options.absolutejsVersion,
|
|
18336
18717
|
bunVersion: options.bunVersion,
|
|
@@ -18341,8 +18722,8 @@ var MANUAL_CHECKS, readPackageVersionForIosReport, sanitizeIosReportText, writeA
|
|
|
18341
18722
|
run: {
|
|
18342
18723
|
...run,
|
|
18343
18724
|
platform: "ios",
|
|
18344
|
-
targetId
|
|
18345
|
-
targetKind
|
|
18725
|
+
targetId,
|
|
18726
|
+
targetKind
|
|
18346
18727
|
}
|
|
18347
18728
|
});
|
|
18348
18729
|
};
|
|
@@ -18359,6 +18740,10 @@ var init_iosTestReport = __esm(() => {
|
|
|
18359
18740
|
["SETUP-05", "Confirm generated iOS project signing and Xcode warnings."],
|
|
18360
18741
|
["DEV-01", "Record cold and warm bun dev startup timings."],
|
|
18361
18742
|
["DEV-02", "Complete route traversal, HMR, relaunch, and recovery checks."],
|
|
18743
|
+
...Array.from({ length: 10 }, (_, index) => [
|
|
18744
|
+
`DEVICEDEV-${String(index + 1).padStart(2, "0")}`,
|
|
18745
|
+
`Complete physical-device development runbook check DEVICEDEV-${String(index + 1).padStart(2, "0")}.`
|
|
18746
|
+
]),
|
|
18362
18747
|
["CAP-01", "Complete automatic device-capability provisioning checks."],
|
|
18363
18748
|
...Array.from({ length: 8 }, (_, index) => [
|
|
18364
18749
|
`SYSUI-${String(index + 1).padStart(2, "0")}`,
|
|
@@ -18613,11 +18998,11 @@ var exports_mobile = {};
|
|
|
18613
18998
|
__export(exports_mobile, {
|
|
18614
18999
|
runMobile: () => runMobile
|
|
18615
19000
|
});
|
|
18616
|
-
import { access as access11, mkdir as mkdir14, readFile as
|
|
19001
|
+
import { access as access11, mkdir as mkdir14, readFile as readFile20, writeFile as writeFile16 } from "fs/promises";
|
|
18617
19002
|
import { join as join52, resolve as resolve41 } from "path";
|
|
18618
19003
|
import { createInterface } from "readline/promises";
|
|
18619
19004
|
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
|
|
19005
|
+
const manifest = JSON.parse(await readFile20(join52(projectRoot, "package.json"), "utf8"));
|
|
18621
19006
|
if (!isRecord15(manifest))
|
|
18622
19007
|
throw new TypeError("Application package.json must contain an object.");
|
|
18623
19008
|
const names = new Set;
|
|
@@ -18630,7 +19015,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
|
|
|
18630
19015
|
return names;
|
|
18631
19016
|
}, resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
18632
19017
|
try {
|
|
18633
|
-
const manifest = JSON.parse(await
|
|
19018
|
+
const manifest = JSON.parse(await readFile20(join52(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
18634
19019
|
return isRecord15(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
18635
19020
|
} catch {
|
|
18636
19021
|
return;
|
|
@@ -19536,7 +19921,7 @@ Emulator setup verification:`);
|
|
|
19536
19921
|
if (!instance?.logFile)
|
|
19537
19922
|
throw new TypeError("The selected dev server has no session log. Run `bun dev` normally before requesting --wait-for-hmr.");
|
|
19538
19923
|
if (!args.includes("--json"))
|
|
19539
|
-
console.log("iOS
|
|
19924
|
+
console.log("iOS app is ready. Save a source edit now; waiting for a native HMR acknowledgement\u2026");
|
|
19540
19925
|
return waitForAbsoluteIosHmrLog({
|
|
19541
19926
|
logPath: instance.logFile,
|
|
19542
19927
|
timeoutMs
|
|
@@ -19546,7 +19931,7 @@ Emulator setup verification:`);
|
|
|
19546
19931
|
console.log(JSON.stringify(report, null, 2));
|
|
19547
19932
|
return;
|
|
19548
19933
|
}
|
|
19549
|
-
console.log(`\u2713 iOS simulator ${report.
|
|
19934
|
+
console.log(report.target === "device" ? `\u2713 Physical iOS app ${report.appId} relaunched and reconnected to HMR; no device identifier or screenshot was recorded.` : `\u2713 iOS simulator ${report.targetId}: ${report.appId} launched; screenshot ${report.screenshot}.`);
|
|
19550
19935
|
if (!report.hmrApply)
|
|
19551
19936
|
return;
|
|
19552
19937
|
const timing = report.hmrApply.serverMs === undefined ? "" : ` (server ${report.hmrApply.serverMs}ms, client ${report.hmrApply.clientMs}ms)`;
|
|
@@ -19557,6 +19942,41 @@ Emulator setup verification:`);
|
|
|
19557
19942
|
if (!xcrun)
|
|
19558
19943
|
throw new TypeError("iOS simulator tools are unavailable. Run this command on macOS after `absolute mobile doctor ios --fix`.");
|
|
19559
19944
|
return xcrun;
|
|
19945
|
+
}, runningIosDevice = (instance) => {
|
|
19946
|
+
if (!instance)
|
|
19947
|
+
return;
|
|
19948
|
+
const index = instance.command.indexOf("--ios-device");
|
|
19949
|
+
return index === NOT_FOUND3 ? undefined : instance.command[index + 1];
|
|
19950
|
+
}, physicalIosCapture = async (options) => {
|
|
19951
|
+
const remoteName = valueAfter(options.args, "--remote");
|
|
19952
|
+
if (remoteName && options.instance.iosRemoteMac && remoteName !== options.instance.iosRemoteMac)
|
|
19953
|
+
throw new TypeError("--remote must match the Remote Mac used by the running bun dev session.");
|
|
19954
|
+
const selectedRemoteName = remoteName ?? options.instance.iosRemoteMac;
|
|
19955
|
+
const remote = process.platform === "darwin" && !selectedRemoteName ? undefined : await getAbsoluteRemoteMacProfile(selectedRemoteName);
|
|
19956
|
+
if (process.platform !== "darwin" && !remote)
|
|
19957
|
+
throw new TypeError("Physical iOS acceptance requires macOS or a paired Remote Mac.");
|
|
19958
|
+
if (remote) {
|
|
19959
|
+
const macos = await captureAbsoluteRemoteMacCommand(remote, [
|
|
19960
|
+
"/usr/bin/sw_vers",
|
|
19961
|
+
"-productVersion"
|
|
19962
|
+
]);
|
|
19963
|
+
if (macos.exitCode !== 0)
|
|
19964
|
+
throw new Error("Remote Mac version inspection failed.");
|
|
19965
|
+
return {
|
|
19966
|
+
macosVersion: macos.stdout.trim(),
|
|
19967
|
+
remote: true,
|
|
19968
|
+
xcodeVersion: remote.xcodeVersion,
|
|
19969
|
+
xcrun: "/usr/bin/xcrun",
|
|
19970
|
+
capture: (command) => captureAbsoluteRemoteMacCommand(remote, command)
|
|
19971
|
+
};
|
|
19972
|
+
}
|
|
19973
|
+
return {
|
|
19974
|
+
macosVersion: requireCapturedCommand(["/usr/bin/sw_vers", "-productVersion"], "macOS version inspection").stdout.trim(),
|
|
19975
|
+
remote: false,
|
|
19976
|
+
xcodeVersion: requireCapturedCommand(["/usr/bin/xcodebuild", "-version"], "Xcode version inspection").stdout.trim(),
|
|
19977
|
+
xcrun: "/usr/bin/xcrun",
|
|
19978
|
+
capture: async (command) => captureCommand4(command)
|
|
19979
|
+
};
|
|
19560
19980
|
}, selectIosSimulator = (xcrun, explicitUdid) => {
|
|
19561
19981
|
const result = captureCommand4([
|
|
19562
19982
|
xcrun,
|
|
@@ -19662,12 +20082,124 @@ Emulator setup verification:`);
|
|
|
19662
20082
|
const reportRoot = nativeReportRoot(options.args, options.projectRoot, "ios");
|
|
19663
20083
|
if (!reportRoot)
|
|
19664
20084
|
return;
|
|
19665
|
-
const metadata = await iosReportMetadata(options.xcrun);
|
|
20085
|
+
const metadata = options.metadata ?? (options.xcrun ? await iosReportMetadata(options.xcrun) : undefined);
|
|
20086
|
+
if (!metadata)
|
|
20087
|
+
throw new Error("iOS report metadata could not be inspected.");
|
|
19666
20088
|
const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({ ...metadata, run: options.run }));
|
|
19667
20089
|
const print = options.args.includes("--json") ? console.error : console.log;
|
|
19668
20090
|
print(`iOS partner report: ${paths.markdownPath}`);
|
|
19669
20091
|
print(`Return this report directory: ${paths.directory}`);
|
|
19670
20092
|
return paths;
|
|
20093
|
+
}, testPhysicalIos = async (options) => {
|
|
20094
|
+
const {
|
|
20095
|
+
args,
|
|
20096
|
+
device,
|
|
20097
|
+
https,
|
|
20098
|
+
instance,
|
|
20099
|
+
mobile,
|
|
20100
|
+
port,
|
|
20101
|
+
projectRoot,
|
|
20102
|
+
timeoutMs
|
|
20103
|
+
} = options;
|
|
20104
|
+
const transport = await physicalIosCapture({ args, instance });
|
|
20105
|
+
const reportRoot = nativeReportRoot(args, projectRoot, "ios");
|
|
20106
|
+
const startedAt = performance.now();
|
|
20107
|
+
try {
|
|
20108
|
+
const acceptance = await testAbsoluteIosPhysicalDevice({
|
|
20109
|
+
appId: mobile.appId,
|
|
20110
|
+
capture: transport.capture,
|
|
20111
|
+
device,
|
|
20112
|
+
xcrun: transport.xcrun,
|
|
20113
|
+
waitForHmr: () => waitForIosHmrClient({ https, port, timeoutMs })
|
|
20114
|
+
});
|
|
20115
|
+
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
20116
|
+
const report = {
|
|
20117
|
+
appId: mobile.appId,
|
|
20118
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
20119
|
+
...hmrApply ? { hmrApply } : {},
|
|
20120
|
+
hmrConnected: true,
|
|
20121
|
+
platform: "ios",
|
|
20122
|
+
port,
|
|
20123
|
+
provider: "capacitor",
|
|
20124
|
+
status: "pass",
|
|
20125
|
+
target: "device",
|
|
20126
|
+
targetId: "physical-device"
|
|
20127
|
+
};
|
|
20128
|
+
sendTelemetryEvent("mobile:ios-device-conformance", {
|
|
20129
|
+
durationMs: report.durationMs,
|
|
20130
|
+
platform: report.platform,
|
|
20131
|
+
provider: report.provider,
|
|
20132
|
+
remote: transport.remote,
|
|
20133
|
+
success: true,
|
|
20134
|
+
waitedForHmr: args.includes("--wait-for-hmr")
|
|
20135
|
+
});
|
|
20136
|
+
printIosTestReport(report, args.includes("--json"));
|
|
20137
|
+
await writeRequestedIosReport({
|
|
20138
|
+
args,
|
|
20139
|
+
metadata: {
|
|
20140
|
+
absolutejsVersion: await absolutejsVersionForReport(),
|
|
20141
|
+
bunVersion: Bun.version,
|
|
20142
|
+
macosVersion: transport.macosVersion,
|
|
20143
|
+
xcodeVersion: transport.xcodeVersion
|
|
20144
|
+
},
|
|
20145
|
+
projectRoot,
|
|
20146
|
+
run: {
|
|
20147
|
+
appId: report.appId,
|
|
20148
|
+
deviceAcceptance: {
|
|
20149
|
+
https: true,
|
|
20150
|
+
relaunchMs: acceptance.relaunchMs,
|
|
20151
|
+
remote: transport.remote
|
|
20152
|
+
},
|
|
20153
|
+
durationMs: report.durationMs,
|
|
20154
|
+
...hmrApply ? {
|
|
20155
|
+
hmr: {
|
|
20156
|
+
durationMs: hmrApply.duration,
|
|
20157
|
+
outcome: hmrApply.outcome,
|
|
20158
|
+
...hmrApply.clientMs === undefined ? {} : { clientMs: hmrApply.clientMs },
|
|
20159
|
+
...hmrApply.serverMs === undefined ? {} : { serverMs: hmrApply.serverMs }
|
|
20160
|
+
}
|
|
20161
|
+
} : {},
|
|
20162
|
+
hmrConnected: true,
|
|
20163
|
+
port,
|
|
20164
|
+
status: "pass",
|
|
20165
|
+
targetId: "physical-device",
|
|
20166
|
+
targetKind: "device"
|
|
20167
|
+
}
|
|
20168
|
+
});
|
|
20169
|
+
return report;
|
|
20170
|
+
} catch (error) {
|
|
20171
|
+
const durationMs = Math.round(performance.now() - startedAt);
|
|
20172
|
+
sendTelemetryEvent("mobile:ios-device-conformance", {
|
|
20173
|
+
durationMs,
|
|
20174
|
+
platform: "ios",
|
|
20175
|
+
provider: "capacitor",
|
|
20176
|
+
remote: transport.remote,
|
|
20177
|
+
success: false,
|
|
20178
|
+
waitedForHmr: args.includes("--wait-for-hmr")
|
|
20179
|
+
});
|
|
20180
|
+
if (reportRoot)
|
|
20181
|
+
await writeRequestedIosReport({
|
|
20182
|
+
args,
|
|
20183
|
+
metadata: {
|
|
20184
|
+
absolutejsVersion: await absolutejsVersionForReport(),
|
|
20185
|
+
bunVersion: Bun.version,
|
|
20186
|
+
macosVersion: transport.macosVersion,
|
|
20187
|
+
xcodeVersion: transport.xcodeVersion
|
|
20188
|
+
},
|
|
20189
|
+
projectRoot,
|
|
20190
|
+
run: {
|
|
20191
|
+
appId: mobile.appId,
|
|
20192
|
+
durationMs,
|
|
20193
|
+
error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
|
|
20194
|
+
hmrConnected: false,
|
|
20195
|
+
port,
|
|
20196
|
+
status: "fail",
|
|
20197
|
+
targetId: "physical-device",
|
|
20198
|
+
targetKind: "device"
|
|
20199
|
+
}
|
|
20200
|
+
});
|
|
20201
|
+
throw error;
|
|
20202
|
+
}
|
|
19671
20203
|
}, testIos = async (args) => {
|
|
19672
20204
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
19673
20205
|
const { https, instance, port } = requireIosTestContext(args, projectRoot);
|
|
@@ -19676,6 +20208,35 @@ Emulator setup verification:`);
|
|
|
19676
20208
|
if (args.includes("--route"))
|
|
19677
20209
|
throw new TypeError("iOS simulator route selection is not exposed through simctl; configure mobile.entry for the native route matrix.");
|
|
19678
20210
|
const timeoutMs = androidTestTimeout(args);
|
|
20211
|
+
const requestedDevice = valueAfter(args, "--device");
|
|
20212
|
+
if (args.includes("--device") && !requestedDevice)
|
|
20213
|
+
throw new TypeError("mobile test ios --device requires a device identifier or name.");
|
|
20214
|
+
if (requestedDevice && (valueAfter(args, "--udid") || valueAfter(args, "--serial")))
|
|
20215
|
+
throw new TypeError("mobile test ios --device cannot be combined with a simulator selector.");
|
|
20216
|
+
if (!requestedDevice && args.includes("--remote"))
|
|
20217
|
+
throw new TypeError("mobile test ios --remote is available only with --device.");
|
|
20218
|
+
if (requestedDevice) {
|
|
20219
|
+
const device = normalizeAbsoluteIosDeviceIdentifier(requestedDevice);
|
|
20220
|
+
const activeDevice = runningIosDevice(instance);
|
|
20221
|
+
if (!activeDevice)
|
|
20222
|
+
throw new TypeError("The selected dev server is not running a physical iOS session. Start bun dev with --ios-device first.");
|
|
20223
|
+
if (activeDevice !== device)
|
|
20224
|
+
throw new TypeError("--device must match the --ios-device value used by the running bun dev session.");
|
|
20225
|
+
if (!https)
|
|
20226
|
+
throw new TypeError("Physical iOS acceptance requires dev.https: true so the report can prove the native trust path.");
|
|
20227
|
+
if (!instance)
|
|
20228
|
+
throw new TypeError("Physical iOS acceptance requires a registered bun dev session.");
|
|
20229
|
+
return testPhysicalIos({
|
|
20230
|
+
args,
|
|
20231
|
+
device,
|
|
20232
|
+
https,
|
|
20233
|
+
instance,
|
|
20234
|
+
mobile,
|
|
20235
|
+
port,
|
|
20236
|
+
projectRoot,
|
|
20237
|
+
timeoutMs
|
|
20238
|
+
});
|
|
20239
|
+
}
|
|
19679
20240
|
const xcrun = await requireIosXcrun();
|
|
19680
20241
|
const simulator = selectIosSimulator(xcrun, valueAfter(args, "--udid") ?? valueAfter(args, "--serial"));
|
|
19681
20242
|
const reportRoot = nativeReportRoot(args, projectRoot, "ios");
|
|
@@ -19713,7 +20274,8 @@ Emulator setup verification:`);
|
|
|
19713
20274
|
provider: "capacitor",
|
|
19714
20275
|
screenshot,
|
|
19715
20276
|
status: "pass",
|
|
19716
|
-
|
|
20277
|
+
target: "simulator",
|
|
20278
|
+
targetId: simulator.udid
|
|
19717
20279
|
};
|
|
19718
20280
|
sendTelemetryEvent("mobile:ios-conformance", {
|
|
19719
20281
|
durationMs: report.durationMs,
|
|
@@ -19741,7 +20303,8 @@ Emulator setup verification:`);
|
|
|
19741
20303
|
port: report.port,
|
|
19742
20304
|
screenshot: report.screenshot,
|
|
19743
20305
|
status: report.status,
|
|
19744
|
-
|
|
20306
|
+
targetId: report.targetId,
|
|
20307
|
+
targetKind: report.target
|
|
19745
20308
|
},
|
|
19746
20309
|
xcrun
|
|
19747
20310
|
});
|
|
@@ -19774,7 +20337,8 @@ Emulator setup verification:`);
|
|
|
19774
20337
|
port,
|
|
19775
20338
|
...screenshot ? { screenshot } : {},
|
|
19776
20339
|
status: "fail",
|
|
19777
|
-
|
|
20340
|
+
targetId: simulator.udid,
|
|
20341
|
+
targetKind: "simulator"
|
|
19778
20342
|
},
|
|
19779
20343
|
xcrun
|
|
19780
20344
|
});
|
|
@@ -19834,7 +20398,7 @@ Emulator setup verification:`);
|
|
|
19834
20398
|
await publishIos(args.slice(2));
|
|
19835
20399
|
return;
|
|
19836
20400
|
}
|
|
19837
|
-
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [--json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | associations [--outdir dir] [--verify] | doctor [ios|android|release] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--outdir dir] [--web-outdir dir] [--unsigned] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--
|
|
20401
|
+
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [--json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | associations [--outdir dir] [--verify] | doctor [ios|android|release] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--outdir dir] [--web-outdir dir] [--unsigned] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--device identifier [--remote name] | --udid id] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--artifacts dir] [--json]> [--config path]");
|
|
19838
20402
|
};
|
|
19839
20403
|
var init_mobile = __esm(() => {
|
|
19840
20404
|
init_dependencies();
|
|
@@ -19855,6 +20419,7 @@ var init_mobile = __esm(() => {
|
|
|
19855
20419
|
init_androidRelease();
|
|
19856
20420
|
init_iosRelease();
|
|
19857
20421
|
init_iosSimulatorController();
|
|
20422
|
+
init_iosPhysicalDeviceTransport();
|
|
19858
20423
|
init_iosConformance();
|
|
19859
20424
|
init_iosTestReport();
|
|
19860
20425
|
init_androidTestReport();
|
|
@@ -21208,13 +21773,15 @@ var androidToolchainReady = (checks, target = "emulator") => {
|
|
|
21208
21773
|
]);
|
|
21209
21774
|
return checks.every((check) => check.platform !== "android" || target === "device" && deviceOnlyChecks.has(check.id) || check.status !== "fail" && check.status !== "warn");
|
|
21210
21775
|
};
|
|
21211
|
-
var iosToolchainReady = (checks) => checks.every((check) => check.platform !== "ios" || check.status !== "fail" && check.status !== "warn");
|
|
21776
|
+
var iosToolchainReady = (checks, target = "simulator") => checks.every((check) => check.platform !== "ios" || target === "device" && check.id === "ios.runtime" || check.status !== "fail" && check.status !== "warn");
|
|
21212
21777
|
var dev = async (serverEntry, configPath2, options = {}) => {
|
|
21213
21778
|
let httpsEnabled = false;
|
|
21214
21779
|
let devCertificateAuthorityPath = null;
|
|
21215
21780
|
let resolvedDev;
|
|
21216
21781
|
let buildDirectory = resolve8(process.cwd(), "build");
|
|
21217
21782
|
let mobileConfig;
|
|
21783
|
+
let iosPhysicalServerHost;
|
|
21784
|
+
let selectedRemoteMacProfile;
|
|
21218
21785
|
try {
|
|
21219
21786
|
const config = await loadConfig(configPath2);
|
|
21220
21787
|
mobileConfig = config?.mobile;
|
|
@@ -21229,17 +21796,35 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21229
21796
|
resolvedDev = resolveDevConfig(undefined);
|
|
21230
21797
|
httpsEnabled = resolvedDev.https;
|
|
21231
21798
|
}
|
|
21232
|
-
if (options.androidDevice && ["localhost", "127.0.0.1", "::1"].includes(resolvedDev.host)) {
|
|
21799
|
+
if ((options.androidDevice || options.iosDevice && detectAbsoluteMobileHost() === "macos") && ["localhost", "127.0.0.1", "::1"].includes(resolvedDev.host)) {
|
|
21233
21800
|
resolvedDev.host = "0.0.0.0";
|
|
21234
21801
|
}
|
|
21235
|
-
if (
|
|
21236
|
-
|
|
21237
|
-
if (options.androidDevice && !mobileConfig) {
|
|
21238
|
-
throw new TypeError("--android-device requires an absolute.config.ts mobile configuration.");
|
|
21802
|
+
if ((options.androidDevice || options.iosDevice) && !mobileConfig) {
|
|
21803
|
+
throw new TypeError("Physical-device development requires an absolute.config.ts mobile configuration.");
|
|
21239
21804
|
}
|
|
21240
21805
|
if (options.androidDevice && mobileConfig?.platforms && !mobileConfig.platforms.includes("android")) {
|
|
21241
21806
|
throw new TypeError("--android-device requires android in mobile.platforms.");
|
|
21242
21807
|
}
|
|
21808
|
+
if (options.iosDevice && mobileConfig?.platforms && !mobileConfig.platforms.includes("ios")) {
|
|
21809
|
+
throw new TypeError("--ios-device requires ios in mobile.platforms.");
|
|
21810
|
+
}
|
|
21811
|
+
if (options.iosDevice) {
|
|
21812
|
+
if (detectAbsoluteMobileHost() === "macos")
|
|
21813
|
+
iosPhysicalServerHost = mobileReachableHost(resolvedDev.host);
|
|
21814
|
+
else {
|
|
21815
|
+
selectedRemoteMacProfile = await getAbsoluteRemoteMacProfile();
|
|
21816
|
+
if (!selectedRemoteMacProfile)
|
|
21817
|
+
throw new Error("--ios-device requires macOS or a paired Remote Mac.");
|
|
21818
|
+
iosPhysicalServerHost = await inspectAbsoluteRemoteMacLanHost(selectedRemoteMacProfile);
|
|
21819
|
+
}
|
|
21820
|
+
}
|
|
21821
|
+
if (httpsEnabled) {
|
|
21822
|
+
const certificateHosts = [
|
|
21823
|
+
...options.androidDevice ? [mobileReachableHost(resolvedDev.host)] : [],
|
|
21824
|
+
...iosPhysicalServerHost ? [iosPhysicalServerHost] : []
|
|
21825
|
+
];
|
|
21826
|
+
devCertificateAuthorityPath = await setupHttpsCert(certificateHosts.length > 0 ? certificateHosts : [resolvedDev.host]);
|
|
21827
|
+
}
|
|
21243
21828
|
let androidDevProject = null;
|
|
21244
21829
|
let iosDevProject = null;
|
|
21245
21830
|
const mobileInteractive = options.mobile !== false && process.env.ABSOLUTE_NO_MOBILE !== "1" && process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
@@ -21275,9 +21860,9 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21275
21860
|
}
|
|
21276
21861
|
if (normalized.platforms.includes("ios")) {
|
|
21277
21862
|
if (detectAbsoluteMobileHost() !== "macos") {
|
|
21278
|
-
const remote = await getAbsoluteRemoteMacProfile();
|
|
21863
|
+
const remote = selectedRemoteMacProfile ?? await getAbsoluteRemoteMacProfile();
|
|
21279
21864
|
if (!remote) {
|
|
21280
|
-
console.log(cliTag("\x1B[33m", "iOS
|
|
21865
|
+
console.log(cliTag("\x1B[33m", "iOS target skipped. Pair a Mac with `absolute mobile pair mac <name> <user@host>`."));
|
|
21281
21866
|
} else {
|
|
21282
21867
|
const nativeDirectory = join13(normalized.nativeProjectDirectory, "ios");
|
|
21283
21868
|
if (!existsSync5(nativeDirectory)) {
|
|
@@ -21288,12 +21873,13 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21288
21873
|
}
|
|
21289
21874
|
}
|
|
21290
21875
|
} else {
|
|
21291
|
-
|
|
21876
|
+
const iosTarget = options.iosDevice ? "device" : "simulator";
|
|
21877
|
+
let ready = iosToolchainReady(await inspectAbsoluteMobileToolchain(), iosTarget);
|
|
21292
21878
|
if (!ready) {
|
|
21293
|
-
const install = await confirmPrompt("iOS simulation is not configured. Install the missing simulator runtime now?");
|
|
21879
|
+
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
21880
|
if (install) {
|
|
21295
21881
|
await fixAbsoluteMobileEmulatorToolchain("ios");
|
|
21296
|
-
ready = iosToolchainReady(await inspectAbsoluteMobileToolchain());
|
|
21882
|
+
ready = iosToolchainReady(await inspectAbsoluteMobileToolchain(), iosTarget);
|
|
21297
21883
|
} else {
|
|
21298
21884
|
console.log(cliTag("\x1B[33m", "Mobile simulator skipped. Run `absolute mobile doctor ios --fix` when ready."));
|
|
21299
21885
|
}
|
|
@@ -21307,7 +21893,8 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21307
21893
|
if (existsSync5(nativeDirectory) || createNativeProject) {
|
|
21308
21894
|
iosDevProject = await prepareAbsoluteIosDevProject(normalized, {
|
|
21309
21895
|
createNativeProject,
|
|
21310
|
-
projectRoot: process.cwd()
|
|
21896
|
+
projectRoot: process.cwd(),
|
|
21897
|
+
target: iosTarget
|
|
21311
21898
|
});
|
|
21312
21899
|
}
|
|
21313
21900
|
}
|
|
@@ -21351,7 +21938,8 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21351
21938
|
serverEntry,
|
|
21352
21939
|
...configPath2 ? ["--config", configPath2] : [],
|
|
21353
21940
|
...options.mobile === false ? ["--no-mobile"] : [],
|
|
21354
|
-
...options.androidDevice ? ["--android-device", options.androidDevice] : []
|
|
21941
|
+
...options.androidDevice ? ["--android-device", options.androidDevice] : [],
|
|
21942
|
+
...options.iosDevice ? ["--ios-device", options.iosDevice] : []
|
|
21355
21943
|
].filter((part) => part.length > 0);
|
|
21356
21944
|
registerInstance({
|
|
21357
21945
|
command: relaunchCommand,
|
|
@@ -21361,6 +21949,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21361
21949
|
frameworks: [],
|
|
21362
21950
|
host: resolvedDev.host,
|
|
21363
21951
|
https: httpsEnabled,
|
|
21952
|
+
...selectedRemoteMacProfile ? { iosRemoteMac: selectedRemoteMacProfile.name } : {},
|
|
21364
21953
|
logFile: instanceLogFile,
|
|
21365
21954
|
name: resolveProjectName(process.cwd()),
|
|
21366
21955
|
pid: instancePid,
|
|
@@ -21523,6 +22112,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21523
22112
|
platform: "ios",
|
|
21524
22113
|
provider: "capacitor",
|
|
21525
22114
|
startedSimulator: session.startedSimulator,
|
|
22115
|
+
target: session.targetKind,
|
|
21526
22116
|
timings: session.timings
|
|
21527
22117
|
});
|
|
21528
22118
|
if (session.timings.building === undefined)
|
|
@@ -21533,14 +22123,17 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21533
22123
|
installMs: session.timings.installing,
|
|
21534
22124
|
platform: "ios",
|
|
21535
22125
|
provider: "capacitor",
|
|
21536
|
-
success: true
|
|
22126
|
+
success: true,
|
|
22127
|
+
target: session.targetKind
|
|
21537
22128
|
});
|
|
21538
22129
|
};
|
|
21539
22130
|
const openIosDevSession = (iosProject) => {
|
|
21540
22131
|
const sessionOptions = {
|
|
21541
22132
|
certificateAuthorityPath: devCertificateAuthorityPath ?? undefined,
|
|
22133
|
+
deviceIdentifier: options.iosDevice,
|
|
21542
22134
|
https: httpsEnabled,
|
|
21543
22135
|
port,
|
|
22136
|
+
serverHost: iosPhysicalServerHost,
|
|
21544
22137
|
signal: iosDevAbort.signal,
|
|
21545
22138
|
log: (message) => printNativeOutput(cliTag("\x1B[35m", message)),
|
|
21546
22139
|
nativeLog: (entry) => printNativeOutput(iosLogTag(entry)),
|
|
@@ -21554,7 +22147,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21554
22147
|
iosDevState = state;
|
|
21555
22148
|
if (state === "ready" || state === "closed")
|
|
21556
22149
|
return;
|
|
21557
|
-
printNativeOutput(cliTag("\x1B[35m", `iOS simulator: ${state}.`));
|
|
22150
|
+
printNativeOutput(cliTag("\x1B[35m", `iOS ${options.iosDevice ? "device" : "simulator"}: ${state}.`));
|
|
21558
22151
|
}
|
|
21559
22152
|
};
|
|
21560
22153
|
if (iosProject.remote)
|
|
@@ -21593,6 +22186,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21593
22186
|
provider: "capacitor",
|
|
21594
22187
|
rootInputChanged: change.rootInputChanged,
|
|
21595
22188
|
success: true,
|
|
22189
|
+
target: replacement.targetKind,
|
|
21596
22190
|
timings: replacement.timings
|
|
21597
22191
|
});
|
|
21598
22192
|
},
|
|
@@ -21603,7 +22197,8 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21603
22197
|
host: iosTelemetryHost(iosProject),
|
|
21604
22198
|
platform: "ios",
|
|
21605
22199
|
provider: "capacitor",
|
|
21606
|
-
success: false
|
|
22200
|
+
success: false,
|
|
22201
|
+
target: options.iosDevice ? "device" : "simulator"
|
|
21607
22202
|
});
|
|
21608
22203
|
}
|
|
21609
22204
|
});
|
|
@@ -21630,9 +22225,10 @@ var dev = async (serverEntry, configPath2, options = {}) => {
|
|
|
21630
22225
|
phase: iosDevState,
|
|
21631
22226
|
platform: "ios",
|
|
21632
22227
|
provider: "capacitor",
|
|
22228
|
+
target: options.iosDevice ? "device" : "simulator",
|
|
21633
22229
|
timings: iosPhaseTimings
|
|
21634
22230
|
});
|
|
21635
|
-
console.error(cliTag("\x1B[31m", `iOS simulator failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
22231
|
+
console.error(cliTag("\x1B[31m", `iOS ${options.iosDevice ? "device" : "simulator"} failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
21636
22232
|
}).finally(() => {
|
|
21637
22233
|
iosDevStart = null;
|
|
21638
22234
|
});
|
|
@@ -23982,16 +24578,24 @@ if (command === "dev") {
|
|
|
23982
24578
|
sendTelemetryEvent("cli:command", { command });
|
|
23983
24579
|
const configPath2 = parseNamedArg("--config");
|
|
23984
24580
|
const androidDevice = parseNamedArg("--android-device");
|
|
24581
|
+
const iosDevice = parseNamedArg("--ios-device");
|
|
23985
24582
|
if (args.includes("--android-device") && !androidDevice) {
|
|
23986
24583
|
throw new TypeError("--android-device requires an ADB device serial.");
|
|
23987
24584
|
}
|
|
23988
24585
|
if (androidDevice && args.includes("--no-mobile")) {
|
|
23989
24586
|
throw new TypeError("--android-device cannot be combined with --no-mobile.");
|
|
23990
24587
|
}
|
|
23991
|
-
|
|
24588
|
+
if (args.includes("--ios-device") && !iosDevice) {
|
|
24589
|
+
throw new TypeError("--ios-device requires an Xcode device identifier or name.");
|
|
24590
|
+
}
|
|
24591
|
+
if (iosDevice && args.includes("--no-mobile")) {
|
|
24592
|
+
throw new TypeError("--ios-device cannot be combined with --no-mobile.");
|
|
24593
|
+
}
|
|
24594
|
+
const positionalArgs2 = stripNamedArgs("--config", "--android-device", "--ios-device").filter((arg) => arg !== "--no-mobile");
|
|
23992
24595
|
const serverEntry = positionalArgs2[0] ?? DEFAULT_SERVER_ENTRY;
|
|
23993
24596
|
await dev(serverEntry, configPath2, {
|
|
23994
24597
|
androidDevice,
|
|
24598
|
+
iosDevice,
|
|
23995
24599
|
mobile: !args.includes("--no-mobile")
|
|
23996
24600
|
});
|
|
23997
24601
|
} else if (command === "start") {
|
|
@@ -24150,13 +24754,13 @@ if (command === "dev") {
|
|
|
24150
24754
|
console.error(message);
|
|
24151
24755
|
console.error("Usage: absolute <command>");
|
|
24152
24756
|
console.error("Commands:");
|
|
24153
|
-
console.error(" dev [entry] [--no-mobile] [--android-device serial] Start web and configured mobile development");
|
|
24757
|
+
console.error(" dev [entry] [--no-mobile] [--android-device serial] [--ios-device identifier] Start web and configured mobile development");
|
|
24154
24758
|
console.error(" workspace dev [--no-tui] Start multi-service workspace dev");
|
|
24155
24759
|
console.error(" build [--outdir dir] [--profile] Build production assets");
|
|
24156
24760
|
console.error(" prepare [entry] [--outdir dir] Build production assets and server without launching");
|
|
24157
24761
|
console.error(" start [entry] [--outdir dir] [--prebuilt] Start production server");
|
|
24158
24762
|
console.error(" compile [entry] [--outdir dir] [--outfile path] Compile standalone executable");
|
|
24159
|
-
console.error(" mobile <init|sync|pair|remotes|doctor|test> Manage Capacitor projects,
|
|
24763
|
+
console.error(" mobile <init|sync|pair|remotes|doctor|test> Manage Capacitor projects, simulators, physical devices, Remote Macs, guided setup, and deep links");
|
|
24160
24764
|
console.error(" config [--port n] Open the unified config UI (ESLint, tsconfig, Prettier)");
|
|
24161
24765
|
console.error(" db <backup|restore|seed> Backup/restore any Postgres DB (ORM-agnostic, upsert by PK) or run the seed script");
|
|
24162
24766
|
console.error(" doctor [--fix] [--json] Diagnose the project (bun, type graph, config, framework dirs, env, port)");
|