@absolutejs/absolute 0.20.0-beta.2 → 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.
@@ -617,7 +617,7 @@ var verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes
617
617
  // src/mobile/androidRelease.ts
618
618
  import { createHash as createHash4 } from "crypto";
619
619
  import {
620
- access as access3,
620
+ access as access4,
621
621
  copyFile as copyFile2,
622
622
  mkdir as mkdir3,
623
623
  mkdtemp as mkdtemp2,
@@ -630,6 +630,7 @@ import {
630
630
  import { dirname as dirname2, isAbsolute as isAbsolute2, join as join4, relative as relative3, resolve as resolve3, sep as sep2 } from "path";
631
631
 
632
632
  // src/mobile/emulatorDoctor.ts
633
+ import { access } from "fs/promises";
633
634
  import { homedir } from "os";
634
635
  import { join as join2 } from "path";
635
636
 
@@ -647,6 +648,34 @@ var isWSLEnvironment = () => {
647
648
  };
648
649
 
649
650
  // src/mobile/emulatorDoctor.ts
651
+ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36";
652
+ var captureCommand = (command) => {
653
+ try {
654
+ const result = Bun.spawnSync(command, {
655
+ stderr: "ignore",
656
+ stdout: "pipe"
657
+ });
658
+ return {
659
+ exitCode: result.exitCode,
660
+ stdout: result.stdout.toString()
661
+ };
662
+ } catch {
663
+ return { exitCode: 1, stdout: "" };
664
+ }
665
+ };
666
+ var hasAvailableIosRuntime = (output) => {
667
+ try {
668
+ const parsed = JSON.parse(output);
669
+ if (typeof parsed !== "object" || parsed === null)
670
+ return false;
671
+ const runtimes = Reflect.get(parsed, "runtimes");
672
+ if (!Array.isArray(runtimes))
673
+ return false;
674
+ return runtimes.some((runtime) => typeof runtime === "object" && runtime !== null && Reflect.get(runtime, "isAvailable") === true && typeof Reflect.get(runtime, "identifier") === "string" && String(Reflect.get(runtime, "identifier")).includes("iOS"));
675
+ } catch {
676
+ return false;
677
+ }
678
+ };
650
679
  var windowsPathToWsl = (path) => {
651
680
  const match = /^([a-z]):[\\/](.*)$/i.exec(path.trim());
652
681
  if (!match)
@@ -678,6 +707,14 @@ var absoluteManagedAndroidSdkRoot = (host, env = process.env) => {
678
707
  }
679
708
  return join2(homedir(), ".absolutejs", "android-sdk");
680
709
  };
710
+ var pathExists = async (path) => {
711
+ try {
712
+ await access(path);
713
+ return true;
714
+ } catch {
715
+ return false;
716
+ }
717
+ };
681
718
  var detectAbsoluteMobileHost = (platform = process.platform, wsl = isWSLEnvironment()) => {
682
719
  if (platform === "darwin")
683
720
  return "macos";
@@ -687,10 +724,151 @@ var detectAbsoluteMobileHost = (platform = process.platform, wsl = isWSLEnvironm
687
724
  return "wsl";
688
725
  return "linux";
689
726
  };
727
+ var executableNames = (host, name) => {
728
+ if (host === "wsl")
729
+ return [`${name}.exe`, `${name}.bat`, name];
730
+ if (host === "windows")
731
+ return [name, `${name}.exe`, `${name}.bat`];
732
+ return [name];
733
+ };
734
+ var findExecutable = async (name, paths, options, host) => {
735
+ const existing = await Promise.all(paths.map(async (path) => await options.exists(path) ? path : undefined));
736
+ const configured = existing.find((path) => path !== undefined);
737
+ if (configured)
738
+ return configured;
739
+ for (const candidate of executableNames(host, name)) {
740
+ const path = options.which(candidate);
741
+ if (path)
742
+ return path;
743
+ }
744
+ return;
745
+ };
746
+ var toolCheck = (id, label, platform, path, remediation) => path ? {
747
+ id,
748
+ label,
749
+ path,
750
+ platform,
751
+ status: "pass"
752
+ } : {
753
+ id,
754
+ label,
755
+ platform,
756
+ remediation,
757
+ status: "fail"
758
+ };
759
+ var inspectAbsoluteMobileToolchain = async (input = {}) => {
760
+ const env = input.env ?? process.env;
761
+ const host = input.host ?? detectAbsoluteMobileHost();
762
+ const exists = input.exists ?? pathExists;
763
+ const which = input.which ?? ((command) => Bun.which(command));
764
+ const capture = input.capture ?? captureCommand;
765
+ const androidRoot = input.androidRoot === null ? undefined : input.androidRoot ?? env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host, env);
766
+ const windowsAndroidTools = host === "windows" || host === "wsl";
767
+ const android = (segments) => androidRoot ? join2(androidRoot, ...segments) : undefined;
768
+ const paths = (values) => values.filter((value) => Boolean(value));
769
+ const adb = await findExecutable("adb", paths([
770
+ android(["platform-tools", windowsAndroidTools ? "adb.exe" : "adb"])
771
+ ]), { exists, which }, host);
772
+ const emulator = await findExecutable("emulator", paths([
773
+ android([
774
+ "emulator",
775
+ windowsAndroidTools ? "emulator.exe" : "emulator"
776
+ ])
777
+ ]), { exists, which }, host);
778
+ const sdkmanager = await findExecutable("sdkmanager", paths([
779
+ android([
780
+ "cmdline-tools",
781
+ "latest",
782
+ "bin",
783
+ windowsAndroidTools ? "sdkmanager.bat" : "sdkmanager"
784
+ ])
785
+ ]), { exists, which }, host);
786
+ const avdmanager = await findExecutable("avdmanager", paths([
787
+ android([
788
+ "cmdline-tools",
789
+ "latest",
790
+ "bin",
791
+ windowsAndroidTools ? "avdmanager.bat" : "avdmanager"
792
+ ])
793
+ ]), { exists, which }, host);
794
+ const java = await findExecutable("java", [], { exists, which }, host);
795
+ const checks = [
796
+ {
797
+ id: "host",
798
+ label: `Development host: ${host}`,
799
+ platform: "host",
800
+ status: "pass"
801
+ },
802
+ toolCheck("android.adb", "Android Debug Bridge", "android", adb, "Install Android SDK Platform Tools or expose adb on PATH."),
803
+ toolCheck("android.emulator", "Android Emulator", "android", emulator, "Install the Android Emulator from Android Studio SDK Manager."),
804
+ toolCheck("android.sdkmanager", "Android SDK Manager", "android", sdkmanager, "Install Android SDK Command-line Tools (latest)."),
805
+ toolCheck("android.avdmanager", "Android Virtual Device Manager", "android", avdmanager, "Install Android SDK Command-line Tools (latest)."),
806
+ toolCheck("android.java", "Java runtime", "android", java, "Install the JDK required by the configured Android Gradle plugin.")
807
+ ];
808
+ if (emulator) {
809
+ const avds = capture([emulator, "-list-avds"]);
810
+ const hasManagedAvd = avds.exitCode === 0 && avds.stdout.split(/\r?\n/).includes(ABSOLUTE_ANDROID_AVD_NAME);
811
+ checks.push({
812
+ id: "android.avd",
813
+ label: `AbsoluteJS Android emulator (${ABSOLUTE_ANDROID_AVD_NAME})`,
814
+ platform: "android",
815
+ remediation: hasManagedAvd ? undefined : "Run absolute mobile doctor android --fix to provision the managed emulator.",
816
+ status: hasManagedAvd ? "pass" : "fail"
817
+ });
818
+ }
819
+ if (host === "wsl") {
820
+ checks.push({
821
+ id: "android.virtualization",
822
+ label: adb?.endsWith(".exe") ? "Windows-host Android bridge available to WSL" : "WSL requires a Windows-host emulator bridge or Linux KVM",
823
+ platform: "android",
824
+ remediation: adb?.endsWith(".exe") ? undefined : "Expose the Windows Android SDK adb.exe to WSL, or enable /dev/kvm for a Linux SDK.",
825
+ status: adb?.endsWith(".exe") ? "pass" : "warn"
826
+ });
827
+ } else if (host === "linux") {
828
+ const hasKvm = await exists("/dev/kvm");
829
+ checks.push({
830
+ id: "android.virtualization",
831
+ label: "Linux KVM acceleration",
832
+ platform: "android",
833
+ remediation: hasKvm ? undefined : "Enable KVM and grant the current user access to /dev/kvm.",
834
+ status: hasKvm ? "pass" : "warn"
835
+ });
836
+ }
837
+ if (host !== "macos") {
838
+ checks.push({
839
+ id: "ios.simulator",
840
+ label: "iOS Simulator requires macOS and Xcode",
841
+ platform: "ios",
842
+ status: "skip"
843
+ });
844
+ return checks;
845
+ }
846
+ const xcrun = await findExecutable("xcrun", [], { exists, which }, host);
847
+ const xcodebuild = await findExecutable("xcodebuild", [], { exists, which }, host);
848
+ checks.push(toolCheck("ios.xcrun", "Xcode command runner", "ios", xcrun, "Install Xcode and select it with xcode-select."), toolCheck("ios.xcodebuild", "Xcode build system", "ios", xcodebuild, "Install Xcode and select it with xcode-select."));
849
+ if (xcrun) {
850
+ const runtimes = capture([
851
+ xcrun,
852
+ "simctl",
853
+ "list",
854
+ "runtimes",
855
+ "--json"
856
+ ]);
857
+ const hasRuntime = runtimes.exitCode === 0 && hasAvailableIosRuntime(runtimes.stdout);
858
+ checks.push({
859
+ id: "ios.runtime",
860
+ label: "iOS Simulator runtime",
861
+ platform: "ios",
862
+ remediation: hasRuntime ? undefined : "Run absolute mobile doctor ios --fix to download an iOS Simulator runtime.",
863
+ status: hasRuntime ? "pass" : "fail"
864
+ });
865
+ }
866
+ return checks;
867
+ };
690
868
 
691
869
  // src/mobile/androidEmulatorController.ts
692
870
  import {
693
- access as access2,
871
+ access as access3,
694
872
  copyFile,
695
873
  lstat,
696
874
  mkdir as mkdir2,
@@ -714,7 +892,7 @@ import {
714
892
  } from "path";
715
893
 
716
894
  // src/mobile/capacitorProject.ts
717
- import { access, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
895
+ import { access as access2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
718
896
  import { relative, resolve } from "path";
719
897
  var CONFIG_FILE = "capacitor.config.ts";
720
898
  var portableRelative = (root, path) => relative(root, path).replaceAll("\\", "/");
@@ -736,7 +914,7 @@ export default config;
736
914
  `;
737
915
  var exists = async (path) => {
738
916
  try {
739
- await access(path);
917
+ await access2(path);
740
918
  return true;
741
919
  } catch {
742
920
  return false;
@@ -766,9 +944,9 @@ var HASH_RADIX = 16;
766
944
  var EXECUTABLE_MODE_MASK = 73;
767
945
  var NATIVE_PUBLIC_PATH_SEGMENTS = 5;
768
946
  var CAPACITOR_PROJECT_DIRECTORY_PATTERN = /project\(['"](:[^'"]+)['"]\)\.projectDir\s*=\s*new File\(['"]([^'"]+)['"]\)/gu;
769
- var pathExists = async (path) => {
947
+ var pathExists2 = async (path) => {
770
948
  try {
771
- await access2(path);
949
+ await access3(path);
772
950
  return true;
773
951
  } catch {
774
952
  return false;
@@ -795,7 +973,7 @@ var runCommand = async (command, options = {}) => {
795
973
  ]);
796
974
  return exitCode;
797
975
  };
798
- var captureCommand = (command, options = {}) => {
976
+ var captureCommand2 = (command, options = {}) => {
799
977
  try {
800
978
  const result = Bun.spawnSync(command, {
801
979
  cwd: options.cwd,
@@ -965,14 +1143,14 @@ var gradleArtifactPath = (nativeDirectory, task, windows = false) => {
965
1143
  };
966
1144
  var resolveGradleArtifactPath = async (nativeDirectory, task) => {
967
1145
  const primary = gradleArtifactPath(nativeDirectory, task);
968
- if (task !== "assembleRelease" || await pathExists(primary)) {
1146
+ if (task !== "assembleRelease" || await pathExists2(primary)) {
969
1147
  return primary;
970
1148
  }
971
1149
  return join3(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
972
1150
  };
973
1151
  var buildAbsoluteAndroidGradleArtifact = async (options) => {
974
1152
  const { project, task } = options;
975
- const capture = options.capture ?? captureCommand;
1153
+ const capture = options.capture ?? captureCommand2;
976
1154
  const run = options.run ?? runCommand;
977
1155
  const env = options.env ?? process.env;
978
1156
  const gradleArguments = options.gradleArguments ?? [];
@@ -1015,9 +1193,9 @@ var requireManifest = (value) => {
1015
1193
  runtime: value.runtime
1016
1194
  };
1017
1195
  };
1018
- var pathExists2 = async (path) => {
1196
+ var pathExists3 = async (path) => {
1019
1197
  try {
1020
- await access3(path);
1198
+ await access4(path);
1021
1199
  return true;
1022
1200
  } catch {
1023
1201
  return false;
@@ -1072,7 +1250,7 @@ var installRelease = async (artifactPath, metadata, outputRoot) => {
1072
1250
  const releaseRoot = join4(outputRoot, metadata.releaseId);
1073
1251
  const artifactName = "app-release.aab";
1074
1252
  const destination = join4(releaseRoot, artifactName);
1075
- if (await pathExists2(releaseRoot)) {
1253
+ if (await pathExists3(releaseRoot)) {
1076
1254
  const existing = requireManifestIdentity(JSON.parse(await readFile4(join4(releaseRoot, "release.json"), "utf8")), metadata);
1077
1255
  const [installedBytes, installedSha256] = await Promise.all([
1078
1256
  stat(destination).then(({ size }) => size),
@@ -1148,7 +1326,7 @@ var buildAbsoluteAndroidRelease = async (options) => {
1148
1326
  run: options.run,
1149
1327
  task: "bundleRelease"
1150
1328
  });
1151
- if (!await pathExists2(artifactPath)) {
1329
+ if (!await pathExists3(artifactPath)) {
1152
1330
  throw new TypeError(`Android Gradle did not produce the expected App Bundle: ${artifactPath}`);
1153
1331
  }
1154
1332
  const capture = options.capture ?? defaultCapture;
@@ -1183,7 +1361,7 @@ var buildAbsoluteAndroidRelease = async (options) => {
1183
1361
  // src/mobile/iosRelease.ts
1184
1362
  import { createHash as createHash5 } from "crypto";
1185
1363
  import {
1186
- access as access4,
1364
+ access as access5,
1187
1365
  copyFile as copyFile3,
1188
1366
  mkdir as mkdir4,
1189
1367
  mkdtemp as mkdtemp3,
@@ -1207,9 +1385,9 @@ var requireManifest2 = (value) => {
1207
1385
  runtime: value.runtime
1208
1386
  };
1209
1387
  };
1210
- var pathExists3 = async (path) => {
1388
+ var pathExists4 = async (path) => {
1211
1389
  try {
1212
- await access4(path);
1390
+ await access5(path);
1213
1391
  return true;
1214
1392
  } catch {
1215
1393
  return false;
@@ -1290,7 +1468,7 @@ var safeOutputDirectory2 = (projectRoot, requested) => {
1290
1468
  };
1291
1469
  var sha256File2 = async (path) => createHash5("sha256").update(await readFile5(path)).digest("hex");
1292
1470
  var findByExtension = async (root, extension) => {
1293
- if (!await pathExists3(root))
1471
+ if (!await pathExists4(root))
1294
1472
  return;
1295
1473
  const entries = await readdir3(root, { withFileTypes: true });
1296
1474
  const matches = await Promise.all(entries.map(async (entry) => {
@@ -1322,7 +1500,7 @@ var requireBuildNumber = (value) => {
1322
1500
  var installRelease2 = async (artifactPath, metadata, outputRoot) => {
1323
1501
  const releaseRoot = join5(outputRoot, metadata.releaseId);
1324
1502
  const destination = join5(releaseRoot, "App.ipa");
1325
- if (await pathExists3(releaseRoot)) {
1503
+ if (await pathExists4(releaseRoot)) {
1326
1504
  const value = JSON.parse(await readFile5(join5(releaseRoot, "release.json"), "utf8"));
1327
1505
  if (!isRecord2(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
1328
1506
  throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
@@ -1461,28 +1639,1490 @@ var buildAbsoluteIosRelease = async (options) => {
1461
1639
  });
1462
1640
  }
1463
1641
  };
1464
- // src/mobile/associationFiles.ts
1642
+ // src/mobile/iosConformance.ts
1643
+ import { readFile as readFile6, stat as stat3 } from "fs/promises";
1644
+ var HMR_LINE = new RegExp(String.raw`\[hmr:ios\]\s+([^\n]*?)\s+(applied in|falling back to reload after|failed after)\s+(\d+)ms(?:; server\s+(\d+)ms, client\s+(\d+)ms)?`, "u");
1645
+ var parseAbsoluteIosHmrLog = (line) => {
1646
+ const match = HMR_LINE.exec(line);
1647
+ if (!match)
1648
+ return null;
1649
+ const [, , action, durationValue, serverValue, clientValue] = match;
1650
+ let outcome = "reloaded";
1651
+ if (action === "applied in")
1652
+ outcome = "applied";
1653
+ if (action === "failed after")
1654
+ outcome = "failed";
1655
+ const serverMs = serverValue === undefined ? undefined : Number(serverValue);
1656
+ const clientMs = clientValue === undefined ? undefined : Number(clientValue);
1657
+ return {
1658
+ ...clientMs === undefined ? {} : { clientMs },
1659
+ duration: Number(durationValue),
1660
+ line: match[0],
1661
+ outcome,
1662
+ ...serverMs === undefined ? {} : { serverMs }
1663
+ };
1664
+ };
1665
+ var findHmrApply = (lines) => {
1666
+ const apply = lines.map((line) => parseAbsoluteIosHmrLog(line)).find((candidate) => candidate !== null);
1667
+ if (apply?.outcome === "failed")
1668
+ throw new Error(`iOS HMR client reported a failed apply: ${apply.line}`);
1669
+ return apply;
1670
+ };
1671
+ var waitForAbsoluteIosHmrLog = async (options) => {
1672
+ const sleep = options.sleep ?? Bun.sleep;
1673
+ const timeoutMs = options.timeoutMs ?? 30000;
1674
+ const deadline = Date.now() + timeoutMs;
1675
+ let offset = options.startOffset ?? await stat3(options.logPath).then(({ size }) => size).catch(() => 0);
1676
+ let buffered = "";
1677
+ const poll = async () => {
1678
+ if (Date.now() > deadline)
1679
+ throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
1680
+ options.signal?.throwIfAborted();
1681
+ const contents = await readFile6(options.logPath).catch(() => Buffer.alloc(0));
1682
+ if (contents.byteLength < offset) {
1683
+ offset = 0;
1684
+ buffered = "";
1685
+ }
1686
+ if (contents.byteLength > offset) {
1687
+ buffered += contents.subarray(offset).toString("utf8");
1688
+ offset = contents.byteLength;
1689
+ const lines = buffered.split(/\r?\n/u);
1690
+ buffered = lines.pop() ?? "";
1691
+ const apply = findHmrApply(lines);
1692
+ if (apply)
1693
+ return apply;
1694
+ }
1695
+ await sleep(100);
1696
+ return poll();
1697
+ };
1698
+ return poll();
1699
+ };
1700
+ // src/mobile/iosSimulatorController.ts
1701
+ import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
1465
1702
  import {
1466
- access as access5,
1703
+ access as access6,
1704
+ copyFile as copyFile4,
1467
1705
  mkdir as mkdir5,
1468
- readFile as readFile6,
1706
+ readFile as readFile7,
1469
1707
  rename as rename6,
1470
1708
  rm as rm5,
1471
1709
  writeFile as writeFile6
1472
1710
  } from "fs/promises";
1473
- import { resolve as resolve6 } from "path";
1711
+ import { dirname as dirname4, isAbsolute as isAbsolute4, join as join6, relative as relative5, resolve as resolve5, sep as sep4 } from "path";
1712
+ init_getDurationString();
1713
+ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone";
1714
+ var BOOT_TIMEOUT_MS = 180000;
1715
+ var BOOT_POLL_MS = 1000;
1716
+ var DEV_JOURNAL_FORMAT = 1;
1717
+ var NATIVE_CACHE_FORMAT = 1;
1718
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1719
+ var pathExists5 = async (path) => {
1720
+ try {
1721
+ await access6(path);
1722
+ return true;
1723
+ } catch {
1724
+ return false;
1725
+ }
1726
+ };
1727
+ var throwIfAborted2 = (signal) => signal?.throwIfAborted();
1728
+ var defaultCapture3 = (command, options = {}) => {
1729
+ try {
1730
+ const result = Bun.spawnSync(command, {
1731
+ cwd: options.cwd,
1732
+ env: options.env,
1733
+ stderr: "pipe",
1734
+ stdin: "ignore",
1735
+ stdout: "pipe"
1736
+ });
1737
+ return {
1738
+ exitCode: result.exitCode,
1739
+ stderr: result.stderr.toString(),
1740
+ stdout: result.stdout.toString()
1741
+ };
1742
+ } catch (error) {
1743
+ return {
1744
+ exitCode: 1,
1745
+ stderr: error instanceof Error ? error.message : String(error),
1746
+ stdout: ""
1747
+ };
1748
+ }
1749
+ };
1750
+ var defaultRun2 = async (command, options = {}) => {
1751
+ const process2 = Bun.spawn(command, {
1752
+ cwd: options.cwd,
1753
+ env: options.env,
1754
+ signal: options.signal,
1755
+ stderr: "inherit",
1756
+ stdin: "inherit",
1757
+ stdout: "inherit"
1758
+ });
1759
+ return process2.exited;
1760
+ };
1761
+ var defaultSpawn = (command, options = {}) => {
1762
+ Bun.spawn(command, {
1763
+ cwd: options.cwd,
1764
+ env: options.env,
1765
+ signal: options.signal,
1766
+ stderr: "ignore",
1767
+ stdin: "ignore",
1768
+ stdout: "ignore"
1769
+ });
1770
+ };
1771
+ var consumeLines = async (stream, onLine) => {
1772
+ const reader = stream.getReader();
1773
+ const decoder = new TextDecoder;
1774
+ let buffered = "";
1775
+ const pump = async () => {
1776
+ const { done, value } = await reader.read();
1777
+ if (done)
1778
+ return;
1779
+ buffered += decoder.decode(value, { stream: true });
1780
+ const lines = buffered.split(/\r?\n/u);
1781
+ buffered = lines.pop() ?? "";
1782
+ lines.forEach(onLine);
1783
+ await pump();
1784
+ };
1785
+ try {
1786
+ await pump();
1787
+ buffered += decoder.decode();
1788
+ if (buffered)
1789
+ onLine(buffered);
1790
+ } finally {
1791
+ reader.releaseLock();
1792
+ }
1793
+ };
1794
+ var defaultStartNativeLogs = (command, options, onLine) => {
1795
+ const process2 = Bun.spawn(command, {
1796
+ cwd: options.cwd,
1797
+ env: options.env,
1798
+ signal: options.signal,
1799
+ stderr: "pipe",
1800
+ stdin: "ignore",
1801
+ stdout: "pipe"
1802
+ });
1803
+ consumeLines(process2.stdout, onLine);
1804
+ consumeLines(process2.stderr, onLine);
1805
+ return {
1806
+ close: async () => {
1807
+ try {
1808
+ process2.kill();
1809
+ } catch {}
1810
+ await process2.exited.catch(() => {
1811
+ return;
1812
+ });
1813
+ }
1814
+ };
1815
+ };
1816
+ var requireSuccess2 = async (command, label, run, options) => {
1817
+ const exitCode = await run(command, options);
1818
+ if (exitCode !== 0)
1819
+ throw new Error(`${label} failed with status ${exitCode}.`);
1820
+ };
1821
+ var requireCapturedSuccess = (result, label) => {
1822
+ if (result.exitCode !== 0) {
1823
+ throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
1824
+ }
1825
+ return result.stdout.trim();
1826
+ };
1827
+ var parseJson = (source, label) => {
1828
+ try {
1829
+ const parsed = JSON.parse(source);
1830
+ if (isRecord3(parsed))
1831
+ return parsed;
1832
+ } catch {}
1833
+ throw new Error(`Invalid ${label} JSON from simctl.`);
1834
+ };
1835
+ var parseIosDeviceTypes = (source) => {
1836
+ const parsed = parseJson(source, "device type");
1837
+ const types = parsed.devicetypes;
1838
+ if (!Array.isArray(types))
1839
+ return [];
1840
+ return types.flatMap((type) => {
1841
+ if (!isRecord3(type))
1842
+ return [];
1843
+ const { identifier } = type;
1844
+ const { name } = type;
1845
+ return typeof identifier === "string" && typeof name === "string" ? [{ identifier, name }] : [];
1846
+ });
1847
+ };
1848
+ var parseIosRuntimes = (source) => {
1849
+ const parsed = parseJson(source, "runtime");
1850
+ const { runtimes } = parsed;
1851
+ if (!Array.isArray(runtimes))
1852
+ return [];
1853
+ return runtimes.flatMap((runtime) => {
1854
+ if (!isRecord3(runtime))
1855
+ return [];
1856
+ const { identifier } = runtime;
1857
+ const { name } = runtime;
1858
+ const { version } = runtime;
1859
+ if (typeof identifier !== "string" || typeof name !== "string" || typeof version !== "string")
1860
+ return [];
1861
+ return [
1862
+ {
1863
+ identifier,
1864
+ isAvailable: runtime.isAvailable !== false,
1865
+ name,
1866
+ version
1867
+ }
1868
+ ];
1869
+ });
1870
+ };
1871
+ var parseIosSimulators = (source) => {
1872
+ const parsed = parseJson(source, "device");
1873
+ const { devices } = parsed;
1874
+ if (!isRecord3(devices))
1875
+ return [];
1876
+ return Object.entries(devices).flatMap(([runtime, values]) => {
1877
+ if (!Array.isArray(values))
1878
+ return [];
1879
+ return values.flatMap((device) => {
1880
+ if (!isRecord3(device))
1881
+ return [];
1882
+ const { name } = device;
1883
+ const { state } = device;
1884
+ const { udid } = device;
1885
+ if (typeof name !== "string" || typeof state !== "string" || typeof udid !== "string")
1886
+ return [];
1887
+ return [
1888
+ {
1889
+ isAvailable: device.isAvailable !== false,
1890
+ name,
1891
+ runtime,
1892
+ state,
1893
+ udid
1894
+ }
1895
+ ];
1896
+ });
1897
+ });
1898
+ };
1899
+ var versionParts = (version) => version.split(".").map((part) => Number(part));
1900
+ var compareVersions = (left, right) => {
1901
+ const leftParts = versionParts(left);
1902
+ const rightParts = versionParts(right);
1903
+ const length = Math.max(leftParts.length, rightParts.length);
1904
+ for (let index = 0;index < length; index++) {
1905
+ const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
1906
+ if (difference !== 0)
1907
+ return difference;
1908
+ }
1909
+ return 0;
1910
+ };
1911
+ var latestIosRuntime = (runtimes) => runtimes.filter((runtime) => runtime.isAvailable && runtime.identifier.includes("SimRuntime.iOS-")).sort((left, right) => compareVersions(right.version, left.version))[0];
1912
+ var iphoneGeneration = (name) => Number(/iPhone\s+(\d+)/u.exec(name)?.[1] ?? 0);
1913
+ var preferredIphoneType = (types) => types.filter((type) => type.name.startsWith("iPhone")).sort((left, right) => {
1914
+ const generation = iphoneGeneration(right.name) - iphoneGeneration(left.name);
1915
+ if (generation !== 0)
1916
+ return generation;
1917
+ const rightPro = right.name.includes("Pro") ? 1 : 0;
1918
+ const leftPro = left.name.includes("Pro") ? 1 : 0;
1919
+ return rightPro - leftPro;
1920
+ })[0];
1921
+ var journalPaths = (projectRoot) => {
1922
+ const root = join6(projectRoot, ".absolutejs", "mobile", "ios-dev-session");
1923
+ return {
1924
+ configBackup: join6(root, "capacitor-config.backup"),
1925
+ infoBackup: join6(root, "Info.plist.backup"),
1926
+ journal: join6(root, "journal.json"),
1927
+ root
1928
+ };
1929
+ };
1930
+ var nativeCachePath = (projectRoot) => join6(projectRoot, ".absolutejs", "mobile", "ios-native-cache.json");
1931
+ var isInside = (root, path) => {
1932
+ const value = relative5(resolve5(root), resolve5(path));
1933
+ return value === "" || !value.startsWith(`..${sep4}`) && value !== ".." && !isAbsolute4(value);
1934
+ };
1935
+ var parseJournal = (value) => {
1936
+ if (!isRecord3(value) || value.format !== DEV_JOURNAL_FORMAT)
1937
+ return null;
1938
+ const { configBackupPath } = value;
1939
+ const { infoBackupPath } = value;
1940
+ const { infoPath } = value;
1941
+ const { nativeConfigPath } = value;
1942
+ if (typeof configBackupPath !== "string" || typeof infoBackupPath !== "string" || typeof infoPath !== "string" || typeof nativeConfigPath !== "string")
1943
+ return null;
1944
+ return {
1945
+ configBackupPath,
1946
+ format: DEV_JOURNAL_FORMAT,
1947
+ infoBackupPath,
1948
+ infoPath,
1949
+ nativeConfigPath
1950
+ };
1951
+ };
1952
+ var repairAbsoluteIosDevSession = async (projectRoot) => {
1953
+ const paths = journalPaths(projectRoot);
1954
+ if (!await pathExists5(paths.journal)) {
1955
+ await rm5(paths.root, { force: true, recursive: true });
1956
+ return false;
1957
+ }
1958
+ const journal = await readFile7(paths.journal, "utf8").then((source) => parseJournal(JSON.parse(source))).catch(() => null);
1959
+ if (!journal || !isInside(projectRoot, journal.nativeConfigPath) || !isInside(projectRoot, journal.infoPath) || !isInside(paths.root, journal.configBackupPath) || !isInside(paths.root, journal.infoBackupPath)) {
1960
+ throw new Error(`Refusing unsafe or invalid iOS dev journal at ${paths.journal}.`);
1961
+ }
1962
+ if (await pathExists5(journal.configBackupPath))
1963
+ await copyFile4(journal.configBackupPath, journal.nativeConfigPath);
1964
+ if (await pathExists5(journal.infoBackupPath))
1965
+ await copyFile4(journal.infoBackupPath, journal.infoPath);
1966
+ await rm5(paths.root, { force: true, recursive: true });
1967
+ return true;
1968
+ };
1969
+ var iosDevelopmentInfoPlist = (source, cleartext) => {
1970
+ if (!cleartext)
1971
+ return source;
1972
+ const arbitraryLoads = /(<key>NSAllowsArbitraryLoads<\/key>\s*)<false\s*\/>/u;
1973
+ if (arbitraryLoads.test(source))
1974
+ return source.replace(arbitraryLoads, "$1<true/>");
1975
+ if (/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(source))
1976
+ return source;
1977
+ const transport = /(<key>NSAppTransportSecurity<\/key>\s*<dict>)/u;
1978
+ if (transport.test(source))
1979
+ return source.replace(transport, `$1
1980
+ <key>NSAllowsArbitraryLoads</key>
1981
+ <true/>`);
1982
+ return source.replace(/<dict>/u, `<dict>
1983
+ <key>NSAppTransportSecurity</key>
1984
+ <dict>
1985
+ <key>NSAllowsArbitraryLoads</key>
1986
+ <true/>
1987
+ </dict>`);
1988
+ };
1989
+ var writeDevProjection = async (project, port, https) => {
1990
+ const paths = journalPaths(project.projectRoot);
1991
+ await repairAbsoluteIosDevSession(project.projectRoot);
1992
+ const nativeConfigPath = join6(project.nativeDirectory, "App", "App", "capacitor.config.json");
1993
+ const infoPath = join6(project.nativeDirectory, "App", "App", "Info.plist");
1994
+ const [configSource, infoSource] = await Promise.all([
1995
+ readFile7(nativeConfigPath, "utf8"),
1996
+ readFile7(infoPath, "utf8")
1997
+ ]);
1998
+ const parsed = JSON.parse(configSource);
1999
+ if (!isRecord3(parsed))
2000
+ throw new Error(`Invalid Capacitor native config at ${nativeConfigPath}.`);
2001
+ await mkdir5(paths.root, { recursive: true });
2002
+ await Promise.all([
2003
+ writeFile6(paths.configBackup, configSource, { flag: "wx" }),
2004
+ writeFile6(paths.infoBackup, infoSource, { flag: "wx" })
2005
+ ]);
2006
+ const journal = {
2007
+ configBackupPath: paths.configBackup,
2008
+ format: DEV_JOURNAL_FORMAT,
2009
+ infoBackupPath: paths.infoBackup,
2010
+ infoPath,
2011
+ nativeConfigPath
2012
+ };
2013
+ await writeFile6(paths.journal, `${JSON.stringify(journal, null, "\t")}
2014
+ `, {
2015
+ flag: "wx"
2016
+ });
2017
+ const developmentUrl = new URL(`${https ? "https" : "http"}://localhost:${port}${project.config.entry}`);
2018
+ developmentUrl.searchParams.set("__absolute_target", "capacitor-ios");
2019
+ const existingServer = parsed.server;
2020
+ parsed.server = {
2021
+ ...isRecord3(existingServer) ? existingServer : {},
2022
+ cleartext: !https,
2023
+ url: developmentUrl.href
2024
+ };
2025
+ await Promise.all([
2026
+ writeFile6(nativeConfigPath, `${JSON.stringify(parsed, null, "\t")}
2027
+ `),
2028
+ writeFile6(infoPath, iosDevelopmentInfoPlist(infoSource, !https))
2029
+ ]);
2030
+ };
2031
+ var parseNativeCache = (value) => {
2032
+ if (!isRecord3(value))
2033
+ return null;
2034
+ const { appId, fingerprint, format, installations } = value;
2035
+ if (format !== NATIVE_CACHE_FORMAT || typeof appId !== "string" || typeof fingerprint !== "string" || !isRecord3(installations) || !Object.values(installations).every((identity) => typeof identity === "string"))
2036
+ return null;
2037
+ return {
2038
+ appId,
2039
+ fingerprint,
2040
+ format,
2041
+ installations: Object.fromEntries(Object.entries(installations).map(([udid, identity]) => [
2042
+ udid,
2043
+ String(identity)
2044
+ ]))
2045
+ };
2046
+ };
2047
+ var readNativeCache = (projectRoot) => readFile7(nativeCachePath(projectRoot), "utf8").then((source) => parseNativeCache(JSON.parse(source))).catch(() => null);
2048
+ var writeNativeCache = async (projectRoot, cache) => {
2049
+ const destination = nativeCachePath(projectRoot);
2050
+ const temporary = `${destination}.${process.pid}.${randomUUID2()}.tmp`;
2051
+ await mkdir5(dirname4(destination), { recursive: true });
2052
+ try {
2053
+ await writeFile6(temporary, `${JSON.stringify(cache, null, "\t")}
2054
+ `, {
2055
+ flag: "wx"
2056
+ });
2057
+ await rename6(temporary, destination);
2058
+ } finally {
2059
+ await rm5(temporary, { force: true }).catch(() => {
2060
+ return;
2061
+ });
2062
+ }
2063
+ };
2064
+ var fingerprintAbsoluteIosDevProject = async (project) => fingerprintAbsoluteIosNativeProject(project.nativeDirectory);
2065
+ var simulatorInventory = (xcrun, capture) => {
2066
+ const result = capture([
2067
+ xcrun,
2068
+ "simctl",
2069
+ "list",
2070
+ "devices",
2071
+ "available",
2072
+ "-j"
2073
+ ]);
2074
+ return parseIosSimulators(requireCapturedSuccess(result, "iOS simulator discovery"));
2075
+ };
2076
+ var ensureManagedSimulator = async (project, capture) => {
2077
+ const runtimes = parseIosRuntimes(requireCapturedSuccess(capture([project.xcrun, "simctl", "list", "runtimes", "-j"]), "iOS runtime discovery"));
2078
+ const runtime = latestIosRuntime(runtimes);
2079
+ if (!runtime)
2080
+ throw new Error("No available iOS Simulator runtime. Run absolute mobile doctor ios --fix.");
2081
+ const [existing] = simulatorInventory(project.xcrun, capture).filter((device) => device.isAvailable && device.name === ABSOLUTE_IOS_SIMULATOR_NAME && device.runtime === runtime.identifier).sort((left, right) => Number(right.state === "Booted") - Number(left.state === "Booted"));
2082
+ if (existing)
2083
+ return { created: false, device: existing };
2084
+ const types = parseIosDeviceTypes(requireCapturedSuccess(capture([project.xcrun, "simctl", "list", "devicetypes", "-j"]), "iOS device-type discovery"));
2085
+ const type = preferredIphoneType(types);
2086
+ if (!type)
2087
+ throw new Error("Xcode did not report an iPhone simulator type.");
2088
+ const [udid] = requireCapturedSuccess(capture([
2089
+ project.xcrun,
2090
+ "simctl",
2091
+ "create",
2092
+ ABSOLUTE_IOS_SIMULATOR_NAME,
2093
+ type.identifier,
2094
+ runtime.identifier
2095
+ ]), "iOS simulator creation").split(/\s/u);
2096
+ if (!udid)
2097
+ throw new Error("simctl did not return the created simulator UDID.");
2098
+ return {
2099
+ created: true,
2100
+ device: {
2101
+ isAvailable: true,
2102
+ name: ABSOLUTE_IOS_SIMULATOR_NAME,
2103
+ runtime: runtime.identifier,
2104
+ state: "Shutdown",
2105
+ udid
2106
+ }
2107
+ };
2108
+ };
2109
+ var waitForBootedSimulator = async (project, udid, capture, sleep, signal) => {
2110
+ const deadline = Date.now() + BOOT_TIMEOUT_MS;
2111
+ const poll = async () => {
2112
+ throwIfAborted2(signal);
2113
+ const device = simulatorInventory(project.xcrun, capture).find((candidate) => candidate.udid === udid);
2114
+ if (device?.state === "Booted")
2115
+ return;
2116
+ if (Date.now() > deadline)
2117
+ throw new Error(`iOS simulator ${udid} did not finish booting within ${BOOT_TIMEOUT_MS / 1000}s.`);
2118
+ await sleep(BOOT_POLL_MS);
2119
+ await poll();
2120
+ };
2121
+ return poll();
2122
+ };
2123
+ var bootSimulator = (project, device, capture) => {
2124
+ if (device.state === "Booted")
2125
+ return;
2126
+ requireCapturedSuccess(capture([project.xcrun, "simctl", "boot", device.udid]), "iOS simulator boot");
2127
+ };
2128
+ var installedAppIdentity = (project, udid, capture) => {
2129
+ const result = capture([
2130
+ project.xcrun,
2131
+ "simctl",
2132
+ "get_app_container",
2133
+ udid,
2134
+ project.config.appId,
2135
+ "app"
2136
+ ]);
2137
+ return result.exitCode === 0 && result.stdout.trim() ? result.stdout.trim() : undefined;
2138
+ };
2139
+ var buildIosDebugApp = async (project, udid, fingerprint, run, signal) => {
2140
+ const derivedDataPath = join6(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash6("sha256").update(project.config.appId).digest("hex").slice(0, 16));
2141
+ await mkdir5(derivedDataPath, { recursive: true });
2142
+ await requireSuccess2([
2143
+ project.xcodebuild,
2144
+ "-workspace",
2145
+ join6(project.nativeDirectory, "App", "App.xcworkspace"),
2146
+ "-scheme",
2147
+ "App",
2148
+ "-configuration",
2149
+ "Debug",
2150
+ "-destination",
2151
+ `platform=iOS Simulator,id=${udid}`,
2152
+ "-derivedDataPath",
2153
+ derivedDataPath,
2154
+ "build"
2155
+ ], "iOS simulator build", run, { cwd: project.nativeDirectory, signal });
2156
+ const appPath = join6(derivedDataPath, "Build", "Products", "Debug-iphonesimulator", "App.app");
2157
+ if (!await pathExists5(appPath))
2158
+ throw new Error(`Xcode did not produce the simulator app at ${appPath}.`);
2159
+ return appPath;
2160
+ };
2161
+ var ensureIosDebugApp = async (options) => {
2162
+ const installed = installedAppIdentity(options.project, options.udid, options.capture);
2163
+ const cacheHit = options.cache?.appId === options.project.config.appId && options.cache.fingerprint === options.fingerprint && installed !== undefined && options.cache.installations[options.udid] === installed;
2164
+ if (cacheHit) {
2165
+ options.log(`iOS native app is unchanged on ${options.udid}; skipped Xcode build and install.`);
2166
+ return true;
2167
+ }
2168
+ options.log("iOS native inputs changed or the installed app is stale; rebuilding.");
2169
+ options.transition("building");
2170
+ const appPath = await buildIosDebugApp(options.project, options.udid, options.fingerprint, options.run, options.signal);
2171
+ throwIfAborted2(options.signal);
2172
+ options.transition("installing");
2173
+ await requireSuccess2([options.project.xcrun, "simctl", "install", options.udid, appPath], "iOS simulator app installation", options.run, { signal: options.signal });
2174
+ const updated = installedAppIdentity(options.project, options.udid, options.capture);
2175
+ if (updated) {
2176
+ await writeNativeCache(options.project.projectRoot, {
2177
+ appId: options.project.config.appId,
2178
+ fingerprint: options.fingerprint,
2179
+ format: NATIVE_CACHE_FORMAT,
2180
+ installations: { [options.udid]: updated }
2181
+ }).catch((error) => options.log(`iOS native cache could not be saved: ${error instanceof Error ? error.message : String(error)}`));
2182
+ }
2183
+ return false;
2184
+ };
2185
+ var SECRET_VALUE = /((?:authorization|cookie|password|secret|token|oauth[_-]?code)\s*[:=]\s*)([^\s,;]+)/giu;
2186
+ var BEARER_VALUE = new RegExp(String.raw`\bBearer\s+[A-Za-z0-9._~+/-]+=*`, "giu");
2187
+ var JWT_VALUE = /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu;
2188
+ var IOS_LOG_PATTERN = new RegExp(String.raw`\s(Debug|Info|Notice|Error|Fault)\s+.*?\[[^\]]+\]\s+\[([^\]]+)\]\s*(.*)$`, "iu");
2189
+ var parseAbsoluteIosLogLine = (line) => {
2190
+ const sanitized = redactAbsoluteIosLog(line).trim();
2191
+ if (!sanitized)
2192
+ return null;
2193
+ const match = IOS_LOG_PATTERN.exec(sanitized);
2194
+ const candidate = match?.[1]?.toLowerCase();
2195
+ const level = candidate === "debug" || candidate === "error" || candidate === "fault" || candidate === "notice" ? candidate : "info";
2196
+ return {
2197
+ level,
2198
+ message: match?.[3]?.trim() || sanitized,
2199
+ tag: match?.[2]?.trim() || "App"
2200
+ };
2201
+ };
2202
+ var redactAbsoluteIosLog = (value) => value.replaceAll(BEARER_VALUE, "Bearer [REDACTED]").replaceAll(JWT_VALUE, "[REDACTED_JWT]").replaceAll(SECRET_VALUE, "$1[REDACTED]").replaceAll(/\p{C}/gu, "");
2203
+ var attachNativeLogs = (project, udid, options) => {
2204
+ if (!options.nativeLog)
2205
+ return null;
2206
+ const start = options.startNativeLogs ?? defaultStartNativeLogs;
2207
+ return start([
2208
+ project.xcrun,
2209
+ "simctl",
2210
+ "spawn",
2211
+ udid,
2212
+ "log",
2213
+ "stream",
2214
+ "--style",
2215
+ "compact",
2216
+ "--level",
2217
+ "debug",
2218
+ "--predicate",
2219
+ 'process == "App"'
2220
+ ], { signal: options.signal }, (line) => {
2221
+ const entry = parseAbsoluteIosLogLine(line);
2222
+ if (entry)
2223
+ options.nativeLog?.(entry);
2224
+ });
2225
+ };
2226
+ var IOS_TIMING_PHASES = [
2227
+ ["syncing", "Capacitor sync"],
2228
+ ["configuring", "dev config"],
2229
+ ["fingerprinting", "fingerprint"],
2230
+ ["booting", "simulator"],
2231
+ ["connecting", "device ready"],
2232
+ ["checking-native", "app check"],
2233
+ ["building", "Xcode"],
2234
+ ["installing", "install"],
2235
+ ["launching", "launch"],
2236
+ ["streaming-logs", "logs"]
2237
+ ];
2238
+ var timingSummary = (timings) => IOS_TIMING_PHASES.map(([phase, label]) => {
2239
+ const duration = timings[phase];
2240
+ return duration === undefined ? null : `${label} ${getDurationString(duration)}`;
2241
+ }).filter((value) => value !== null).join(", ");
2242
+ var prepareAbsoluteIosDevProject = async (config, options) => {
2243
+ if (detectAbsoluteMobileHost() !== "macos")
2244
+ throw new Error("iOS simulation requires macOS and Xcode.");
2245
+ const projectRoot = resolve5(options.projectRoot);
2246
+ const checks = await inspectAbsoluteMobileToolchain({ host: "macos" });
2247
+ const failed = checks.filter((check) => check.platform === "ios" && (check.status === "fail" || check.status === "warn"));
2248
+ if (failed.length > 0)
2249
+ throw new Error(`iOS simulation is not ready: ${failed.map(({ label }) => label).join(", ")}.`);
2250
+ const xcrun = checks.find((check) => check.id === "ios.xcrun")?.path;
2251
+ const xcodebuild = checks.find((check) => check.id === "ios.xcodebuild")?.path;
2252
+ if (!xcrun || !xcodebuild)
2253
+ throw new Error("Xcode tools disappeared after readiness checks.");
2254
+ const cap = join6(projectRoot, "node_modules", ".bin", "cap");
2255
+ if (!await pathExists5(cap))
2256
+ throw new Error("Capacitor CLI is not installed. Run absolute mobile init first.");
2257
+ await writeAbsoluteCapacitorConfig(config, { projectRoot });
2258
+ await mkdir5(config.bundleDirectory, { recursive: true });
2259
+ const placeholder = join6(config.bundleDirectory, "index.html");
2260
+ if (!await pathExists5(placeholder))
2261
+ await writeFile6(placeholder, `<!doctype html><title>AbsoluteJS mobile development</title>
2262
+ `);
2263
+ const nativeDirectory = join6(config.nativeProjectDirectory, "ios");
2264
+ if (!await pathExists5(nativeDirectory)) {
2265
+ if (!options.createNativeProject)
2266
+ throw new Error("iOS native project has not been created.");
2267
+ const run = options.run ?? defaultRun2;
2268
+ if (await run([cap, "add", "ios"], { cwd: projectRoot }) !== 0)
2269
+ throw new Error("Capacitor iOS project creation failed.");
2270
+ }
2271
+ return {
2272
+ cap,
2273
+ config,
2274
+ nativeDirectory,
2275
+ projectRoot,
2276
+ xcodebuild,
2277
+ xcrun
2278
+ };
2279
+ };
2280
+ var startAbsoluteIosDevSession = async (options) => {
2281
+ const { project } = options;
2282
+ const capture = options.capture ?? defaultCapture3;
2283
+ const run = options.run ?? defaultRun2;
2284
+ const sleep = options.sleep ?? Bun.sleep;
2285
+ const spawn = options.spawn ?? defaultSpawn;
2286
+ const log = options.log ?? console.log;
2287
+ const startedAt = performance.now();
2288
+ let phaseStartedAt = performance.now();
2289
+ const timings = {};
2290
+ let state = "syncing";
2291
+ const transition = (next) => {
2292
+ if (next === state) {
2293
+ options.onStateChange?.(next);
2294
+ return;
2295
+ }
2296
+ const now = performance.now();
2297
+ const durationMs = now - phaseStartedAt;
2298
+ timings[state] = (timings[state] ?? 0) + durationMs;
2299
+ options.onPhaseTiming?.({
2300
+ durationMs,
2301
+ phase: state,
2302
+ totalMs: now - startedAt
2303
+ });
2304
+ state = next;
2305
+ phaseStartedAt = now;
2306
+ options.onStateChange?.(next);
2307
+ };
2308
+ let nativeLogs = null;
2309
+ const closeLogs = async () => {
2310
+ const stream = nativeLogs;
2311
+ nativeLogs = null;
2312
+ await stream?.close().catch(() => {
2313
+ return;
2314
+ });
2315
+ };
2316
+ try {
2317
+ await repairAbsoluteIosDevSession(project.projectRoot);
2318
+ throwIfAborted2(options.signal);
2319
+ transition("syncing");
2320
+ await requireSuccess2([project.cap, "sync", "ios"], "Capacitor iOS synchronization", run, { cwd: project.projectRoot, signal: options.signal });
2321
+ transition("configuring");
2322
+ await writeDevProjection(project, options.port, options.https === true);
2323
+ throwIfAborted2(options.signal);
2324
+ const fingerprintStartedAt = performance.now();
2325
+ const fingerprintPromise = fingerprintAbsoluteIosDevProject(project).then((fingerprint2) => {
2326
+ timings.fingerprinting = performance.now() - fingerprintStartedAt;
2327
+ return fingerprint2;
2328
+ });
2329
+ transition("booting");
2330
+ const { created, device } = await ensureManagedSimulator(project, capture);
2331
+ const startedSimulator = created || device.state !== "Booted";
2332
+ bootSimulator(project, device, capture);
2333
+ spawn([
2334
+ "open",
2335
+ "-a",
2336
+ "Simulator",
2337
+ "--args",
2338
+ "-CurrentDeviceUDID",
2339
+ device.udid
2340
+ ]);
2341
+ transition("connecting");
2342
+ await waitForBootedSimulator(project, device.udid, capture, sleep, options.signal);
2343
+ await requireSuccess2([project.xcrun, "simctl", "bootstatus", device.udid, "-b"], "iOS simulator boot readiness", run, { signal: options.signal });
2344
+ const fingerprint = await fingerprintPromise;
2345
+ transition("checking-native");
2346
+ const nativeCacheHit = await ensureIosDebugApp({
2347
+ cache: await readNativeCache(project.projectRoot),
2348
+ capture,
2349
+ fingerprint,
2350
+ log,
2351
+ project,
2352
+ run,
2353
+ signal: options.signal,
2354
+ transition,
2355
+ udid: device.udid
2356
+ });
2357
+ throwIfAborted2(options.signal);
2358
+ if (options.nativeLog)
2359
+ transition("streaming-logs");
2360
+ nativeLogs = attachNativeLogs(project, device.udid, options);
2361
+ transition("launching");
2362
+ await requireSuccess2([
2363
+ project.xcrun,
2364
+ "simctl",
2365
+ "launch",
2366
+ "--terminate-running-process",
2367
+ device.udid,
2368
+ project.config.appId
2369
+ ], "iOS app launch", run, { signal: options.signal });
2370
+ transition("ready");
2371
+ timings.total = performance.now() - startedAt;
2372
+ log(`iOS simulator connected (${device.udid}) with HMR on port ${options.port} in ${getDurationString(timings.total)} (${nativeCacheHit ? "native cache hit" : "native build installed"}).`);
2373
+ log(`iOS startup: ${timingSummary(timings)}.`);
2374
+ let closed = false;
2375
+ const close = async () => {
2376
+ if (closed)
2377
+ return;
2378
+ closed = true;
2379
+ transition("closing");
2380
+ await closeLogs();
2381
+ await repairAbsoluteIosDevSession(project.projectRoot);
2382
+ transition("closed");
2383
+ };
2384
+ return {
2385
+ close,
2386
+ nativeCacheHit,
2387
+ startedSimulator,
2388
+ timings: { ...timings },
2389
+ udid: device.udid,
2390
+ rebuild: async () => {
2391
+ if (closed)
2392
+ throw new Error("iOS development session is closed.");
2393
+ log("iOS native inputs changed; rebuilding without restarting the dev server.");
2394
+ await close();
2395
+ return startAbsoluteIosDevSession(options);
2396
+ },
2397
+ relaunch: async () => {
2398
+ if (closed)
2399
+ throw new Error("iOS development session is closed.");
2400
+ transition("launching");
2401
+ try {
2402
+ await requireSuccess2([
2403
+ project.xcrun,
2404
+ "simctl",
2405
+ "launch",
2406
+ "--terminate-running-process",
2407
+ device.udid,
2408
+ project.config.appId
2409
+ ], "iOS app relaunch", run, { signal: options.signal });
2410
+ transition("ready");
2411
+ log(`iOS app relaunched on ${device.udid}.`);
2412
+ } catch (error) {
2413
+ transition("failed");
2414
+ throw error;
2415
+ }
2416
+ },
2417
+ screenshot: async (destination) => {
2418
+ const resolved = resolve5(project.projectRoot, destination);
2419
+ if (!isInside(project.projectRoot, resolved))
2420
+ throw new Error("iOS screenshot destination must remain inside the project.");
2421
+ await mkdir5(dirname4(resolved), { recursive: true });
2422
+ await requireSuccess2([
2423
+ project.xcrun,
2424
+ "simctl",
2425
+ "io",
2426
+ device.udid,
2427
+ "screenshot",
2428
+ resolved
2429
+ ], "iOS simulator screenshot", run, { signal: options.signal });
2430
+ return resolved;
2431
+ },
2432
+ get state() {
2433
+ return state;
2434
+ }
2435
+ };
2436
+ } catch (error) {
2437
+ transition("failed");
2438
+ await closeLogs();
2439
+ await repairAbsoluteIosDevSession(project.projectRoot);
2440
+ throw error;
2441
+ }
2442
+ };
2443
+ // src/mobile/iosNativeWatcher.ts
2444
+ import { watch } from "fs";
2445
+ import { basename } from "path";
2446
+ var NATIVE_CHANGE_DEBOUNCE_MS = 500;
2447
+ var ROOT_NATIVE_INPUTS = new Set([
2448
+ "absolute.config.js",
2449
+ "absolute.config.mjs",
2450
+ "absolute.config.ts",
2451
+ "absolutejs.config.js",
2452
+ "absolutejs.config.mjs",
2453
+ "absolutejs.config.ts",
2454
+ "bun.lock",
2455
+ "bun.lockb",
2456
+ "capacitor.config.js",
2457
+ "capacitor.config.ts",
2458
+ "package.json"
2459
+ ]);
2460
+ var createAbsoluteIosNativeWatcher = async (options) => {
2461
+ let fingerprint = await fingerprintAbsoluteIosDevProject(options.project);
2462
+ let closed = false;
2463
+ let running = false;
2464
+ let timer;
2465
+ let rootInputChanged = false;
2466
+ const changedPaths = new Set;
2467
+ const watchers = [];
2468
+ const debounceMs = options.debounceMs ?? NATIVE_CHANGE_DEBOUNCE_MS;
2469
+ const close = () => {
2470
+ if (closed)
2471
+ return;
2472
+ closed = true;
2473
+ if (timer)
2474
+ clearTimeout(timer);
2475
+ watchers.forEach((watcher) => watcher.close());
2476
+ options.signal?.removeEventListener("abort", close);
2477
+ };
2478
+ const schedule = () => {
2479
+ if (closed || running)
2480
+ return;
2481
+ if (timer)
2482
+ clearTimeout(timer);
2483
+ timer = setTimeout(() => void flush(), debounceMs);
2484
+ };
2485
+ const flush = async () => {
2486
+ timer = undefined;
2487
+ if (closed || running || changedPaths.size === 0)
2488
+ return;
2489
+ running = true;
2490
+ const paths = [...changedPaths].sort();
2491
+ const forced = rootInputChanged;
2492
+ changedPaths.clear();
2493
+ rootInputChanged = false;
2494
+ try {
2495
+ const next = await fingerprintAbsoluteIosDevProject(options.project);
2496
+ if (!forced && next === fingerprint)
2497
+ return;
2498
+ await options.onChange({
2499
+ afterFingerprint: next,
2500
+ beforeFingerprint: fingerprint,
2501
+ paths,
2502
+ rootInputChanged: forced
2503
+ });
2504
+ fingerprint = await fingerprintAbsoluteIosDevProject(options.project);
2505
+ } catch (error) {
2506
+ options.onError?.(error);
2507
+ } finally {
2508
+ running = false;
2509
+ if (changedPaths.size > 0)
2510
+ schedule();
2511
+ }
2512
+ };
2513
+ const record = (path, force) => {
2514
+ if (closed)
2515
+ return;
2516
+ changedPaths.add(path);
2517
+ rootInputChanged ||= force;
2518
+ schedule();
2519
+ };
2520
+ watchers.push(watch(options.project.nativeDirectory, { recursive: true }, (_event, filename) => {
2521
+ if (filename)
2522
+ record(String(filename), false);
2523
+ }));
2524
+ watchers.push(watch(options.project.projectRoot, (_event, filename) => {
2525
+ if (!filename)
2526
+ return;
2527
+ const path = String(filename);
2528
+ if (isAbsoluteIosNativeRootInput(path))
2529
+ record(path, true);
2530
+ }));
2531
+ watchers.forEach((watcher) => watcher.on("error", (error) => options.onError?.(error)));
2532
+ options.signal?.addEventListener("abort", close, { once: true });
2533
+ return { close };
2534
+ };
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
+ };
3104
+ // src/mobile/associationFiles.ts
3105
+ import {
3106
+ access as access7,
3107
+ mkdir as mkdir7,
3108
+ readFile as readFile9,
3109
+ rename as rename8,
3110
+ rm as rm6,
3111
+ writeFile as writeFile8
3112
+ } from "fs/promises";
3113
+ import { resolve as resolve7 } from "path";
1474
3114
  import { Elysia } from "elysia";
1475
3115
 
1476
3116
  // src/mobile/config.ts
1477
- import { resolve as resolve5 } from "path";
3117
+ import { resolve as resolve6 } from "path";
1478
3118
  var APP_ID_PATTERN = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
1479
3119
  var SCHEME_PATTERN = /^[a-z][a-z0-9+.-]*$/;
1480
3120
  var APPLE_APP_ID_PREFIX_PATTERN = /^[A-Z0-9]{10}$/;
1481
3121
  var CERTIFICATE_FINGERPRINT_PATTERN = /^[0-9A-F]{64}$/;
1482
3122
  var HOSTNAME_PATTERN = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$/;
1483
3123
  var resolveProjectPath = (projectRoot, value, field) => {
1484
- const root = resolve5(projectRoot);
1485
- const path = resolve5(root, value);
3124
+ const root = resolve6(projectRoot);
3125
+ const path = resolve6(root, value);
1486
3126
  if (path !== root && !path.startsWith(`${root}/`)) {
1487
3127
  throw new TypeError(`${field} must remain inside the project root.`);
1488
3128
  }
@@ -1670,7 +3310,7 @@ var createAbsoluteMobileAssociationPlugin = (mobile, projectRoot, options = {})
1670
3310
  var writeAtomic = async (path, source) => {
1671
3311
  let current;
1672
3312
  try {
1673
- current = await readFile6(path, "utf8");
3313
+ current = await readFile9(path, "utf8");
1674
3314
  } catch (error) {
1675
3315
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
1676
3316
  throw error;
@@ -1679,23 +3319,23 @@ var writeAtomic = async (path, source) => {
1679
3319
  if (current === source)
1680
3320
  return false;
1681
3321
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
1682
- await writeFile6(temporary, source, { flag: "wx" });
1683
- await rename6(temporary, path);
3322
+ await writeFile8(temporary, source, { flag: "wx" });
3323
+ await rename8(temporary, path);
1684
3324
  return true;
1685
3325
  };
1686
3326
  var exists2 = async (path) => {
1687
3327
  try {
1688
- await access5(path);
3328
+ await access7(path);
1689
3329
  return true;
1690
3330
  } catch {
1691
3331
  return false;
1692
3332
  }
1693
3333
  };
1694
3334
  var assertOwnedOutput = async (root) => {
1695
- const path = resolve6(root, OWNERSHIP_FILE);
3335
+ const path = resolve7(root, OWNERSHIP_FILE);
1696
3336
  let ownership;
1697
3337
  try {
1698
- ownership = JSON.parse(await readFile6(path, "utf8"));
3338
+ ownership = JSON.parse(await readFile9(path, "utf8"));
1699
3339
  } catch {
1700
3340
  throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
1701
3341
  }
@@ -1709,22 +3349,22 @@ var publishGeneratedDirectory = async (temporary, root) => {
1709
3349
  await assertOwnedOutput(root);
1710
3350
  const backup = `${root}.${crypto.randomUUID()}.previous`;
1711
3351
  if (hasCurrent)
1712
- await rename6(root, backup);
3352
+ await rename8(root, backup);
1713
3353
  try {
1714
- await rename6(temporary, root);
3354
+ await rename8(temporary, root);
1715
3355
  } catch (error) {
1716
3356
  if (hasCurrent)
1717
- await rename6(backup, root);
3357
+ await rename8(backup, root);
1718
3358
  throw error;
1719
3359
  }
1720
3360
  if (hasCurrent)
1721
- await rm5(backup, { force: true, recursive: true });
3361
+ await rm6(backup, { force: true, recursive: true });
1722
3362
  };
1723
3363
  var materializeHost = async (root, host, files) => {
1724
- const directory = resolve6(root, host, ".well-known");
1725
- await mkdir5(directory, { recursive: true });
3364
+ const directory = resolve7(root, host, ".well-known");
3365
+ await mkdir7(directory, { recursive: true });
1726
3366
  return Promise.all(files.map(async ([name, document]) => {
1727
- const path = resolve6(directory, name);
3367
+ const path = resolve7(directory, name);
1728
3368
  await writeAtomic(path, `${JSON.stringify(document, null, 2)}
1729
3369
  `);
1730
3370
  return path;
@@ -1749,7 +3389,7 @@ var associationEndpoints = (config, documents) => config.deepLinkHosts.flatMap((
1749
3389
  return endpoints;
1750
3390
  });
1751
3391
  var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory) => {
1752
- const root = resolve6(outputDirectory);
3392
+ const root = resolve7(outputDirectory);
1753
3393
  const temporary = `${root}.${crypto.randomUUID()}.tmp`;
1754
3394
  const documents = createAbsoluteMobileAssociationDocuments(config, {
1755
3395
  requireAll: true
@@ -1760,16 +3400,16 @@ var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory)
1760
3400
  if (documents.apple) {
1761
3401
  files.push(["apple-app-site-association", documents.apple]);
1762
3402
  }
1763
- await mkdir5(temporary, { recursive: true });
3403
+ await mkdir7(temporary, { recursive: true });
1764
3404
  try {
1765
3405
  const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host) => materializeHost(temporary, host, files)))).flat();
1766
- await writeAtomic(resolve6(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
3406
+ await writeAtomic(resolve7(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
1767
3407
  `);
1768
3408
  await publishGeneratedDirectory(temporary, root);
1769
- const written = temporaryPaths.map((path) => resolve6(root, path.slice(temporary.length + 1)));
3409
+ const written = temporaryPaths.map((path) => resolve7(root, path.slice(temporary.length + 1)));
1770
3410
  return { root, written };
1771
3411
  } catch (error) {
1772
- await rm5(temporary, { force: true, recursive: true });
3412
+ await rm6(temporary, { force: true, recursive: true });
1773
3413
  throw error;
1774
3414
  }
1775
3415
  };
@@ -1814,10 +3454,10 @@ var frameworks2 = new Set([
1814
3454
  "svelte",
1815
3455
  "vue"
1816
3456
  ]);
1817
- var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3457
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1818
3458
  var isPageFramework2 = (value) => typeof value === "string" && frameworks2.has(value);
1819
3459
  var parseAbsoluteMobileBuildPageMetadata = (value) => {
1820
- if (!isRecord3(value))
3460
+ if (!isRecord4(value))
1821
3461
  return;
1822
3462
  if (typeof value.bundleKey !== "string" || typeof value.contract !== "string" || !isPageFramework2(value.framework) || typeof value.pageId !== "string" || typeof value.propsSchemaHash !== "string") {
1823
3463
  return;
@@ -1831,23 +3471,23 @@ var parseAbsoluteMobileBuildPageMetadata = (value) => {
1831
3471
  };
1832
3472
  };
1833
3473
  // src/mobile/buildPipeline.ts
1834
- import { readFile as readFile10 } from "fs/promises";
1835
- import { join as join9, resolve as resolve9 } from "path";
3474
+ import { readFile as readFile13 } from "fs/promises";
3475
+ import { join as join11, resolve as resolve10 } from "path";
1836
3476
  import { pathToFileURL as pathToFileURL2 } from "url";
1837
3477
 
1838
3478
  // src/mobile/buildRelease.ts
1839
- import { createHash as createHash6 } from "crypto";
1840
- import { readFile as readFile7 } from "fs/promises";
1841
- import { join as join6, relative as relative5, resolve as resolve7 } from "path";
1842
- var sha256 = (bytes) => createHash6("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");
1843
3483
  var readPageMetadata = (route) => parseAbsoluteMobileBuildPageMetadata(route.hooks?.detail?.[ABSOLUTE_MOBILE_ROUTE_DETAIL]);
1844
3484
  var resolveAssetPath = (buildDirectory, assetPath) => {
1845
- const resolvedBuildDirectory = resolve7(buildDirectory);
1846
- const resolvedAsset = resolve7(assetPath);
3485
+ const resolvedBuildDirectory = resolve8(buildDirectory);
3486
+ const resolvedAsset = resolve8(assetPath);
1847
3487
  if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
1848
3488
  return resolvedAsset;
1849
3489
  }
1850
- return join6(buildDirectory, assetPath.replace(/^\/+/, ""));
3490
+ return join8(buildDirectory, assetPath.replace(/^\/+/, ""));
1851
3491
  };
1852
3492
  var pageFor = async (metadata, manifest, buildDirectory) => {
1853
3493
  const assetPath = manifest[metadata.bundleKey];
@@ -1855,8 +3495,8 @@ var pageFor = async (metadata, manifest, buildDirectory) => {
1855
3495
  throw new TypeError(`Mobile page ${metadata.pageId} references missing manifest asset ${metadata.bundleKey}.`);
1856
3496
  }
1857
3497
  const resolvedAssetPath = resolveAssetPath(buildDirectory, assetPath);
1858
- const bytes = await readFile7(resolvedAssetPath);
1859
- const bundlePath = `/${relative5(resolve7(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
3498
+ const bytes = await readFile10(resolvedAssetPath);
3499
+ const bundlePath = `/${relative7(resolve8(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
1860
3500
  return {
1861
3501
  bundleHash: sha256(bytes),
1862
3502
  bundlePath,
@@ -1869,7 +3509,7 @@ var pageFor = async (metadata, manifest, buildDirectory) => {
1869
3509
  var buildAbsoluteMobileCompatibilityRelease = async (options) => {
1870
3510
  const [captured, producerBytes] = await Promise.all([
1871
3511
  captureAbsoluteMobileRouteGraph(options.app),
1872
- readFile7(options.producerPath)
3512
+ readFile10(options.producerPath)
1873
3513
  ]);
1874
3514
  if (captured.length === 0) {
1875
3515
  throw new TypeError("No instrumented AbsoluteJS mobile page routes were found in the finalized Elysia route graph.");
@@ -1949,16 +3589,16 @@ var captureAbsoluteMobileRouteGraph = async (app) => {
1949
3589
 
1950
3590
  // src/mobile/capacitorBundle.ts
1951
3591
  import {
1952
- copyFile as copyFile4,
1953
- mkdir as mkdir6,
3592
+ copyFile as copyFile5,
3593
+ mkdir as mkdir8,
1954
3594
  mkdtemp as mkdtemp4,
1955
- readFile as readFile8,
1956
- rename as rename7,
1957
- rm as rm6,
1958
- writeFile as writeFile7
3595
+ readFile as readFile11,
3596
+ rename as rename9,
3597
+ rm as rm7,
3598
+ writeFile as writeFile9
1959
3599
  } from "fs/promises";
1960
3600
  import { existsSync as existsSync2 } from "fs";
1961
- import { basename, dirname as dirname4, extname, join as join7, resolve as resolve8 } from "path";
3601
+ import { basename as basename2, dirname as dirname6, extname, join as join9, resolve as resolve9 } from "path";
1962
3602
 
1963
3603
  // src/mobile/routeMatcher.ts
1964
3604
  var REGEXP_SPECIAL_CHARACTERS = /[.*+?^${}()|[\]\\]/g;
@@ -2114,14 +3754,14 @@ var envelopeResponse = (response, status) => new Response(JSON.stringify({
2114
3754
  protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
2115
3755
  response
2116
3756
  }), { headers: responseHeaders(), status });
2117
- var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3757
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2118
3758
  var normalizeJsonValue = (value) => {
2119
3759
  const serialized = JSON.stringify(value);
2120
3760
  if (serialized === undefined) {
2121
3761
  throw new TypeError("Mobile page props must be JSON-serializable.");
2122
3762
  }
2123
3763
  const parsed = JSON.parse(serialized);
2124
- if (!isRecord4(parsed)) {
3764
+ if (!isRecord5(parsed)) {
2125
3765
  throw new TypeError("Mobile page props must serialize to an object.");
2126
3766
  }
2127
3767
  return parsed;
@@ -2246,14 +3886,14 @@ var upgradeReasons = new Set([
2246
3886
  "protocol",
2247
3887
  "runtime"
2248
3888
  ]);
2249
- var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3889
+ var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2250
3890
  var isFramework = (value) => typeof value === "string" && frameworks4.has(value);
2251
3891
  var isUpgradeReason = (value) => typeof value === "string" && upgradeReasons.has(value);
2252
3892
  var parsePageResult = (value) => {
2253
3893
  if (typeof value.contract !== "string" || !isFramework(value.framework) || typeof value.pageId !== "string" || typeof value.status !== "number") {
2254
3894
  throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The mobile page response is missing required page metadata.");
2255
3895
  }
2256
- if (!isRecord5(value.props)) {
3896
+ if (!isRecord6(value.props)) {
2257
3897
  throw new AbsoluteMobilePageProtocolError("invalid-props", "The mobile page response must contain an object props value.");
2258
3898
  }
2259
3899
  return {
@@ -2299,7 +3939,7 @@ var activateAbsoluteMobilePage = async (value, options) => {
2299
3939
  };
2300
3940
  };
2301
3941
  var parseAbsoluteMobilePageEnvelope = (value) => {
2302
- if (!isRecord5(value) || !isRecord5(value.response)) {
3942
+ if (!isRecord6(value) || !isRecord6(value.response)) {
2303
3943
  throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The server did not return an AbsoluteJS mobile page envelope.");
2304
3944
  }
2305
3945
  if (value.protocol !== ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION) {
@@ -2392,7 +4032,7 @@ var INDEX_FILE = "index.html";
2392
4032
  var CLIENT_IMPORT_PATTERN = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s*)["'](\/[^"']+)["']/gu;
2393
4033
  var errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
2394
4034
  var shellBootstrapModule = () => {
2395
- const candidate = ["js", "ts"].map((extension) => join7(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync2);
4035
+ const candidate = ["js", "ts"].map((extension) => join9(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync2);
2396
4036
  if (candidate)
2397
4037
  return candidate;
2398
4038
  throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
@@ -2413,8 +4053,8 @@ var indexHtml = (appName) => `<!doctype html>
2413
4053
  </html>
2414
4054
  `;
2415
4055
  var sourceAssetPath = (buildDirectory, bundlePath) => {
2416
- const root = resolve8(buildDirectory);
2417
- const asset = resolve8(root, bundlePath.replace(/^\/+/, ""));
4056
+ const root = resolve9(buildDirectory);
4057
+ const asset = resolve9(root, bundlePath.replace(/^\/+/, ""));
2418
4058
  if (!asset.startsWith(`${root}/`)) {
2419
4059
  throw new TypeError("Mobile page bundle escaped the build directory.");
2420
4060
  }
@@ -2422,8 +4062,8 @@ var sourceAssetPath = (buildDirectory, bundlePath) => {
2422
4062
  };
2423
4063
  var buildShellBootstrap = async (staging) => {
2424
4064
  const modulePath = shellBootstrapModule();
2425
- const entryPath = join7(staging, ".absolute-mobile-entry.ts");
2426
- await writeFile7(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)};
2427
4067
  void startAbsoluteMobileShell();
2428
4068
  `);
2429
4069
  const build = await Bun.build({
@@ -2435,31 +4075,31 @@ void startAbsoluteMobileShell();
2435
4075
  if (!build.success || build.outputs.length !== 1) {
2436
4076
  throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
2437
4077
  }
2438
- await rename7(build.outputs[0]?.path ?? "", join7(staging, BOOTSTRAP_FILE));
2439
- await rm6(entryPath, { force: true });
4078
+ await rename9(build.outputs[0]?.path ?? "", join9(staging, BOOTSTRAP_FILE));
4079
+ await rm7(entryPath, { force: true });
2440
4080
  };
2441
4081
  var removePreviousBundle = async (backup, moved) => {
2442
4082
  if (!moved)
2443
4083
  return;
2444
- await rm6(backup, { force: true, recursive: true });
4084
+ await rm7(backup, { force: true, recursive: true });
2445
4085
  };
2446
4086
  var restorePreviousBundle = async (backup, destination, moved) => {
2447
4087
  if (!moved)
2448
4088
  return;
2449
- await rename7(backup, destination);
4089
+ await rename9(backup, destination);
2450
4090
  };
2451
4091
  var installBundle = async (staging, destination) => {
2452
4092
  const backup = `${destination}.previous-${crypto.randomUUID()}`;
2453
4093
  let movedPrevious = false;
2454
4094
  try {
2455
- await rename7(destination, backup);
4095
+ await rename9(destination, backup);
2456
4096
  movedPrevious = true;
2457
4097
  } catch (error) {
2458
4098
  if (!errorHasCode2(error, "ENOENT"))
2459
4099
  throw error;
2460
4100
  }
2461
4101
  try {
2462
- await rename7(staging, destination);
4102
+ await rename9(staging, destination);
2463
4103
  await removePreviousBundle(backup, movedPrevious);
2464
4104
  } catch (error) {
2465
4105
  await restorePreviousBundle(backup, destination, movedPrevious);
@@ -2473,12 +4113,12 @@ var copyClientPage = async (page, buildDirectory, staging, copiedDependencies) =
2473
4113
  const extension = extname(page.bundlePath) || ".js";
2474
4114
  const localBundlePath = `./pages/${page.bundleHash}${extension}`;
2475
4115
  const source = sourceAssetPath(buildDirectory, page.bundlePath);
2476
- await copyFile4(source, join7(staging, localBundlePath));
4116
+ await copyFile5(source, join9(staging, localBundlePath));
2477
4117
  await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
2478
4118
  return { ...page, localBundlePath };
2479
4119
  };
2480
4120
  var absoluteClientImports = async (sourcePath) => {
2481
- const source = await readFile8(sourcePath, "utf8");
4121
+ const source = await readFile11(sourcePath, "utf8");
2482
4122
  return [...source.matchAll(CLIENT_IMPORT_PATTERN)].flatMap((match) => {
2483
4123
  const [specifier] = match.slice(1);
2484
4124
  return specifier ? [specifier.split(/[?#]/u, 1)[0] ?? specifier] : [];
@@ -2489,9 +4129,9 @@ var copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, co
2489
4129
  return;
2490
4130
  copied.add(specifier);
2491
4131
  const source = sourceAssetPath(buildDirectory, specifier);
2492
- const destination = join7(staging, specifier.replace(/^\/+/, ""));
2493
- await mkdir6(dirname4(destination), { recursive: true });
2494
- await copyFile4(source, destination);
4132
+ const destination = join9(staging, specifier.replace(/^\/+/, ""));
4133
+ await mkdir8(dirname6(destination), { recursive: true });
4134
+ await copyFile5(source, destination);
2495
4135
  await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
2496
4136
  };
2497
4137
  var copyAbsoluteClientDependencies = async (sourcePath, buildDirectory, staging, copied) => {
@@ -2503,11 +4143,11 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
2503
4143
  throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
2504
4144
  }
2505
4145
  const destination = options.config.bundleDirectory;
2506
- await mkdir6(dirname4(destination), { recursive: true });
2507
- const staging = await mkdtemp4(join7(dirname4(destination), `.${basename(destination)}.stage-`));
4146
+ await mkdir8(dirname6(destination), { recursive: true });
4147
+ const staging = await mkdtemp4(join9(dirname6(destination), `.${basename2(destination)}.stage-`));
2508
4148
  try {
2509
- const pageDirectory = join7(staging, "pages");
2510
- await mkdir6(pageDirectory, { recursive: true });
4149
+ const pageDirectory = join9(staging, "pages");
4150
+ await mkdir8(pageDirectory, { recursive: true });
2511
4151
  const copiedDependencies = new Set;
2512
4152
  const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
2513
4153
  const manifest = {
@@ -2524,48 +4164,48 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
2524
4164
  runtime: options.artifact.runtime
2525
4165
  };
2526
4166
  await Promise.all([
2527
- writeFile7(join7(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
4167
+ writeFile9(join9(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
2528
4168
  `),
2529
- writeFile7(join7(staging, INDEX_FILE), indexHtml(options.config.appName)),
4169
+ writeFile9(join9(staging, INDEX_FILE), indexHtml(options.config.appName)),
2530
4170
  buildShellBootstrap(staging)
2531
4171
  ]);
2532
4172
  await installBundle(staging, destination);
2533
4173
  return manifest;
2534
4174
  } catch (error) {
2535
- await rm6(staging, { force: true, recursive: true });
4175
+ await rm7(staging, { force: true, recursive: true });
2536
4176
  throw error;
2537
4177
  }
2538
4178
  };
2539
4179
 
2540
4180
  // src/mobile/materializedBundle.ts
2541
- import { createHash as createHash7 } from "crypto";
4181
+ import { createHash as createHash9 } from "crypto";
2542
4182
  import {
2543
- access as access6,
2544
- mkdir as mkdir7,
4183
+ access as access8,
4184
+ mkdir as mkdir9,
2545
4185
  mkdtemp as mkdtemp5,
2546
- readFile as readFile9,
2547
- rename as rename8,
2548
- rm as rm7,
2549
- writeFile as writeFile8
4186
+ readFile as readFile12,
4187
+ rename as rename10,
4188
+ rm as rm8,
4189
+ writeFile as writeFile10
2550
4190
  } from "fs/promises";
2551
- import { dirname as dirname5, join as join8, resolve as resolvePath2 } from "path";
4191
+ import { dirname as dirname7, join as join10, resolve as resolvePath3 } from "path";
2552
4192
  import { pathToFileURL } from "url";
2553
4193
  var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1;
2554
4194
  var CURRENT_BUNDLE_FILE = "current.json";
2555
4195
  var BUNDLES_DIRECTORY = "bundles";
2556
4196
  var ARTIFACT_FILE2 = "artifact.json";
2557
4197
  var BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
2558
- var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4198
+ var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2559
4199
  var errorHasCode3 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
2560
4200
  var bundleIdFor = (currentReleaseId, releases) => {
2561
4201
  const identity = JSON.stringify({
2562
4202
  currentReleaseId,
2563
4203
  releases: releases.map(({ releaseId }) => releaseId)
2564
4204
  });
2565
- return `amb_${createHash7("sha256").update(identity).digest("hex")}`;
4205
+ return `amb_${createHash9("sha256").update(identity).digest("hex")}`;
2566
4206
  };
2567
4207
  var parseBundleIndex = (value) => {
2568
- if (!isRecord6(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)) {
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)) {
2569
4209
  throw new TypeError("Invalid materialized mobile compatibility bundle.");
2570
4210
  }
2571
4211
  const releases = value.releases.map(parseAbsoluteMobileCompatibilityArtifact);
@@ -2588,30 +4228,30 @@ var parseBundleIndex = (value) => {
2588
4228
  };
2589
4229
  };
2590
4230
  var writeRelease = async (root, release) => {
2591
- const directory = join8(root, release.artifact.releaseId);
2592
- const producerPath = join8(directory, release.artifact.producer.module);
2593
- await mkdir7(dirname5(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 });
2594
4234
  await Promise.all([
2595
- writeFile8(join8(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
4235
+ writeFile10(join10(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
2596
4236
  `),
2597
- writeFile8(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
4237
+ writeFile10(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
2598
4238
  ]);
2599
4239
  };
2600
4240
  var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
2601
- const destination = join8(bundlesRoot, bundleId);
4241
+ const destination = join10(bundlesRoot, bundleId);
2602
4242
  try {
2603
- await access6(destination);
4243
+ await access8(destination);
2604
4244
  return destination;
2605
4245
  } catch (error) {
2606
4246
  if (!errorHasCode3(error, "ENOENT"))
2607
4247
  throw error;
2608
4248
  }
2609
- const staging = await mkdtemp5(join8(bundlesRoot, ".stage-"));
4249
+ const staging = await mkdtemp5(join10(bundlesRoot, ".stage-"));
2610
4250
  try {
2611
4251
  await Promise.all(releases.map((release) => writeRelease(staging, release)));
2612
- await rename8(staging, destination);
4252
+ await rename10(staging, destination);
2613
4253
  } catch (error) {
2614
- await rm7(staging, { force: true, recursive: true });
4254
+ await rm8(staging, { force: true, recursive: true });
2615
4255
  if (errorHasCode3(error, "EEXIST") || errorHasCode3(error, "ENOTEMPTY")) {
2616
4256
  return destination;
2617
4257
  }
@@ -2622,7 +4262,7 @@ var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
2622
4262
  var readCompatibilityModule = (modulePath) => import(pathToFileURL(modulePath).href);
2623
4263
  var resolveProducerHandler = (loaded, exportName) => {
2624
4264
  const value = loaded[exportName];
2625
- if (!isRecord6(value) || typeof value.handle !== "function") {
4265
+ if (!isRecord7(value) || typeof value.handle !== "function") {
2626
4266
  throw new TypeError(`Compatibility producer export ${exportName} must expose handle(request).`);
2627
4267
  }
2628
4268
  const { handle } = value;
@@ -2639,16 +4279,16 @@ var resolveProducerHandler = (loaded, exportName) => {
2639
4279
  };
2640
4280
  };
2641
4281
  var loadAbsoluteMobileMaterializedBundle = async (root) => {
2642
- const resolvedRoot = resolvePath2(root);
2643
- const serialized = await readFile9(join8(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
4282
+ const resolvedRoot = resolvePath3(root);
4283
+ const serialized = await readFile12(join10(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
2644
4284
  const parsed = JSON.parse(serialized);
2645
4285
  const index = parseBundleIndex(parsed);
2646
- const bundleRoot = join8(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
4286
+ const bundleRoot = join10(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
2647
4287
  return {
2648
4288
  artifacts: index.releases,
2649
4289
  currentReleaseId: index.currentReleaseId,
2650
4290
  loadProducer: async (artifact) => {
2651
- const modulePath = join8(bundleRoot, artifact.releaseId, artifact.producer.module);
4291
+ const modulePath = join10(bundleRoot, artifact.releaseId, artifact.producer.module);
2652
4292
  await verifyAbsoluteMobileCompatibilityProducer({
2653
4293
  artifact,
2654
4294
  producer: Bun.file(modulePath)
@@ -2674,9 +4314,9 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
2674
4314
  }
2675
4315
  return release;
2676
4316
  });
2677
- const root = resolvePath2(input.root);
2678
- const bundlesRoot = join8(root, BUNDLES_DIRECTORY);
2679
- await mkdir7(bundlesRoot, { recursive: true });
4317
+ const root = resolvePath3(input.root);
4318
+ const bundlesRoot = join10(root, BUNDLES_DIRECTORY);
4319
+ await mkdir9(bundlesRoot, { recursive: true });
2680
4320
  const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
2681
4321
  await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
2682
4322
  const index = {
@@ -2685,22 +4325,22 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
2685
4325
  format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
2686
4326
  releases: artifacts
2687
4327
  };
2688
- const pointerPath = join8(root, CURRENT_BUNDLE_FILE);
2689
- const temporaryPointerPath = join8(root, `.current-${crypto.randomUUID()}.json`);
2690
- await writeFile8(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")}
2691
4331
  `, { flag: "wx" });
2692
- await rename8(temporaryPointerPath, pointerPath);
4332
+ await rename10(temporaryPointerPath, pointerPath);
2693
4333
  return index;
2694
4334
  };
2695
4335
  var readAbsoluteMobileMaterializedReleases = async (root) => {
2696
- const resolvedRoot = resolvePath2(root);
4336
+ const resolvedRoot = resolvePath3(root);
2697
4337
  try {
2698
- const serialized = await readFile9(join8(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
4338
+ const serialized = await readFile12(join10(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
2699
4339
  const parsed = JSON.parse(serialized);
2700
4340
  const index = parseBundleIndex(parsed);
2701
- const bundleRoot = join8(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
4341
+ const bundleRoot = join10(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
2702
4342
  return Promise.all(index.releases.map(async (artifact) => {
2703
- const producer = Bun.file(join8(bundleRoot, artifact.releaseId, artifact.producer.module));
4343
+ const producer = Bun.file(join10(bundleRoot, artifact.releaseId, artifact.producer.module));
2704
4344
  await verifyAbsoluteMobileCompatibilityProducer({
2705
4345
  artifact,
2706
4346
  producer
@@ -2749,11 +4389,11 @@ var loadServerApp = async (producerPath) => {
2749
4389
  return { app, exportName };
2750
4390
  };
2751
4391
  var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
2752
- const buildDirectory = resolve9(options.buildDirectory);
4392
+ const buildDirectory = resolve10(options.buildDirectory);
2753
4393
  const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
2754
- const root = join9(buildDirectory, ".absolutejs", "mobile-compatibility");
4394
+ const root = join11(buildDirectory, ".absolutejs", "mobile-compatibility");
2755
4395
  const [manifestSource, previous] = await Promise.all([
2756
- readFile10(join9(buildDirectory, "manifest.json"), "utf8"),
4396
+ readFile13(join11(buildDirectory, "manifest.json"), "utf8"),
2757
4397
  readAbsoluteMobileMaterializedReleases(root)
2758
4398
  ]);
2759
4399
  const manifest = JSON.parse(manifestSource);
@@ -2764,7 +4404,7 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
2764
4404
  process.env.ABSOLUTE_BUILD_DIR = buildDirectory;
2765
4405
  let loaded;
2766
4406
  try {
2767
- loaded = await loadServerApp(resolve9(options.producerPath));
4407
+ loaded = await loadServerApp(resolve10(options.producerPath));
2768
4408
  } finally {
2769
4409
  restoreBuildDirectory(previousBuildDirectory);
2770
4410
  }
@@ -2775,7 +4415,7 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
2775
4415
  manifest,
2776
4416
  previousArtifacts: previous.map(({ artifact }) => artifact),
2777
4417
  producerExport: loaded.exportName,
2778
- producerPath: resolve9(options.producerPath),
4418
+ producerPath: resolve10(options.producerPath),
2779
4419
  runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
2780
4420
  });
2781
4421
  const releasesById = new Map([current, ...previous].map((release) => [
@@ -2869,20 +4509,20 @@ var createAbsoluteMobileCompatibilityDispatcher = (options) => {
2869
4509
  }).as("global");
2870
4510
  };
2871
4511
  // src/mobile/nativeDeepLinks.ts
2872
- import { readFile as readFile11, rename as rename9, writeFile as writeFile9 } from "fs/promises";
2873
- import { join as join10 } from "path";
4512
+ import { readFile as readFile14, rename as rename11, writeFile as writeFile11 } from "fs/promises";
4513
+ import { join as join12 } from "path";
2874
4514
  var START_MARKER = "<!-- absolutejs:deep-links:start -->";
2875
4515
  var END_MARKER = "<!-- absolutejs:deep-links:end -->";
2876
4516
  var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
2877
4517
  var NOT_FOUND = -1;
2878
4518
  var escapeXml = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
2879
4519
  var writeChangedFile = async (path, source) => {
2880
- const current = await readFile11(path, "utf8");
4520
+ const current = await readFile14(path, "utf8");
2881
4521
  if (current === source)
2882
4522
  return false;
2883
4523
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
2884
- await writeFile9(temporary, source, { flag: "wx" });
2885
- await rename9(temporary, path);
4524
+ await writeFile11(temporary, source, { flag: "wx" });
4525
+ await rename11(temporary, path);
2886
4526
  return true;
2887
4527
  };
2888
4528
  var replaceManagedRegion = (source, region, insertAt) => {
@@ -2927,8 +4567,8 @@ ${hosts}
2927
4567
  `;
2928
4568
  };
2929
4569
  var configureAndroid = async (config) => {
2930
- const path = join10(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
2931
- const source = await readFile11(path, "utf8");
4570
+ const path = join12(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
4571
+ const source = await readFile14(path, "utf8");
2932
4572
  const mainActivity = source.indexOf('android:name=".MainActivity"');
2933
4573
  if (mainActivity === NOT_FOUND) {
2934
4574
  throw new TypeError("Android MainActivity was not found.");
@@ -2953,8 +4593,8 @@ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
2953
4593
  ${END_MARKER}
2954
4594
  `;
2955
4595
  var configureIosInfo = async (config) => {
2956
- const path = join10(config.nativeProjectDirectory, "ios/App/App/Info.plist");
2957
- const source = await readFile11(path, "utf8");
4596
+ const path = join12(config.nativeProjectDirectory, "ios/App/App/Info.plist");
4597
+ const source = await readFile14(path, "utf8");
2958
4598
  const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
2959
4599
  ${END_MARKER}
2960
4600
  `;
@@ -2977,10 +4617,10 @@ ${domains}
2977
4617
  `;
2978
4618
  };
2979
4619
  var configureIosEntitlements = async (config) => {
2980
- const path = join10(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
4620
+ const path = join12(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
2981
4621
  let current = "";
2982
4622
  try {
2983
- current = await readFile11(path, "utf8");
4623
+ current = await readFile14(path, "utf8");
2984
4624
  } catch (error) {
2985
4625
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
2986
4626
  throw error;
@@ -2990,13 +4630,13 @@ var configureIosEntitlements = async (config) => {
2990
4630
  if (current === source)
2991
4631
  return false;
2992
4632
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
2993
- await writeFile9(temporary, source, { flag: "wx" });
2994
- await rename9(temporary, path);
4633
+ await writeFile11(temporary, source, { flag: "wx" });
4634
+ await rename11(temporary, path);
2995
4635
  return true;
2996
4636
  };
2997
4637
  var configureIosProject = async (config) => {
2998
- const path = join10(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
2999
- const source = await readFile11(path, "utf8");
4638
+ const path = join12(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
4639
+ const source = await readFile14(path, "utf8");
3000
4640
  const declarations = [
3001
4641
  ...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
3002
4642
  ].map((match) => match[1]);
@@ -3034,8 +4674,8 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
3034
4674
  };
3035
4675
  };
3036
4676
  // src/mobile/releasePublisher.ts
3037
- import { access as access7 } from "fs/promises";
3038
- import { isAbsolute as isAbsolute4, relative as relative6, resolve as resolve10, sep as sep4 } from "path";
4677
+ import { access as access9 } from "fs/promises";
4678
+ import { isAbsolute as isAbsolute6, relative as relative8, resolve as resolve11, sep as sep6 } from "path";
3039
4679
  import { pathToFileURL as pathToFileURL3 } from "url";
3040
4680
  var prepareAbsoluteIosRelease = async (publisher, options) => {
3041
4681
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -3058,24 +4698,24 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
3058
4698
  }
3059
4699
  return versionCode;
3060
4700
  };
3061
- var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3062
- var isPublisher = (value) => isRecord7(value) && typeof value.publish === "function";
4701
+ var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4702
+ var isPublisher = (value) => isRecord8(value) && typeof value.publish === "function";
3063
4703
  var publisherModulePath = (projectRoot, requested) => {
3064
- const root = resolve10(projectRoot);
3065
- const path = resolve10(root, requested);
3066
- const projectRelative = relative6(root, path);
3067
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep4}`) || isAbsolute4(projectRelative)) {
4704
+ const root = resolve11(projectRoot);
4705
+ const path = resolve11(root, requested);
4706
+ const projectRelative = relative8(root, path);
4707
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep6}`) || isAbsolute6(projectRelative)) {
3068
4708
  throw new TypeError("mobile publish --registry must remain inside the project.");
3069
4709
  }
3070
4710
  return path;
3071
4711
  };
3072
4712
  var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
3073
4713
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
3074
- await access7(modulePath).catch(() => {
4714
+ await access9(modulePath).catch(() => {
3075
4715
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
3076
4716
  });
3077
4717
  const loaded = await import(pathToFileURL3(modulePath).href);
3078
- const publisher = isRecord7(loaded) ? loaded.default ?? loaded.registry : undefined;
4718
+ const publisher = isRecord8(loaded) ? loaded.default ?? loaded.registry : undefined;
3079
4719
  if (!isPublisher(publisher)) {
3080
4720
  throw new TypeError("Native release registry module must default-export a registry with publish(options).");
3081
4721
  }
@@ -3133,13 +4773,13 @@ var publishAbsoluteIosRelease = async (options) => {
3133
4773
  };
3134
4774
  // src/mobile/routeMetadataTransform.ts
3135
4775
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
3136
- import { dirname as dirname6, extname as extname2, relative as relative7, resolve as resolve11 } from "path";
4776
+ import { dirname as dirname8, extname as extname2, relative as relative9, resolve as resolve12 } from "path";
3137
4777
  import ts from "typescript";
3138
4778
  var ROUTE_METHODS = new Set(["get", "head"]);
3139
4779
  var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
3140
4780
  var PAGE_HANDLER = "handleReactPageRequest";
3141
4781
  var posixPath = (value) => value.replace(/\\/g, "/");
3142
- var findTsconfig = (entry, projectRoot) => ts.findConfigFile(dirname6(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");
3143
4783
  var createProgram = (entry, projectRoot) => {
3144
4784
  const configPath = findTsconfig(entry, projectRoot);
3145
4785
  if (!configPath) {
@@ -3151,7 +4791,7 @@ var createProgram = (entry, projectRoot) => {
3151
4791
  target: ts.ScriptTarget.ESNext
3152
4792
  });
3153
4793
  }
3154
- const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) => readFileSync2(path, "utf8")).config, ts.sys, dirname6(configPath));
4794
+ const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) => readFileSync2(path, "utf8")).config, ts.sys, dirname8(configPath));
3155
4795
  if (!parsed.fileNames.includes(entry))
3156
4796
  parsed.fileNames.push(entry);
3157
4797
  return ts.createProgram(parsed.fileNames, parsed.options);
@@ -3257,7 +4897,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
3257
4897
  const declaration = symbol?.declarations?.[0];
3258
4898
  const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
3259
4899
  const exportedName = symbol?.name ?? expression.getText(sourceFile);
3260
- const source = posixPath(relative7(projectRoot, file));
4900
+ const source = posixPath(relative9(projectRoot, file));
3261
4901
  return `${source}#${exportedName}`;
3262
4902
  };
3263
4903
  var resolveAlias = (symbol, checker) => {
@@ -3364,7 +5004,7 @@ var analyzeProgram = (program, projectRoot) => {
3364
5004
  const checker = program.getTypeChecker();
3365
5005
  const analyzed = new Map;
3366
5006
  for (const sourceFile of program.getSourceFiles()) {
3367
- const resolvedFile = resolve11(sourceFile.fileName);
5007
+ const resolvedFile = resolve12(sourceFile.fileName);
3368
5008
  if (!isProjectSource(sourceFile, resolvedFile, projectRoot))
3369
5009
  continue;
3370
5010
  const analysis = analyzeSourceFile(sourceFile, checker, projectRoot);
@@ -3447,14 +5087,14 @@ var transformFile = (source, fileName, analysis) => {
3447
5087
  };
3448
5088
  var ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL = ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION;
3449
5089
  var createAbsoluteMobileRouteMetadataPlugin = (options) => {
3450
- const projectRoot = resolve11(options.projectRoot ?? process.cwd());
3451
- const entry = resolve11(options.entry);
5090
+ const projectRoot = resolve12(options.projectRoot ?? process.cwd());
5091
+ const entry = resolve12(options.entry);
3452
5092
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
3453
5093
  return {
3454
5094
  name: "absolute-mobile-route-metadata",
3455
5095
  setup(build) {
3456
5096
  build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
3457
- const analysis = analyzed.get(resolve11(path));
5097
+ const analysis = analyzed.get(resolve12(path));
3458
5098
  if (!analysis)
3459
5099
  return;
3460
5100
  const source = await Bun.file(path).text();
@@ -3467,47 +5107,71 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
3467
5107
  };
3468
5108
  };
3469
5109
  var inspectAbsoluteMobileRouteMetadata = (options) => {
3470
- const projectRoot = resolve11(options.projectRoot ?? process.cwd());
3471
- const entry = resolve11(options.entry);
5110
+ const projectRoot = resolve12(options.projectRoot ?? process.cwd());
5111
+ const entry = resolve12(options.entry);
3472
5112
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
3473
5113
  return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
3474
- file: posixPath(relative7(projectRoot, file)),
5114
+ file: posixPath(relative9(projectRoot, file)),
3475
5115
  metadata
3476
5116
  })));
3477
5117
  };
3478
5118
  export {
3479
5119
  writeAbsoluteCapacitorConfig,
5120
+ waitForAbsoluteIosHmrLog,
3480
5121
  verifyAbsoluteMobileCompatibilityProducer,
3481
5122
  verifyAbsoluteMobileAssociationFiles,
5123
+ validateAbsoluteSshDestination,
5124
+ validateAbsoluteRemoteMacProfileName,
5125
+ syncAbsoluteRemoteMacProject,
5126
+ startAbsoluteRemoteIosDevSession,
5127
+ startAbsoluteIosDevSession,
3482
5128
  runWithAbsoluteMobileProducer,
3483
5129
  retainAbsoluteMobileCompatibilityArtifacts,
3484
5130
  resolveAbsoluteMobileRoute,
3485
5131
  resolveAbsoluteMobileDeepLink,
3486
5132
  resolveAbsoluteMobileCompatibilityRelease,
5133
+ repairAbsoluteIosDevSession,
5134
+ removeAbsoluteRemoteMacProfile,
5135
+ redactAbsoluteIosLog,
3487
5136
  readAbsoluteMobileMaterializedReleases,
3488
5137
  publishAbsoluteIosRelease,
3489
5138
  publishAbsoluteAndroidRelease,
3490
5139
  prepareAbsoluteIosRelease,
5140
+ prepareAbsoluteIosDevProject,
3491
5141
  prepareAbsoluteAndroidRelease,
5142
+ parseIosSimulators,
5143
+ parseIosRuntimes,
5144
+ parseIosDeviceTypes,
3492
5145
  parseAbsoluteMobilePageRequest,
3493
5146
  parseAbsoluteMobilePageEnvelope,
3494
5147
  parseAbsoluteMobileCompatibilityArtifact,
3495
5148
  parseAbsoluteMobileBuildPageMetadata,
5149
+ parseAbsoluteIosLogLine,
5150
+ parseAbsoluteIosHmrLog,
5151
+ pairAbsoluteRemoteMac,
3496
5152
  normalizeAbsoluteMobileConfig,
3497
5153
  navigateAbsoluteMobilePage,
5154
+ materializeAbsoluteRemoteMacAgent,
3498
5155
  materializeAbsoluteMobileCompatibilityBundle,
3499
5156
  materializeAbsoluteMobileAssociationFiles,
3500
5157
  materializeAbsoluteCapacitorWebBundle,
3501
5158
  matchesAbsoluteMobileRoutePattern,
3502
5159
  loadAbsoluteNativeReleasePublisher,
3503
5160
  loadAbsoluteMobileMaterializedBundle,
5161
+ listAbsoluteRemoteMacProfiles,
5162
+ isAbsoluteIosNativeRootInput,
5163
+ installAbsoluteRemoteMacAgent,
5164
+ inspectAbsoluteRemoteMac,
3504
5165
  inspectAbsoluteMobileRouteMetadata,
3505
5166
  hashAbsoluteMobilePropsSchema,
3506
5167
  getCurrentAbsoluteMobileProducerContext,
5168
+ getAbsoluteRemoteMacProfile,
3507
5169
  fingerprintAbsoluteIosNativeProject,
5170
+ fingerprintAbsoluteIosDevProject,
3508
5171
  finalizeAbsoluteMobilePage,
3509
5172
  finalizeAbsoluteMobileCompatibilityBuild,
3510
5173
  fetchAbsoluteMobilePage,
5174
+ createAbsoluteRemoteIosDevProject,
3511
5175
  createAbsoluteMobileUpgradeResponse,
3512
5176
  createAbsoluteMobileRouteMetadataPlugin,
3513
5177
  createAbsoluteMobilePageRequest,
@@ -3519,6 +5183,7 @@ export {
3519
5183
  createAbsoluteMobileBlobArtifactStore,
3520
5184
  createAbsoluteMobileAssociationPlugin,
3521
5185
  createAbsoluteMobileAssociationDocuments,
5186
+ createAbsoluteIosNativeWatcher,
3522
5187
  carryForwardAbsoluteMobileCompatibilityReleases,
3523
5188
  captureAbsoluteMobileRouteGraph,
3524
5189
  buildAbsoluteMobileCompatibilityRelease,
@@ -3527,10 +5192,14 @@ export {
3527
5192
  applyAbsoluteNativeDeepLinks,
3528
5193
  activateAbsoluteMobilePage,
3529
5194
  acceptsAbsoluteMobilePage,
5195
+ absoluteRemoteProjectSyncCommands,
5196
+ absoluteRemoteMacSshBase,
3530
5197
  MOBILE_PAGE_REQUEST_HEADERS,
3531
5198
  AbsoluteMobilePageProtocolError,
3532
5199
  APPLE_ASSOCIATION_PATH,
3533
5200
  ANDROID_ASSOCIATION_PATH,
5201
+ ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION,
5202
+ ABSOLUTE_REMOTE_MAC_EVENT_PREFIX,
3534
5203
  ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL,
3535
5204
  ABSOLUTE_MOBILE_ROUTE_DETAIL,
3536
5205
  ABSOLUTE_MOBILE_RETAINED_GENERATIONS,
@@ -3539,9 +5208,10 @@ export {
3539
5208
  ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
3540
5209
  ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT,
3541
5210
  ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
5211
+ ABSOLUTE_IOS_SIMULATOR_NAME,
3542
5212
  ABSOLUTE_IOS_RELEASE_FORMAT,
3543
5213
  ABSOLUTE_ANDROID_RELEASE_FORMAT
3544
5214
  };
3545
5215
 
3546
- //# debugId=8F6E00755BF98A2E64756E2164756E21
5216
+ //# debugId=5FE7B7589D1661C064756E2164756E21
3547
5217
  //# sourceMappingURL=index.js.map