@absolutejs/absolute 0.20.0-beta.3 → 0.20.0-beta.4

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.
@@ -2533,14 +2533,582 @@ var createAbsoluteIosNativeWatcher = async (options) => {
2533
2533
  return { close };
2534
2534
  };
2535
2535
  var isAbsoluteIosNativeRootInput = (path) => ROOT_NATIVE_INPUTS.has(basename(path));
2536
+ // src/mobile/remoteMacProtocol.ts
2537
+ import { createHash as createHash7, randomUUID as randomUUID3 } from "crypto";
2538
+ import { chmod, mkdir as mkdir6, readFile as readFile8, rename as rename7, writeFile as writeFile7 } from "fs/promises";
2539
+ import { homedir as homedir2 } from "os";
2540
+ import {
2541
+ dirname as dirname5,
2542
+ isAbsolute as isAbsolute5,
2543
+ join as join7,
2544
+ posix,
2545
+ relative as relative6,
2546
+ resolve as resolvePath2,
2547
+ sep as sep5
2548
+ } from "path";
2549
+
2550
+ // src/mobile/remoteMacWire.ts
2551
+ var ABSOLUTE_REMOTE_MAC_EVENT_PREFIX = "ABSOLUTE_REMOTE_MAC\t";
2552
+ var ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION = 1;
2553
+
2554
+ // src/mobile/remoteMacProtocol.ts
2555
+ var PROFILE_FORMAT = 1;
2556
+ var PROFILE_NAME = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/u;
2557
+ var SSH_DESTINATION = /^(?:[A-Za-z0-9._-]+@)?[A-Za-z0-9._:-]+$/u;
2558
+ var defaultProfilePath = () => join7(homedir2(), ".absolutejs", "mobile", "remote-macs.json");
2559
+ var emptyStore = () => ({
2560
+ format: PROFILE_FORMAT,
2561
+ profiles: {}
2562
+ });
2563
+ var loadStore = async (path = defaultProfilePath()) => {
2564
+ try {
2565
+ const parsed = JSON.parse(await readFile8(path, "utf8"));
2566
+ if (parsed.format !== PROFILE_FORMAT || typeof parsed.profiles !== "object" || parsed.profiles === null || Array.isArray(parsed.profiles))
2567
+ throw new Error("Unsupported remote Mac profile format.");
2568
+ for (const [key, profile] of Object.entries(parsed.profiles)) {
2569
+ if (typeof profile !== "object" || profile === null || validateAbsoluteRemoteMacProfileName(key) !== key || profile.name !== key || validateAbsoluteSshDestination(profile.destination) !== profile.destination || validatePort(profile.port) !== profile.port || typeof profile.createdAt !== "string" || !profile.createdAt || typeof profile.bunPath !== "string" || !profile.bunPath.startsWith("/") || /[\r\n\0]/u.test(profile.bunPath) || typeof profile.workspaceRoot !== "string" || !profile.workspaceRoot.startsWith("/") || profile.workspaceRoot === "/" || /[\r\n\0]/u.test(profile.workspaceRoot) || typeof profile.xcodeVersion !== "string" || !profile.xcodeVersion.startsWith("Xcode "))
2570
+ throw new Error(`Remote Mac profile ${JSON.stringify(key)} is invalid.`);
2571
+ }
2572
+ if (parsed.defaultProfile !== undefined && !parsed.profiles[parsed.defaultProfile])
2573
+ throw new Error("The default remote Mac profile does not exist.");
2574
+ return parsed;
2575
+ } catch (error) {
2576
+ if (error.code === "ENOENT")
2577
+ return emptyStore();
2578
+ throw error;
2579
+ }
2580
+ };
2581
+ var saveStore = async (store, path = defaultProfilePath()) => {
2582
+ await mkdir6(dirname5(path), { recursive: true });
2583
+ const temporary = `${path}.${randomUUID3()}.tmp`;
2584
+ await writeFile7(temporary, `${JSON.stringify(store, null, 2)}
2585
+ `, {
2586
+ mode: 384
2587
+ });
2588
+ await rename7(temporary, path);
2589
+ await chmod(path, 384);
2590
+ };
2591
+ var validateAbsoluteRemoteMacProfileName = (name) => {
2592
+ const normalized = name.trim().toLowerCase();
2593
+ if (!PROFILE_NAME.test(normalized))
2594
+ throw new TypeError("Remote Mac profile names must use 1-64 lowercase letters, digits, dots, dashes, or underscores.");
2595
+ return normalized;
2596
+ };
2597
+ var validateAbsoluteSshDestination = (destination) => {
2598
+ const normalized = destination.trim();
2599
+ if (!SSH_DESTINATION.test(normalized) || normalized.startsWith("-"))
2600
+ throw new TypeError("Remote Mac SSH destination must be a host, SSH alias, or user@host without command-line options.");
2601
+ return normalized;
2602
+ };
2603
+ var validatePort = (port) => {
2604
+ if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535))
2605
+ throw new TypeError("Remote Mac SSH port must be between 1 and 65535.");
2606
+ return port;
2607
+ };
2608
+ var shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
2609
+ var absoluteRemoteMacSshBase = (profile, options = {}) => [
2610
+ "ssh",
2611
+ "-o",
2612
+ "BatchMode=yes",
2613
+ "-o",
2614
+ "ConnectTimeout=10",
2615
+ "-o",
2616
+ "ServerAliveInterval=15",
2617
+ "-o",
2618
+ "ServerAliveCountMax=3",
2619
+ "-o",
2620
+ `StrictHostKeyChecking=${options.acceptNew ? "accept-new" : "yes"}`,
2621
+ ...profile.port ? ["-p", String(profile.port)] : [],
2622
+ profile.destination
2623
+ ];
2624
+ var localCapture = async (command) => {
2625
+ const process2 = Bun.spawn(command, {
2626
+ stderr: "pipe",
2627
+ stdin: "ignore",
2628
+ stdout: "pipe"
2629
+ });
2630
+ const [exitCode, stdout, stderr] = await Promise.all([
2631
+ process2.exited,
2632
+ new Response(process2.stdout).text(),
2633
+ new Response(process2.stderr).text()
2634
+ ]);
2635
+ return { exitCode, stderr, stdout };
2636
+ };
2637
+ var defaultTransport = {
2638
+ capture: localCapture,
2639
+ spawn: (command, options) => Bun.spawn(command, {
2640
+ signal: options.signal,
2641
+ stderr: "pipe",
2642
+ stdin: "pipe",
2643
+ stdout: "pipe"
2644
+ })
2645
+ };
2646
+ var requireRemoteSuccess = (result, label) => {
2647
+ if (result.exitCode !== 0)
2648
+ throw new Error(`${label} failed: ${(result.stderr || result.stdout).trim() || `status ${result.exitCode}`}`);
2649
+ return result.stdout.trim();
2650
+ };
2651
+ var getAbsoluteRemoteMacProfile = async (name, profilePath) => {
2652
+ const store = await loadStore(profilePath);
2653
+ const selected = name ?? process.env.ABSOLUTE_IOS_REMOTE ?? store.defaultProfile;
2654
+ if (!selected)
2655
+ return;
2656
+ const profile = store.profiles[selected];
2657
+ if (!profile)
2658
+ throw new Error(`Remote Mac profile ${JSON.stringify(selected)} was not found.`);
2659
+ return profile;
2660
+ };
2661
+ var inspectAbsoluteRemoteMac = async (destination, options = {}) => {
2662
+ const profile = {
2663
+ destination: validateAbsoluteSshDestination(destination),
2664
+ port: validatePort(options.port)
2665
+ };
2666
+ const capture = options.transport?.capture ?? defaultTransport.capture;
2667
+ const command = [
2668
+ ...absoluteRemoteMacSshBase(profile, {
2669
+ acceptNew: options.acceptNew === true
2670
+ }),
2671
+ "/bin/sh -lc",
2672
+ shellQuote(`bun_path="$(command -v bun || true)"; if [ -z "$bun_path" ] && [ -x "$HOME/.bun/bin/bun" ]; then bun_path="$HOME/.bun/bin/bun"; fi; printf '%s\\n' "$(uname -s)" "$HOME" "$bun_path" "$(/usr/bin/xcodebuild -version 2>/dev/null | tr '\\n' ' ' || true)"`)
2673
+ ];
2674
+ const lines = requireRemoteSuccess(await capture(command), "Remote Mac handshake").split(/\r?\n/u);
2675
+ const [operatingSystem, home, bunPath, xcodeVersion] = lines;
2676
+ if (operatingSystem !== "Darwin")
2677
+ throw new Error("The SSH target is not a Mac.");
2678
+ if (!home?.startsWith("/") || !bunPath?.startsWith("/"))
2679
+ throw new Error("The remote Mac must have Bun installed and available to SSH.");
2680
+ if (!xcodeVersion?.startsWith("Xcode "))
2681
+ throw new Error("The remote Mac must have full Xcode installed and selected.");
2682
+ return { bunPath, home, os: operatingSystem, xcodeVersion };
2683
+ };
2684
+ var listAbsoluteRemoteMacProfiles = async (profilePath) => {
2685
+ const store = await loadStore(profilePath);
2686
+ return {
2687
+ defaultProfile: store.defaultProfile,
2688
+ profiles: Object.values(store.profiles).sort((left, right) => left.name.localeCompare(right.name))
2689
+ };
2690
+ };
2691
+ var pairAbsoluteRemoteMac = async (options) => {
2692
+ const name = validateAbsoluteRemoteMacProfileName(options.name);
2693
+ const destination = validateAbsoluteSshDestination(options.destination);
2694
+ const port = validatePort(options.port);
2695
+ const inspection = await inspectAbsoluteRemoteMac(destination, {
2696
+ acceptNew: true,
2697
+ port,
2698
+ transport: options.transport
2699
+ });
2700
+ const workspaceRoot = options.workspaceRoot ? options.workspaceRoot.trim() : posix.join(inspection.home, ".absolutejs", "remote-ios");
2701
+ if (!workspaceRoot.startsWith("/") || workspaceRoot === "/" || /[\r\n\0]/u.test(workspaceRoot))
2702
+ throw new TypeError("Remote Mac workspace must be an absolute macOS path.");
2703
+ const profile = {
2704
+ bunPath: inspection.bunPath,
2705
+ createdAt: new Date().toISOString(),
2706
+ destination,
2707
+ name,
2708
+ ...port ? { port } : {},
2709
+ workspaceRoot,
2710
+ xcodeVersion: inspection.xcodeVersion
2711
+ };
2712
+ const store = await loadStore(options.profilePath);
2713
+ store.profiles[name] = profile;
2714
+ store.defaultProfile = name;
2715
+ await saveStore(store, options.profilePath);
2716
+ return profile;
2717
+ };
2718
+ var removeAbsoluteRemoteMacProfile = async (name, profilePath) => {
2719
+ const normalized = validateAbsoluteRemoteMacProfileName(name);
2720
+ const store = await loadStore(profilePath);
2721
+ if (!store.profiles[normalized])
2722
+ return false;
2723
+ delete store.profiles[normalized];
2724
+ if (store.defaultProfile === normalized) {
2725
+ const [nextDefault] = Object.keys(store.profiles).sort();
2726
+ store.defaultProfile = nextDefault;
2727
+ }
2728
+ await saveStore(store, profilePath);
2729
+ return true;
2730
+ };
2731
+ var projectIdentity = (projectRoot, appId) => createHash7("sha256").update(`${resolvePath2(projectRoot)}\x00${appId}`).digest("hex").slice(0, 20);
2732
+ var createAbsoluteRemoteIosDevProject = (config, projectRoot, profile) => ({
2733
+ cap: join7(resolvePath2(projectRoot), "node_modules", ".bin", "cap"),
2734
+ config,
2735
+ nativeDirectory: join7(config.nativeProjectDirectory, "ios"),
2736
+ profile,
2737
+ projectRoot: resolvePath2(projectRoot),
2738
+ remote: true,
2739
+ remoteProjectRoot: posix.join(profile.workspaceRoot, "projects", projectIdentity(projectRoot, config.appId), "current"),
2740
+ xcodebuild: "remote:xcodebuild",
2741
+ xcrun: "remote:xcrun"
2742
+ });
2743
+ var installAbsoluteRemoteMacAgent = async (project) => {
2744
+ const artifact = await materializeAbsoluteRemoteMacAgent(project.projectRoot);
2745
+ const directory = posix.join(project.profile.workspaceRoot, "agents", `protocol-${ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION}`, artifact.sha256);
2746
+ const remotePath = posix.join(directory, "agent.js");
2747
+ const verifyScript = `test -f ${shellQuote(remotePath)} && ` + `test "$(shasum -a 256 ${shellQuote(remotePath)} | awk '{print $1}')" = ${shellQuote(artifact.sha256)}`;
2748
+ const verified = await defaultTransport.capture([
2749
+ ...absoluteRemoteMacSshBase(project.profile),
2750
+ "/bin/sh -lc",
2751
+ shellQuote(verifyScript)
2752
+ ]);
2753
+ if (verified.exitCode === 0)
2754
+ return { ...artifact, remotePath, uploaded: false };
2755
+ const temporary = posix.join(directory, `.agent-${randomUUID3()}.tmp`);
2756
+ const installScript = [
2757
+ "set -eu",
2758
+ "umask 077",
2759
+ `mkdir -p ${shellQuote(directory)}`,
2760
+ `cat > ${shellQuote(temporary)}`,
2761
+ `test "$(shasum -a 256 ${shellQuote(temporary)} | awk '{print $1}')" = ${shellQuote(artifact.sha256)}`,
2762
+ `chmod 600 ${shellQuote(temporary)}`,
2763
+ `mv ${shellQuote(temporary)} ${shellQuote(remotePath)}`
2764
+ ].join("; ");
2765
+ const upload = Bun.spawn([
2766
+ ...absoluteRemoteMacSshBase(project.profile),
2767
+ "/bin/sh -lc",
2768
+ shellQuote(installScript)
2769
+ ], {
2770
+ stderr: "pipe",
2771
+ stdin: Bun.file(artifact.path),
2772
+ stdout: "pipe"
2773
+ });
2774
+ const [exitCode, stderr] = await Promise.all([
2775
+ upload.exited,
2776
+ new Response(upload.stderr).text()
2777
+ ]);
2778
+ if (exitCode !== 0)
2779
+ throw new Error(`Remote Mac agent installation failed: ${stderr.trim() || `status ${exitCode}`}`);
2780
+ return { ...artifact, remotePath, uploaded: true };
2781
+ };
2782
+ var materializeAbsoluteRemoteMacAgent = async (projectRoot) => {
2783
+ const shippedCandidates = [
2784
+ join7(import.meta.dir, "remoteMacAgentEntry.js"),
2785
+ join7(import.meta.dir, "..", "mobile", "remoteMacAgentEntry.js")
2786
+ ];
2787
+ let path;
2788
+ for (const candidate of shippedCandidates) {
2789
+ if (await Bun.file(candidate).exists()) {
2790
+ path = candidate;
2791
+ break;
2792
+ }
2793
+ }
2794
+ if (!path) {
2795
+ const sourceCandidates = [
2796
+ join7(import.meta.dir, "remoteMacAgentEntry.ts"),
2797
+ join7(import.meta.dir, "..", "..", "src", "mobile", "remoteMacAgentEntry.ts")
2798
+ ];
2799
+ const source = await sourceCandidates.reduce(async (found, candidate) => await found ?? (await Bun.file(candidate).exists() ? candidate : undefined), Promise.resolve(undefined));
2800
+ if (!source)
2801
+ throw new Error("The AbsoluteJS installation does not contain its remote Mac agent artifact.");
2802
+ const outdir = join7(resolvePath2(projectRoot), ".absolutejs", "mobile", "remote-agent");
2803
+ await mkdir6(outdir, { recursive: true });
2804
+ const result = await Bun.build({
2805
+ entrypoints: [source],
2806
+ minify: true,
2807
+ outdir,
2808
+ target: "bun"
2809
+ });
2810
+ if (!result.success)
2811
+ throw new AggregateError(result.logs, "Failed to build the AbsoluteJS remote Mac agent.");
2812
+ path = join7(outdir, "remoteMacAgentEntry.js");
2813
+ }
2814
+ const bytes = await Bun.file(path).arrayBuffer();
2815
+ const sha256 = createHash7("sha256").update(new Uint8Array(bytes)).digest("hex");
2816
+ return { bytes: bytes.byteLength, path, sha256 };
2817
+ };
2818
+ var portableRelativePath = (root, path) => relative6(root, path).split(sep5).join(posix.sep);
2819
+ var portableMobileConfig = (project) => ({
2820
+ appId: project.config.appId,
2821
+ appName: project.config.appName,
2822
+ bundleDirectory: portableRelativePath(project.projectRoot, project.config.bundleDirectory),
2823
+ ...project.config.deepLinkScheme || project.config.deepLinkHosts.length > 1 || project.config.appleAppIdPrefix ? {
2824
+ deepLinks: {
2825
+ ...project.config.deepLinkScheme ? { scheme: project.config.deepLinkScheme } : {},
2826
+ hosts: project.config.deepLinkHosts,
2827
+ ...project.config.appleAppIdPrefix ? {
2828
+ apple: {
2829
+ appIdPrefix: project.config.appleAppIdPrefix
2830
+ }
2831
+ } : {}
2832
+ }
2833
+ } : {},
2834
+ entry: project.config.entry,
2835
+ ...project.config.iosVersion ? { ios: { version: project.config.iosVersion } } : {},
2836
+ nativeProject: {
2837
+ directory: portableRelativePath(project.projectRoot, project.config.nativeProjectDirectory),
2838
+ mode: "source"
2839
+ },
2840
+ platforms: ["ios"],
2841
+ server: { productionOrigin: project.config.productionOrigin }
2842
+ });
2843
+ var absoluteRemoteProjectSyncCommands = (project) => {
2844
+ const current = project.remoteProjectRoot;
2845
+ const parent = posix.dirname(current);
2846
+ const staging = posix.join(parent, `.incoming-${randomUUID3()}`);
2847
+ const previous = posix.join(parent, ".previous");
2848
+ const script = [
2849
+ "set -eu",
2850
+ `mkdir -p ${shellQuote(staging)}`,
2851
+ `tar -xf - -C ${shellQuote(staging)}`,
2852
+ `if [ -d ${shellQuote(posix.join(current, "node_modules"))} ]; then mv ${shellQuote(posix.join(current, "node_modules"))} ${shellQuote(posix.join(staging, "node_modules"))}; fi`,
2853
+ `if [ -d ${shellQuote(posix.join(current, ".absolutejs"))} ]; then mv ${shellQuote(posix.join(current, ".absolutejs"))} ${shellQuote(posix.join(staging, ".absolutejs"))}; fi`,
2854
+ `rm -rf ${shellQuote(previous)}`,
2855
+ `if [ -d ${shellQuote(current)} ]; then mv ${shellQuote(current)} ${shellQuote(previous)}; fi`,
2856
+ `mv ${shellQuote(staging)} ${shellQuote(current)}`,
2857
+ `rm -rf ${shellQuote(previous)}`
2858
+ ].join("; ");
2859
+ return {
2860
+ remote: [
2861
+ ...absoluteRemoteMacSshBase(project.profile),
2862
+ "/bin/sh -lc",
2863
+ shellQuote(script)
2864
+ ],
2865
+ tar: [
2866
+ "tar",
2867
+ "--exclude=.git",
2868
+ "--exclude=node_modules",
2869
+ "--exclude=build",
2870
+ "--exclude=.absolutejs",
2871
+ "-cf",
2872
+ "-",
2873
+ "-C",
2874
+ project.projectRoot,
2875
+ "."
2876
+ ]
2877
+ };
2878
+ };
2879
+ var syncAbsoluteRemoteMacProject = async (project) => {
2880
+ const commands = absoluteRemoteProjectSyncCommands(project);
2881
+ const archive = Bun.spawn(commands.tar, {
2882
+ stderr: "pipe",
2883
+ stdout: "pipe"
2884
+ });
2885
+ const upload = Bun.spawn(commands.remote, {
2886
+ stderr: "pipe",
2887
+ stdin: archive.stdout,
2888
+ stdout: "pipe"
2889
+ });
2890
+ const [archiveExit, uploadExit, archiveError, uploadError] = await Promise.all([
2891
+ archive.exited,
2892
+ upload.exited,
2893
+ new Response(archive.stderr).text(),
2894
+ new Response(upload.stderr).text()
2895
+ ]);
2896
+ if (archiveExit !== 0 || uploadExit !== 0)
2897
+ throw new Error(`Remote Mac project synchronization failed: ${(archiveError || uploadError).trim()}`);
2898
+ const install = await defaultTransport.capture([
2899
+ ...absoluteRemoteMacSshBase(project.profile),
2900
+ "/bin/sh -lc",
2901
+ shellQuote(`cd ${shellQuote(project.remoteProjectRoot)} && ${shellQuote(project.profile.bunPath)} install --frozen-lockfile`)
2902
+ ]);
2903
+ requireRemoteSuccess(install, "Remote Mac dependency installation");
2904
+ };
2905
+ var consumeLines2 = async (stream, onLine) => {
2906
+ const reader = stream.getReader();
2907
+ const decoder = new TextDecoder;
2908
+ let buffered = "";
2909
+ try {
2910
+ while (true) {
2911
+ const { done, value } = await reader.read();
2912
+ if (done)
2913
+ break;
2914
+ buffered += decoder.decode(value, { stream: true });
2915
+ const lines = buffered.split(/\r?\n/u);
2916
+ buffered = lines.pop() ?? "";
2917
+ lines.forEach(onLine);
2918
+ }
2919
+ buffered += decoder.decode();
2920
+ if (buffered)
2921
+ onLine(buffered);
2922
+ } finally {
2923
+ reader.releaseLock();
2924
+ }
2925
+ };
2926
+ var startAbsoluteRemoteIosDevSession = async (options) => {
2927
+ const startedAt = performance.now();
2928
+ const transport = options.transport ?? defaultTransport;
2929
+ const installAgent = options.installAgent ?? installAbsoluteRemoteMacAgent;
2930
+ const syncProject = options.syncProject ?? syncAbsoluteRemoteMacProject;
2931
+ const agentStartedAt = performance.now();
2932
+ const agent = await installAgent(options.project);
2933
+ const agentDuration = performance.now() - agentStartedAt;
2934
+ const syncStartedAt = performance.now();
2935
+ await syncProject(options.project);
2936
+ const syncDuration = performance.now() - syncStartedAt;
2937
+ const encodedConfig = Buffer.from(JSON.stringify(portableMobileConfig(options.project))).toString("base64url");
2938
+ const remoteCommand = [
2939
+ `cd ${shellQuote(options.project.remoteProjectRoot)}`,
2940
+ "&&",
2941
+ "exec",
2942
+ shellQuote(options.project.profile.bunPath),
2943
+ shellQuote(agent.remotePath),
2944
+ "--port",
2945
+ String(options.port),
2946
+ "--mobile-config",
2947
+ shellQuote(encodedConfig),
2948
+ ...options.https ? ["--https"] : []
2949
+ ].join(" ");
2950
+ const command = [
2951
+ ...absoluteRemoteMacSshBase(options.project.profile),
2952
+ "-o",
2953
+ "ExitOnForwardFailure=yes",
2954
+ "-R",
2955
+ `${options.port}:127.0.0.1:${options.port}`,
2956
+ "/bin/sh -lc",
2957
+ shellQuote(remoteCommand)
2958
+ ];
2959
+ const connectStartedAt = performance.now();
2960
+ const process2 = transport.spawn(command, { signal: options.signal });
2961
+ let state = "syncing";
2962
+ let ready;
2963
+ let fatal;
2964
+ const pending = new Map;
2965
+ let resolveReady;
2966
+ let rejectReady;
2967
+ const readyPromise = new Promise((resolve6, reject) => {
2968
+ resolveReady = resolve6;
2969
+ rejectReady = reject;
2970
+ });
2971
+ const handleEvent = (event) => {
2972
+ if (event.v !== ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION) {
2973
+ rejectReady(new Error("Remote Mac protocol version mismatch."));
2974
+ return;
2975
+ }
2976
+ if (event.type === "log")
2977
+ options.log?.(event.message);
2978
+ if (event.type === "native-log")
2979
+ options.nativeLog?.(event.entry);
2980
+ if (event.type === "state") {
2981
+ ({ state } = event);
2982
+ options.onStateChange?.(state);
2983
+ }
2984
+ if (event.type === "timing")
2985
+ options.onPhaseTiming?.(event);
2986
+ if (event.type === "ready") {
2987
+ ready = event;
2988
+ resolveReady();
2989
+ }
2990
+ if (event.type === "fatal") {
2991
+ fatal = new Error(event.error);
2992
+ rejectReady(fatal);
2993
+ }
2994
+ if (event.type === "response") {
2995
+ const request2 = pending.get(event.id);
2996
+ if (!request2)
2997
+ return;
2998
+ pending.delete(event.id);
2999
+ if (event.ok)
3000
+ request2.resolve(event.result);
3001
+ else
3002
+ request2.reject(new Error(event.error ?? "Remote command failed."));
3003
+ }
3004
+ };
3005
+ const stdoutDone = consumeLines2(process2.stdout, (line) => {
3006
+ if (!line.startsWith(ABSOLUTE_REMOTE_MAC_EVENT_PREFIX))
3007
+ return;
3008
+ try {
3009
+ handleEvent(JSON.parse(line.slice(ABSOLUTE_REMOTE_MAC_EVENT_PREFIX.length)));
3010
+ } catch {
3011
+ options.log?.(`Remote Mac emitted an invalid protocol event.`);
3012
+ }
3013
+ }).catch((error) => {
3014
+ fatal = error instanceof Error ? error : new Error("Failed to read the remote Mac protocol stream.");
3015
+ rejectReady(fatal);
3016
+ });
3017
+ const stderrDone = consumeLines2(process2.stderr, (line) => options.log?.(`[remote] ${line}`)).catch((error) => options.log?.(`[remote] ${error instanceof Error ? error.message : "Failed to read SSH stderr."}`));
3018
+ process2.exited.then(async (exitCode) => {
3019
+ await Promise.all([stdoutDone, stderrDone]);
3020
+ const error = fatal ?? new Error(`Remote Mac connection closed with status ${exitCode}.`);
3021
+ if (!ready)
3022
+ rejectReady(error);
3023
+ pending.forEach(({ reject }) => reject(error));
3024
+ pending.clear();
3025
+ return;
3026
+ });
3027
+ await readyPromise;
3028
+ if (!ready)
3029
+ throw fatal ?? new Error("Remote Mac did not become ready.");
3030
+ const totalDuration = performance.now() - startedAt;
3031
+ let currentReady = {
3032
+ ...ready,
3033
+ timings: {
3034
+ ...ready.timings,
3035
+ "remote-agent": agentDuration,
3036
+ "remote-connect": performance.now() - connectStartedAt,
3037
+ "remote-sync": syncDuration,
3038
+ total: totalDuration
3039
+ }
3040
+ };
3041
+ 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.`);
3042
+ const request = (commandName) => {
3043
+ const id = randomUUID3();
3044
+ const response = new Promise((resolve6, reject) => pending.set(id, { reject, resolve: resolve6 }));
3045
+ process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: 1 })}
3046
+ `);
3047
+ process2.stdin.flush();
3048
+ return response;
3049
+ };
3050
+ let closed = false;
3051
+ const close = async () => {
3052
+ if (closed)
3053
+ return;
3054
+ closed = true;
3055
+ await request("close").catch(() => {
3056
+ return;
3057
+ });
3058
+ process2.stdin.end();
3059
+ await process2.exited.catch(() => {
3060
+ return;
3061
+ });
3062
+ };
3063
+ const makeSession = () => ({
3064
+ close,
3065
+ nativeCacheHit: currentReady.nativeCacheHit,
3066
+ startedSimulator: currentReady.startedSimulator,
3067
+ timings: currentReady.timings,
3068
+ udid: currentReady.udid,
3069
+ rebuild: async () => {
3070
+ const rebuildStartedAt = performance.now();
3071
+ const rebuildSyncStartedAt = performance.now();
3072
+ await syncProject(options.project);
3073
+ const rebuildSyncDuration = performance.now() - rebuildSyncStartedAt;
3074
+ const result = await request("rebuild");
3075
+ currentReady = {
3076
+ ...result,
3077
+ timings: {
3078
+ ...result.timings,
3079
+ "remote-sync": rebuildSyncDuration,
3080
+ total: performance.now() - rebuildStartedAt
3081
+ }
3082
+ };
3083
+ return makeSession();
3084
+ },
3085
+ relaunch: async () => {
3086
+ await request("relaunch");
3087
+ },
3088
+ screenshot: async (destination) => {
3089
+ const result = await request("screenshot");
3090
+ const target = resolvePath2(options.project.projectRoot, destination);
3091
+ const targetRelative = relative6(options.project.projectRoot, target);
3092
+ if (targetRelative.startsWith("..") || isAbsolute5(targetRelative))
3093
+ throw new Error("iOS screenshot must remain inside the project.");
3094
+ await mkdir6(dirname5(target), { recursive: true });
3095
+ await writeFile7(target, Buffer.from(result.data, "base64"));
3096
+ return target;
3097
+ },
3098
+ get state() {
3099
+ return state;
3100
+ }
3101
+ });
3102
+ return makeSession();
3103
+ };
2536
3104
  // src/mobile/associationFiles.ts
2537
3105
  import {
2538
3106
  access as access7,
2539
- mkdir as mkdir6,
2540
- readFile as readFile8,
2541
- rename as rename7,
3107
+ mkdir as mkdir7,
3108
+ readFile as readFile9,
3109
+ rename as rename8,
2542
3110
  rm as rm6,
2543
- writeFile as writeFile7
3111
+ writeFile as writeFile8
2544
3112
  } from "fs/promises";
2545
3113
  import { resolve as resolve7 } from "path";
2546
3114
  import { Elysia } from "elysia";
@@ -2742,7 +3310,7 @@ var createAbsoluteMobileAssociationPlugin = (mobile, projectRoot, options = {})
2742
3310
  var writeAtomic = async (path, source) => {
2743
3311
  let current;
2744
3312
  try {
2745
- current = await readFile8(path, "utf8");
3313
+ current = await readFile9(path, "utf8");
2746
3314
  } catch (error) {
2747
3315
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
2748
3316
  throw error;
@@ -2751,8 +3319,8 @@ var writeAtomic = async (path, source) => {
2751
3319
  if (current === source)
2752
3320
  return false;
2753
3321
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
2754
- await writeFile7(temporary, source, { flag: "wx" });
2755
- await rename7(temporary, path);
3322
+ await writeFile8(temporary, source, { flag: "wx" });
3323
+ await rename8(temporary, path);
2756
3324
  return true;
2757
3325
  };
2758
3326
  var exists2 = async (path) => {
@@ -2767,7 +3335,7 @@ var assertOwnedOutput = async (root) => {
2767
3335
  const path = resolve7(root, OWNERSHIP_FILE);
2768
3336
  let ownership;
2769
3337
  try {
2770
- ownership = JSON.parse(await readFile8(path, "utf8"));
3338
+ ownership = JSON.parse(await readFile9(path, "utf8"));
2771
3339
  } catch {
2772
3340
  throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
2773
3341
  }
@@ -2781,12 +3349,12 @@ var publishGeneratedDirectory = async (temporary, root) => {
2781
3349
  await assertOwnedOutput(root);
2782
3350
  const backup = `${root}.${crypto.randomUUID()}.previous`;
2783
3351
  if (hasCurrent)
2784
- await rename7(root, backup);
3352
+ await rename8(root, backup);
2785
3353
  try {
2786
- await rename7(temporary, root);
3354
+ await rename8(temporary, root);
2787
3355
  } catch (error) {
2788
3356
  if (hasCurrent)
2789
- await rename7(backup, root);
3357
+ await rename8(backup, root);
2790
3358
  throw error;
2791
3359
  }
2792
3360
  if (hasCurrent)
@@ -2794,7 +3362,7 @@ var publishGeneratedDirectory = async (temporary, root) => {
2794
3362
  };
2795
3363
  var materializeHost = async (root, host, files) => {
2796
3364
  const directory = resolve7(root, host, ".well-known");
2797
- await mkdir6(directory, { recursive: true });
3365
+ await mkdir7(directory, { recursive: true });
2798
3366
  return Promise.all(files.map(async ([name, document]) => {
2799
3367
  const path = resolve7(directory, name);
2800
3368
  await writeAtomic(path, `${JSON.stringify(document, null, 2)}
@@ -2832,7 +3400,7 @@ var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory)
2832
3400
  if (documents.apple) {
2833
3401
  files.push(["apple-app-site-association", documents.apple]);
2834
3402
  }
2835
- await mkdir6(temporary, { recursive: true });
3403
+ await mkdir7(temporary, { recursive: true });
2836
3404
  try {
2837
3405
  const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host) => materializeHost(temporary, host, files)))).flat();
2838
3406
  await writeAtomic(resolve7(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
@@ -2903,15 +3471,15 @@ var parseAbsoluteMobileBuildPageMetadata = (value) => {
2903
3471
  };
2904
3472
  };
2905
3473
  // src/mobile/buildPipeline.ts
2906
- import { readFile as readFile12 } from "fs/promises";
2907
- import { join as join10, resolve as resolve10 } from "path";
3474
+ import { readFile as readFile13 } from "fs/promises";
3475
+ import { join as join11, resolve as resolve10 } from "path";
2908
3476
  import { pathToFileURL as pathToFileURL2 } from "url";
2909
3477
 
2910
3478
  // src/mobile/buildRelease.ts
2911
- import { createHash as createHash7 } from "crypto";
2912
- import { readFile as readFile9 } from "fs/promises";
2913
- import { join as join7, relative as relative6, resolve as resolve8 } from "path";
2914
- var sha256 = (bytes) => createHash7("sha256").update(bytes).digest("hex");
3479
+ import { createHash as createHash8 } from "crypto";
3480
+ import { readFile as readFile10 } from "fs/promises";
3481
+ import { join as join8, relative as relative7, resolve as resolve8 } from "path";
3482
+ var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex");
2915
3483
  var readPageMetadata = (route) => parseAbsoluteMobileBuildPageMetadata(route.hooks?.detail?.[ABSOLUTE_MOBILE_ROUTE_DETAIL]);
2916
3484
  var resolveAssetPath = (buildDirectory, assetPath) => {
2917
3485
  const resolvedBuildDirectory = resolve8(buildDirectory);
@@ -2919,7 +3487,7 @@ var resolveAssetPath = (buildDirectory, assetPath) => {
2919
3487
  if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
2920
3488
  return resolvedAsset;
2921
3489
  }
2922
- return join7(buildDirectory, assetPath.replace(/^\/+/, ""));
3490
+ return join8(buildDirectory, assetPath.replace(/^\/+/, ""));
2923
3491
  };
2924
3492
  var pageFor = async (metadata, manifest, buildDirectory) => {
2925
3493
  const assetPath = manifest[metadata.bundleKey];
@@ -2927,8 +3495,8 @@ var pageFor = async (metadata, manifest, buildDirectory) => {
2927
3495
  throw new TypeError(`Mobile page ${metadata.pageId} references missing manifest asset ${metadata.bundleKey}.`);
2928
3496
  }
2929
3497
  const resolvedAssetPath = resolveAssetPath(buildDirectory, assetPath);
2930
- const bytes = await readFile9(resolvedAssetPath);
2931
- const bundlePath = `/${relative6(resolve8(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
3498
+ const bytes = await readFile10(resolvedAssetPath);
3499
+ const bundlePath = `/${relative7(resolve8(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
2932
3500
  return {
2933
3501
  bundleHash: sha256(bytes),
2934
3502
  bundlePath,
@@ -2941,7 +3509,7 @@ var pageFor = async (metadata, manifest, buildDirectory) => {
2941
3509
  var buildAbsoluteMobileCompatibilityRelease = async (options) => {
2942
3510
  const [captured, producerBytes] = await Promise.all([
2943
3511
  captureAbsoluteMobileRouteGraph(options.app),
2944
- readFile9(options.producerPath)
3512
+ readFile10(options.producerPath)
2945
3513
  ]);
2946
3514
  if (captured.length === 0) {
2947
3515
  throw new TypeError("No instrumented AbsoluteJS mobile page routes were found in the finalized Elysia route graph.");
@@ -3022,15 +3590,15 @@ var captureAbsoluteMobileRouteGraph = async (app) => {
3022
3590
  // src/mobile/capacitorBundle.ts
3023
3591
  import {
3024
3592
  copyFile as copyFile5,
3025
- mkdir as mkdir7,
3593
+ mkdir as mkdir8,
3026
3594
  mkdtemp as mkdtemp4,
3027
- readFile as readFile10,
3028
- rename as rename8,
3595
+ readFile as readFile11,
3596
+ rename as rename9,
3029
3597
  rm as rm7,
3030
- writeFile as writeFile8
3598
+ writeFile as writeFile9
3031
3599
  } from "fs/promises";
3032
3600
  import { existsSync as existsSync2 } from "fs";
3033
- import { basename as basename2, dirname as dirname5, extname, join as join8, resolve as resolve9 } from "path";
3601
+ import { basename as basename2, dirname as dirname6, extname, join as join9, resolve as resolve9 } from "path";
3034
3602
 
3035
3603
  // src/mobile/routeMatcher.ts
3036
3604
  var REGEXP_SPECIAL_CHARACTERS = /[.*+?^${}()|[\]\\]/g;
@@ -3464,7 +4032,7 @@ var INDEX_FILE = "index.html";
3464
4032
  var CLIENT_IMPORT_PATTERN = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s*)["'](\/[^"']+)["']/gu;
3465
4033
  var errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
3466
4034
  var shellBootstrapModule = () => {
3467
- const candidate = ["js", "ts"].map((extension) => join8(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync2);
4035
+ const candidate = ["js", "ts"].map((extension) => join9(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync2);
3468
4036
  if (candidate)
3469
4037
  return candidate;
3470
4038
  throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
@@ -3494,8 +4062,8 @@ var sourceAssetPath = (buildDirectory, bundlePath) => {
3494
4062
  };
3495
4063
  var buildShellBootstrap = async (staging) => {
3496
4064
  const modulePath = shellBootstrapModule();
3497
- const entryPath = join8(staging, ".absolute-mobile-entry.ts");
3498
- await writeFile8(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
4065
+ const entryPath = join9(staging, ".absolute-mobile-entry.ts");
4066
+ await writeFile9(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
3499
4067
  void startAbsoluteMobileShell();
3500
4068
  `);
3501
4069
  const build = await Bun.build({
@@ -3507,7 +4075,7 @@ void startAbsoluteMobileShell();
3507
4075
  if (!build.success || build.outputs.length !== 1) {
3508
4076
  throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
3509
4077
  }
3510
- await rename8(build.outputs[0]?.path ?? "", join8(staging, BOOTSTRAP_FILE));
4078
+ await rename9(build.outputs[0]?.path ?? "", join9(staging, BOOTSTRAP_FILE));
3511
4079
  await rm7(entryPath, { force: true });
3512
4080
  };
3513
4081
  var removePreviousBundle = async (backup, moved) => {
@@ -3518,20 +4086,20 @@ var removePreviousBundle = async (backup, moved) => {
3518
4086
  var restorePreviousBundle = async (backup, destination, moved) => {
3519
4087
  if (!moved)
3520
4088
  return;
3521
- await rename8(backup, destination);
4089
+ await rename9(backup, destination);
3522
4090
  };
3523
4091
  var installBundle = async (staging, destination) => {
3524
4092
  const backup = `${destination}.previous-${crypto.randomUUID()}`;
3525
4093
  let movedPrevious = false;
3526
4094
  try {
3527
- await rename8(destination, backup);
4095
+ await rename9(destination, backup);
3528
4096
  movedPrevious = true;
3529
4097
  } catch (error) {
3530
4098
  if (!errorHasCode2(error, "ENOENT"))
3531
4099
  throw error;
3532
4100
  }
3533
4101
  try {
3534
- await rename8(staging, destination);
4102
+ await rename9(staging, destination);
3535
4103
  await removePreviousBundle(backup, movedPrevious);
3536
4104
  } catch (error) {
3537
4105
  await restorePreviousBundle(backup, destination, movedPrevious);
@@ -3545,12 +4113,12 @@ var copyClientPage = async (page, buildDirectory, staging, copiedDependencies) =
3545
4113
  const extension = extname(page.bundlePath) || ".js";
3546
4114
  const localBundlePath = `./pages/${page.bundleHash}${extension}`;
3547
4115
  const source = sourceAssetPath(buildDirectory, page.bundlePath);
3548
- await copyFile5(source, join8(staging, localBundlePath));
4116
+ await copyFile5(source, join9(staging, localBundlePath));
3549
4117
  await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
3550
4118
  return { ...page, localBundlePath };
3551
4119
  };
3552
4120
  var absoluteClientImports = async (sourcePath) => {
3553
- const source = await readFile10(sourcePath, "utf8");
4121
+ const source = await readFile11(sourcePath, "utf8");
3554
4122
  return [...source.matchAll(CLIENT_IMPORT_PATTERN)].flatMap((match) => {
3555
4123
  const [specifier] = match.slice(1);
3556
4124
  return specifier ? [specifier.split(/[?#]/u, 1)[0] ?? specifier] : [];
@@ -3561,8 +4129,8 @@ var copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, co
3561
4129
  return;
3562
4130
  copied.add(specifier);
3563
4131
  const source = sourceAssetPath(buildDirectory, specifier);
3564
- const destination = join8(staging, specifier.replace(/^\/+/, ""));
3565
- await mkdir7(dirname5(destination), { recursive: true });
4132
+ const destination = join9(staging, specifier.replace(/^\/+/, ""));
4133
+ await mkdir8(dirname6(destination), { recursive: true });
3566
4134
  await copyFile5(source, destination);
3567
4135
  await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
3568
4136
  };
@@ -3575,11 +4143,11 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
3575
4143
  throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
3576
4144
  }
3577
4145
  const destination = options.config.bundleDirectory;
3578
- await mkdir7(dirname5(destination), { recursive: true });
3579
- const staging = await mkdtemp4(join8(dirname5(destination), `.${basename2(destination)}.stage-`));
4146
+ await mkdir8(dirname6(destination), { recursive: true });
4147
+ const staging = await mkdtemp4(join9(dirname6(destination), `.${basename2(destination)}.stage-`));
3580
4148
  try {
3581
- const pageDirectory = join8(staging, "pages");
3582
- await mkdir7(pageDirectory, { recursive: true });
4149
+ const pageDirectory = join9(staging, "pages");
4150
+ await mkdir8(pageDirectory, { recursive: true });
3583
4151
  const copiedDependencies = new Set;
3584
4152
  const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
3585
4153
  const manifest = {
@@ -3596,9 +4164,9 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
3596
4164
  runtime: options.artifact.runtime
3597
4165
  };
3598
4166
  await Promise.all([
3599
- writeFile8(join8(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
4167
+ writeFile9(join9(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
3600
4168
  `),
3601
- writeFile8(join8(staging, INDEX_FILE), indexHtml(options.config.appName)),
4169
+ writeFile9(join9(staging, INDEX_FILE), indexHtml(options.config.appName)),
3602
4170
  buildShellBootstrap(staging)
3603
4171
  ]);
3604
4172
  await installBundle(staging, destination);
@@ -3610,17 +4178,17 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
3610
4178
  };
3611
4179
 
3612
4180
  // src/mobile/materializedBundle.ts
3613
- import { createHash as createHash8 } from "crypto";
4181
+ import { createHash as createHash9 } from "crypto";
3614
4182
  import {
3615
4183
  access as access8,
3616
- mkdir as mkdir8,
4184
+ mkdir as mkdir9,
3617
4185
  mkdtemp as mkdtemp5,
3618
- readFile as readFile11,
3619
- rename as rename9,
4186
+ readFile as readFile12,
4187
+ rename as rename10,
3620
4188
  rm as rm8,
3621
- writeFile as writeFile9
4189
+ writeFile as writeFile10
3622
4190
  } from "fs/promises";
3623
- import { dirname as dirname6, join as join9, resolve as resolvePath2 } from "path";
4191
+ import { dirname as dirname7, join as join10, resolve as resolvePath3 } from "path";
3624
4192
  import { pathToFileURL } from "url";
3625
4193
  var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1;
3626
4194
  var CURRENT_BUNDLE_FILE = "current.json";
@@ -3634,7 +4202,7 @@ var bundleIdFor = (currentReleaseId, releases) => {
3634
4202
  currentReleaseId,
3635
4203
  releases: releases.map(({ releaseId }) => releaseId)
3636
4204
  });
3637
- return `amb_${createHash8("sha256").update(identity).digest("hex")}`;
4205
+ return `amb_${createHash9("sha256").update(identity).digest("hex")}`;
3638
4206
  };
3639
4207
  var parseBundleIndex = (value) => {
3640
4208
  if (!isRecord7(value) || value.format !== ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT || typeof value.bundleId !== "string" || !BUNDLE_ID_PATTERN.test(value.bundleId) || typeof value.currentReleaseId !== "string" || !Array.isArray(value.releases)) {
@@ -3660,17 +4228,17 @@ var parseBundleIndex = (value) => {
3660
4228
  };
3661
4229
  };
3662
4230
  var writeRelease = async (root, release) => {
3663
- const directory = join9(root, release.artifact.releaseId);
3664
- const producerPath = join9(directory, release.artifact.producer.module);
3665
- await mkdir8(dirname6(producerPath), { recursive: true });
4231
+ const directory = join10(root, release.artifact.releaseId);
4232
+ const producerPath = join10(directory, release.artifact.producer.module);
4233
+ await mkdir9(dirname7(producerPath), { recursive: true });
3666
4234
  await Promise.all([
3667
- writeFile9(join9(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
4235
+ writeFile10(join10(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
3668
4236
  `),
3669
- writeFile9(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
4237
+ writeFile10(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
3670
4238
  ]);
3671
4239
  };
3672
4240
  var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
3673
- const destination = join9(bundlesRoot, bundleId);
4241
+ const destination = join10(bundlesRoot, bundleId);
3674
4242
  try {
3675
4243
  await access8(destination);
3676
4244
  return destination;
@@ -3678,10 +4246,10 @@ var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
3678
4246
  if (!errorHasCode3(error, "ENOENT"))
3679
4247
  throw error;
3680
4248
  }
3681
- const staging = await mkdtemp5(join9(bundlesRoot, ".stage-"));
4249
+ const staging = await mkdtemp5(join10(bundlesRoot, ".stage-"));
3682
4250
  try {
3683
4251
  await Promise.all(releases.map((release) => writeRelease(staging, release)));
3684
- await rename9(staging, destination);
4252
+ await rename10(staging, destination);
3685
4253
  } catch (error) {
3686
4254
  await rm8(staging, { force: true, recursive: true });
3687
4255
  if (errorHasCode3(error, "EEXIST") || errorHasCode3(error, "ENOTEMPTY")) {
@@ -3711,16 +4279,16 @@ var resolveProducerHandler = (loaded, exportName) => {
3711
4279
  };
3712
4280
  };
3713
4281
  var loadAbsoluteMobileMaterializedBundle = async (root) => {
3714
- const resolvedRoot = resolvePath2(root);
3715
- const serialized = await readFile11(join9(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
4282
+ const resolvedRoot = resolvePath3(root);
4283
+ const serialized = await readFile12(join10(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
3716
4284
  const parsed = JSON.parse(serialized);
3717
4285
  const index = parseBundleIndex(parsed);
3718
- const bundleRoot = join9(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
4286
+ const bundleRoot = join10(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
3719
4287
  return {
3720
4288
  artifacts: index.releases,
3721
4289
  currentReleaseId: index.currentReleaseId,
3722
4290
  loadProducer: async (artifact) => {
3723
- const modulePath = join9(bundleRoot, artifact.releaseId, artifact.producer.module);
4291
+ const modulePath = join10(bundleRoot, artifact.releaseId, artifact.producer.module);
3724
4292
  await verifyAbsoluteMobileCompatibilityProducer({
3725
4293
  artifact,
3726
4294
  producer: Bun.file(modulePath)
@@ -3746,9 +4314,9 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
3746
4314
  }
3747
4315
  return release;
3748
4316
  });
3749
- const root = resolvePath2(input.root);
3750
- const bundlesRoot = join9(root, BUNDLES_DIRECTORY);
3751
- await mkdir8(bundlesRoot, { recursive: true });
4317
+ const root = resolvePath3(input.root);
4318
+ const bundlesRoot = join10(root, BUNDLES_DIRECTORY);
4319
+ await mkdir9(bundlesRoot, { recursive: true });
3752
4320
  const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
3753
4321
  await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
3754
4322
  const index = {
@@ -3757,22 +4325,22 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
3757
4325
  format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
3758
4326
  releases: artifacts
3759
4327
  };
3760
- const pointerPath = join9(root, CURRENT_BUNDLE_FILE);
3761
- const temporaryPointerPath = join9(root, `.current-${crypto.randomUUID()}.json`);
3762
- await writeFile9(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
4328
+ const pointerPath = join10(root, CURRENT_BUNDLE_FILE);
4329
+ const temporaryPointerPath = join10(root, `.current-${crypto.randomUUID()}.json`);
4330
+ await writeFile10(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
3763
4331
  `, { flag: "wx" });
3764
- await rename9(temporaryPointerPath, pointerPath);
4332
+ await rename10(temporaryPointerPath, pointerPath);
3765
4333
  return index;
3766
4334
  };
3767
4335
  var readAbsoluteMobileMaterializedReleases = async (root) => {
3768
- const resolvedRoot = resolvePath2(root);
4336
+ const resolvedRoot = resolvePath3(root);
3769
4337
  try {
3770
- const serialized = await readFile11(join9(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
4338
+ const serialized = await readFile12(join10(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
3771
4339
  const parsed = JSON.parse(serialized);
3772
4340
  const index = parseBundleIndex(parsed);
3773
- const bundleRoot = join9(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
4341
+ const bundleRoot = join10(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
3774
4342
  return Promise.all(index.releases.map(async (artifact) => {
3775
- const producer = Bun.file(join9(bundleRoot, artifact.releaseId, artifact.producer.module));
4343
+ const producer = Bun.file(join10(bundleRoot, artifact.releaseId, artifact.producer.module));
3776
4344
  await verifyAbsoluteMobileCompatibilityProducer({
3777
4345
  artifact,
3778
4346
  producer
@@ -3823,9 +4391,9 @@ var loadServerApp = async (producerPath) => {
3823
4391
  var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
3824
4392
  const buildDirectory = resolve10(options.buildDirectory);
3825
4393
  const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
3826
- const root = join10(buildDirectory, ".absolutejs", "mobile-compatibility");
4394
+ const root = join11(buildDirectory, ".absolutejs", "mobile-compatibility");
3827
4395
  const [manifestSource, previous] = await Promise.all([
3828
- readFile12(join10(buildDirectory, "manifest.json"), "utf8"),
4396
+ readFile13(join11(buildDirectory, "manifest.json"), "utf8"),
3829
4397
  readAbsoluteMobileMaterializedReleases(root)
3830
4398
  ]);
3831
4399
  const manifest = JSON.parse(manifestSource);
@@ -3941,20 +4509,20 @@ var createAbsoluteMobileCompatibilityDispatcher = (options) => {
3941
4509
  }).as("global");
3942
4510
  };
3943
4511
  // src/mobile/nativeDeepLinks.ts
3944
- import { readFile as readFile13, rename as rename10, writeFile as writeFile10 } from "fs/promises";
3945
- import { join as join11 } from "path";
4512
+ import { readFile as readFile14, rename as rename11, writeFile as writeFile11 } from "fs/promises";
4513
+ import { join as join12 } from "path";
3946
4514
  var START_MARKER = "<!-- absolutejs:deep-links:start -->";
3947
4515
  var END_MARKER = "<!-- absolutejs:deep-links:end -->";
3948
4516
  var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
3949
4517
  var NOT_FOUND = -1;
3950
4518
  var escapeXml = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
3951
4519
  var writeChangedFile = async (path, source) => {
3952
- const current = await readFile13(path, "utf8");
4520
+ const current = await readFile14(path, "utf8");
3953
4521
  if (current === source)
3954
4522
  return false;
3955
4523
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
3956
- await writeFile10(temporary, source, { flag: "wx" });
3957
- await rename10(temporary, path);
4524
+ await writeFile11(temporary, source, { flag: "wx" });
4525
+ await rename11(temporary, path);
3958
4526
  return true;
3959
4527
  };
3960
4528
  var replaceManagedRegion = (source, region, insertAt) => {
@@ -3999,8 +4567,8 @@ ${hosts}
3999
4567
  `;
4000
4568
  };
4001
4569
  var configureAndroid = async (config) => {
4002
- const path = join11(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
4003
- const source = await readFile13(path, "utf8");
4570
+ const path = join12(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
4571
+ const source = await readFile14(path, "utf8");
4004
4572
  const mainActivity = source.indexOf('android:name=".MainActivity"');
4005
4573
  if (mainActivity === NOT_FOUND) {
4006
4574
  throw new TypeError("Android MainActivity was not found.");
@@ -4025,8 +4593,8 @@ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
4025
4593
  ${END_MARKER}
4026
4594
  `;
4027
4595
  var configureIosInfo = async (config) => {
4028
- const path = join11(config.nativeProjectDirectory, "ios/App/App/Info.plist");
4029
- const source = await readFile13(path, "utf8");
4596
+ const path = join12(config.nativeProjectDirectory, "ios/App/App/Info.plist");
4597
+ const source = await readFile14(path, "utf8");
4030
4598
  const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
4031
4599
  ${END_MARKER}
4032
4600
  `;
@@ -4049,10 +4617,10 @@ ${domains}
4049
4617
  `;
4050
4618
  };
4051
4619
  var configureIosEntitlements = async (config) => {
4052
- const path = join11(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
4620
+ const path = join12(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
4053
4621
  let current = "";
4054
4622
  try {
4055
- current = await readFile13(path, "utf8");
4623
+ current = await readFile14(path, "utf8");
4056
4624
  } catch (error) {
4057
4625
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
4058
4626
  throw error;
@@ -4062,13 +4630,13 @@ var configureIosEntitlements = async (config) => {
4062
4630
  if (current === source)
4063
4631
  return false;
4064
4632
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
4065
- await writeFile10(temporary, source, { flag: "wx" });
4066
- await rename10(temporary, path);
4633
+ await writeFile11(temporary, source, { flag: "wx" });
4634
+ await rename11(temporary, path);
4067
4635
  return true;
4068
4636
  };
4069
4637
  var configureIosProject = async (config) => {
4070
- const path = join11(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
4071
- const source = await readFile13(path, "utf8");
4638
+ const path = join12(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
4639
+ const source = await readFile14(path, "utf8");
4072
4640
  const declarations = [
4073
4641
  ...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
4074
4642
  ].map((match) => match[1]);
@@ -4107,7 +4675,7 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
4107
4675
  };
4108
4676
  // src/mobile/releasePublisher.ts
4109
4677
  import { access as access9 } from "fs/promises";
4110
- import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve11, sep as sep5 } from "path";
4678
+ import { isAbsolute as isAbsolute6, relative as relative8, resolve as resolve11, sep as sep6 } from "path";
4111
4679
  import { pathToFileURL as pathToFileURL3 } from "url";
4112
4680
  var prepareAbsoluteIosRelease = async (publisher, options) => {
4113
4681
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -4135,8 +4703,8 @@ var isPublisher = (value) => isRecord8(value) && typeof value.publish === "funct
4135
4703
  var publisherModulePath = (projectRoot, requested) => {
4136
4704
  const root = resolve11(projectRoot);
4137
4705
  const path = resolve11(root, requested);
4138
- const projectRelative = relative7(root, path);
4139
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute5(projectRelative)) {
4706
+ const projectRelative = relative8(root, path);
4707
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep6}`) || isAbsolute6(projectRelative)) {
4140
4708
  throw new TypeError("mobile publish --registry must remain inside the project.");
4141
4709
  }
4142
4710
  return path;
@@ -4205,13 +4773,13 @@ var publishAbsoluteIosRelease = async (options) => {
4205
4773
  };
4206
4774
  // src/mobile/routeMetadataTransform.ts
4207
4775
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
4208
- import { dirname as dirname7, extname as extname2, relative as relative8, resolve as resolve12 } from "path";
4776
+ import { dirname as dirname8, extname as extname2, relative as relative9, resolve as resolve12 } from "path";
4209
4777
  import ts from "typescript";
4210
4778
  var ROUTE_METHODS = new Set(["get", "head"]);
4211
4779
  var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
4212
4780
  var PAGE_HANDLER = "handleReactPageRequest";
4213
4781
  var posixPath = (value) => value.replace(/\\/g, "/");
4214
- var findTsconfig = (entry, projectRoot) => ts.findConfigFile(dirname7(entry), existsSync3, "tsconfig.json") ?? ts.findConfigFile(projectRoot, existsSync3, "tsconfig.json");
4782
+ var findTsconfig = (entry, projectRoot) => ts.findConfigFile(dirname8(entry), existsSync3, "tsconfig.json") ?? ts.findConfigFile(projectRoot, existsSync3, "tsconfig.json");
4215
4783
  var createProgram = (entry, projectRoot) => {
4216
4784
  const configPath = findTsconfig(entry, projectRoot);
4217
4785
  if (!configPath) {
@@ -4223,7 +4791,7 @@ var createProgram = (entry, projectRoot) => {
4223
4791
  target: ts.ScriptTarget.ESNext
4224
4792
  });
4225
4793
  }
4226
- const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) => readFileSync2(path, "utf8")).config, ts.sys, dirname7(configPath));
4794
+ const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) => readFileSync2(path, "utf8")).config, ts.sys, dirname8(configPath));
4227
4795
  if (!parsed.fileNames.includes(entry))
4228
4796
  parsed.fileNames.push(entry);
4229
4797
  return ts.createProgram(parsed.fileNames, parsed.options);
@@ -4329,7 +4897,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
4329
4897
  const declaration = symbol?.declarations?.[0];
4330
4898
  const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
4331
4899
  const exportedName = symbol?.name ?? expression.getText(sourceFile);
4332
- const source = posixPath(relative8(projectRoot, file));
4900
+ const source = posixPath(relative9(projectRoot, file));
4333
4901
  return `${source}#${exportedName}`;
4334
4902
  };
4335
4903
  var resolveAlias = (symbol, checker) => {
@@ -4543,7 +5111,7 @@ var inspectAbsoluteMobileRouteMetadata = (options) => {
4543
5111
  const entry = resolve12(options.entry);
4544
5112
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
4545
5113
  return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
4546
- file: posixPath(relative8(projectRoot, file)),
5114
+ file: posixPath(relative9(projectRoot, file)),
4547
5115
  metadata
4548
5116
  })));
4549
5117
  };
@@ -4552,6 +5120,10 @@ export {
4552
5120
  waitForAbsoluteIosHmrLog,
4553
5121
  verifyAbsoluteMobileCompatibilityProducer,
4554
5122
  verifyAbsoluteMobileAssociationFiles,
5123
+ validateAbsoluteSshDestination,
5124
+ validateAbsoluteRemoteMacProfileName,
5125
+ syncAbsoluteRemoteMacProject,
5126
+ startAbsoluteRemoteIosDevSession,
4555
5127
  startAbsoluteIosDevSession,
4556
5128
  runWithAbsoluteMobileProducer,
4557
5129
  retainAbsoluteMobileCompatibilityArtifacts,
@@ -4559,6 +5131,7 @@ export {
4559
5131
  resolveAbsoluteMobileDeepLink,
4560
5132
  resolveAbsoluteMobileCompatibilityRelease,
4561
5133
  repairAbsoluteIosDevSession,
5134
+ removeAbsoluteRemoteMacProfile,
4562
5135
  redactAbsoluteIosLog,
4563
5136
  readAbsoluteMobileMaterializedReleases,
4564
5137
  publishAbsoluteIosRelease,
@@ -4575,23 +5148,30 @@ export {
4575
5148
  parseAbsoluteMobileBuildPageMetadata,
4576
5149
  parseAbsoluteIosLogLine,
4577
5150
  parseAbsoluteIosHmrLog,
5151
+ pairAbsoluteRemoteMac,
4578
5152
  normalizeAbsoluteMobileConfig,
4579
5153
  navigateAbsoluteMobilePage,
5154
+ materializeAbsoluteRemoteMacAgent,
4580
5155
  materializeAbsoluteMobileCompatibilityBundle,
4581
5156
  materializeAbsoluteMobileAssociationFiles,
4582
5157
  materializeAbsoluteCapacitorWebBundle,
4583
5158
  matchesAbsoluteMobileRoutePattern,
4584
5159
  loadAbsoluteNativeReleasePublisher,
4585
5160
  loadAbsoluteMobileMaterializedBundle,
5161
+ listAbsoluteRemoteMacProfiles,
4586
5162
  isAbsoluteIosNativeRootInput,
5163
+ installAbsoluteRemoteMacAgent,
5164
+ inspectAbsoluteRemoteMac,
4587
5165
  inspectAbsoluteMobileRouteMetadata,
4588
5166
  hashAbsoluteMobilePropsSchema,
4589
5167
  getCurrentAbsoluteMobileProducerContext,
5168
+ getAbsoluteRemoteMacProfile,
4590
5169
  fingerprintAbsoluteIosNativeProject,
4591
5170
  fingerprintAbsoluteIosDevProject,
4592
5171
  finalizeAbsoluteMobilePage,
4593
5172
  finalizeAbsoluteMobileCompatibilityBuild,
4594
5173
  fetchAbsoluteMobilePage,
5174
+ createAbsoluteRemoteIosDevProject,
4595
5175
  createAbsoluteMobileUpgradeResponse,
4596
5176
  createAbsoluteMobileRouteMetadataPlugin,
4597
5177
  createAbsoluteMobilePageRequest,
@@ -4612,10 +5192,14 @@ export {
4612
5192
  applyAbsoluteNativeDeepLinks,
4613
5193
  activateAbsoluteMobilePage,
4614
5194
  acceptsAbsoluteMobilePage,
5195
+ absoluteRemoteProjectSyncCommands,
5196
+ absoluteRemoteMacSshBase,
4615
5197
  MOBILE_PAGE_REQUEST_HEADERS,
4616
5198
  AbsoluteMobilePageProtocolError,
4617
5199
  APPLE_ASSOCIATION_PATH,
4618
5200
  ANDROID_ASSOCIATION_PATH,
5201
+ ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION,
5202
+ ABSOLUTE_REMOTE_MAC_EVENT_PREFIX,
4619
5203
  ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL,
4620
5204
  ABSOLUTE_MOBILE_ROUTE_DETAIL,
4621
5205
  ABSOLUTE_MOBILE_RETAINED_GENERATIONS,
@@ -4629,5 +5213,5 @@ export {
4629
5213
  ABSOLUTE_ANDROID_RELEASE_FORMAT
4630
5214
  };
4631
5215
 
4632
- //# debugId=048422942BB9C32864756E2164756E21
5216
+ //# debugId=5FE7B7589D1661C064756E2164756E21
4633
5217
  //# sourceMappingURL=index.js.map