@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.
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +6 -1
- package/dist/build.js.map +4 -4
- package/dist/cli/index.js +2855 -946
- package/dist/index.js +15 -1
- package/dist/index.js.map +5 -5
- package/dist/mobile/index.js +1841 -171
- package/dist/mobile/index.js.map +8 -3
- package/dist/mobile/remoteMacAgentEntry.js +29 -0
- package/dist/src/core/prepare.d.ts +6 -0
- package/dist/src/dev/clientManager.d.ts +2 -0
- package/dist/src/mobile/index.d.ts +4 -0
- package/dist/src/mobile/iosConformance.d.ts +15 -0
- package/dist/src/mobile/iosNativeWatcher.d.ts +19 -0
- package/dist/src/mobile/iosRelease.d.ts +2 -2
- package/dist/src/mobile/iosSimulatorController.d.ts +89 -0
- package/dist/src/mobile/remoteMacAgent.d.ts +2 -0
- package/dist/src/mobile/remoteMacAgentEntry.d.ts +1 -0
- package/dist/src/mobile/remoteMacProtocol.d.ts +114 -0
- package/dist/src/mobile/remoteMacWire.d.ts +2 -0
- package/dist/src/plugins/hmr.d.ts +3 -0
- package/package.json +8 -7
package/dist/mobile/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
947
|
+
var pathExists2 = async (path) => {
|
|
770
948
|
try {
|
|
771
|
-
await
|
|
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
|
|
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
|
|
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 ??
|
|
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
|
|
1196
|
+
var pathExists3 = async (path) => {
|
|
1019
1197
|
try {
|
|
1020
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1388
|
+
var pathExists4 = async (path) => {
|
|
1211
1389
|
try {
|
|
1212
|
-
await
|
|
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
|
|
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
|
|
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/
|
|
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
|
|
1703
|
+
access as access6,
|
|
1704
|
+
copyFile as copyFile4,
|
|
1467
1705
|
mkdir as mkdir5,
|
|
1468
|
-
readFile as
|
|
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
|
|
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
|
|
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 =
|
|
1485
|
-
const path =
|
|
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
|
|
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
|
|
1683
|
-
await
|
|
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
|
|
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 =
|
|
3335
|
+
const path = resolve7(root, OWNERSHIP_FILE);
|
|
1696
3336
|
let ownership;
|
|
1697
3337
|
try {
|
|
1698
|
-
ownership = JSON.parse(await
|
|
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
|
|
3352
|
+
await rename8(root, backup);
|
|
1713
3353
|
try {
|
|
1714
|
-
await
|
|
3354
|
+
await rename8(temporary, root);
|
|
1715
3355
|
} catch (error) {
|
|
1716
3356
|
if (hasCurrent)
|
|
1717
|
-
await
|
|
3357
|
+
await rename8(backup, root);
|
|
1718
3358
|
throw error;
|
|
1719
3359
|
}
|
|
1720
3360
|
if (hasCurrent)
|
|
1721
|
-
await
|
|
3361
|
+
await rm6(backup, { force: true, recursive: true });
|
|
1722
3362
|
};
|
|
1723
3363
|
var materializeHost = async (root, host, files) => {
|
|
1724
|
-
const directory =
|
|
1725
|
-
await
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
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) =>
|
|
3409
|
+
const written = temporaryPaths.map((path) => resolve7(root, path.slice(temporary.length + 1)));
|
|
1770
3410
|
return { root, written };
|
|
1771
3411
|
} catch (error) {
|
|
1772
|
-
await
|
|
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
|
|
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 (!
|
|
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
|
|
1835
|
-
import { join as
|
|
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
|
|
1840
|
-
import { readFile as
|
|
1841
|
-
import { join as
|
|
1842
|
-
var sha256 = (bytes) =>
|
|
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 =
|
|
1846
|
-
const resolvedAsset =
|
|
3485
|
+
const resolvedBuildDirectory = resolve8(buildDirectory);
|
|
3486
|
+
const resolvedAsset = resolve8(assetPath);
|
|
1847
3487
|
if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
|
|
1848
3488
|
return resolvedAsset;
|
|
1849
3489
|
}
|
|
1850
|
-
return
|
|
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
|
|
1859
|
-
const bundlePath = `/${
|
|
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
|
-
|
|
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
|
|
1953
|
-
mkdir as
|
|
3592
|
+
copyFile as copyFile5,
|
|
3593
|
+
mkdir as mkdir8,
|
|
1954
3594
|
mkdtemp as mkdtemp4,
|
|
1955
|
-
readFile as
|
|
1956
|
-
rename as
|
|
1957
|
-
rm as
|
|
1958
|
-
writeFile as
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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) =>
|
|
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 =
|
|
2417
|
-
const asset =
|
|
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 =
|
|
2426
|
-
await
|
|
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
|
|
2439
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
2493
|
-
await
|
|
2494
|
-
await
|
|
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
|
|
2507
|
-
const staging = await mkdtemp4(
|
|
4146
|
+
await mkdir8(dirname6(destination), { recursive: true });
|
|
4147
|
+
const staging = await mkdtemp4(join9(dirname6(destination), `.${basename2(destination)}.stage-`));
|
|
2508
4148
|
try {
|
|
2509
|
-
const pageDirectory =
|
|
2510
|
-
await
|
|
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
|
-
|
|
4167
|
+
writeFile9(join9(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
2528
4168
|
`),
|
|
2529
|
-
|
|
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
|
|
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
|
|
4181
|
+
import { createHash as createHash9 } from "crypto";
|
|
2542
4182
|
import {
|
|
2543
|
-
access as
|
|
2544
|
-
mkdir as
|
|
4183
|
+
access as access8,
|
|
4184
|
+
mkdir as mkdir9,
|
|
2545
4185
|
mkdtemp as mkdtemp5,
|
|
2546
|
-
readFile as
|
|
2547
|
-
rename as
|
|
2548
|
-
rm as
|
|
2549
|
-
writeFile as
|
|
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
|
|
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
|
|
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_${
|
|
4205
|
+
return `amb_${createHash9("sha256").update(identity).digest("hex")}`;
|
|
2566
4206
|
};
|
|
2567
4207
|
var parseBundleIndex = (value) => {
|
|
2568
|
-
if (!
|
|
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 =
|
|
2592
|
-
const producerPath =
|
|
2593
|
-
await
|
|
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
|
-
|
|
4235
|
+
writeFile10(join10(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
|
|
2596
4236
|
`),
|
|
2597
|
-
|
|
4237
|
+
writeFile10(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
|
|
2598
4238
|
]);
|
|
2599
4239
|
};
|
|
2600
4240
|
var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
|
|
2601
|
-
const destination =
|
|
4241
|
+
const destination = join10(bundlesRoot, bundleId);
|
|
2602
4242
|
try {
|
|
2603
|
-
await
|
|
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(
|
|
4249
|
+
const staging = await mkdtemp5(join10(bundlesRoot, ".stage-"));
|
|
2610
4250
|
try {
|
|
2611
4251
|
await Promise.all(releases.map((release) => writeRelease(staging, release)));
|
|
2612
|
-
await
|
|
4252
|
+
await rename10(staging, destination);
|
|
2613
4253
|
} catch (error) {
|
|
2614
|
-
await
|
|
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 (!
|
|
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 =
|
|
2643
|
-
const serialized = await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2678
|
-
const bundlesRoot =
|
|
2679
|
-
await
|
|
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 =
|
|
2689
|
-
const temporaryPointerPath =
|
|
2690
|
-
await
|
|
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
|
|
4332
|
+
await rename10(temporaryPointerPath, pointerPath);
|
|
2693
4333
|
return index;
|
|
2694
4334
|
};
|
|
2695
4335
|
var readAbsoluteMobileMaterializedReleases = async (root) => {
|
|
2696
|
-
const resolvedRoot =
|
|
4336
|
+
const resolvedRoot = resolvePath3(root);
|
|
2697
4337
|
try {
|
|
2698
|
-
const serialized = await
|
|
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 =
|
|
4341
|
+
const bundleRoot = join10(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
2702
4342
|
return Promise.all(index.releases.map(async (artifact) => {
|
|
2703
|
-
const producer = Bun.file(
|
|
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 =
|
|
4392
|
+
const buildDirectory = resolve10(options.buildDirectory);
|
|
2753
4393
|
const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
|
|
2754
|
-
const root =
|
|
4394
|
+
const root = join11(buildDirectory, ".absolutejs", "mobile-compatibility");
|
|
2755
4395
|
const [manifestSource, previous] = await Promise.all([
|
|
2756
|
-
|
|
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(
|
|
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:
|
|
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
|
|
2873
|
-
import { join as
|
|
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("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
2879
4519
|
var writeChangedFile = async (path, source) => {
|
|
2880
|
-
const current = await
|
|
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
|
|
2885
|
-
await
|
|
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 =
|
|
2931
|
-
const source = await
|
|
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 =
|
|
2957
|
-
const source = await
|
|
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 =
|
|
4620
|
+
const path = join12(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
2981
4621
|
let current = "";
|
|
2982
4622
|
try {
|
|
2983
|
-
current = await
|
|
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
|
|
2994
|
-
await
|
|
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 =
|
|
2999
|
-
const source = await
|
|
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
|
|
3038
|
-
import { isAbsolute as
|
|
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
|
|
3062
|
-
var isPublisher = (value) =>
|
|
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 =
|
|
3065
|
-
const path =
|
|
3066
|
-
const projectRelative =
|
|
3067
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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,
|
|
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(
|
|
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 =
|
|
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 =
|
|
3451
|
-
const 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(
|
|
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 =
|
|
3471
|
-
const 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(
|
|
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=
|
|
5216
|
+
//# debugId=5FE7B7589D1661C064756E2164756E21
|
|
3547
5217
|
//# sourceMappingURL=index.js.map
|