@absolutejs/absolute 0.20.0-beta.1 → 0.20.0-beta.3
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 +2631 -683
- package/dist/index.js +25 -1
- package/dist/index.js.map +6 -6
- package/dist/mobile/index.js +1740 -165
- package/dist/mobile/index.js.map +11 -6
- package/dist/src/core/prepare.d.ts +6 -0
- package/dist/src/dev/clientManager.d.ts +2 -0
- package/dist/src/mobile/androidEmulatorController.d.ts +2 -1
- package/dist/src/mobile/androidRelease.d.ts +4 -0
- package/dist/src/mobile/config.d.ts +1 -0
- package/dist/src/mobile/index.d.ts +5 -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 +61 -0
- package/dist/src/mobile/iosSimulatorController.d.ts +89 -0
- package/dist/src/mobile/releasePublisher.d.ts +130 -0
- package/dist/src/plugins/hmr.d.ts +3 -0
- package/dist/types/build.d.ts +4 -0
- package/package.json +8 -7
package/dist/mobile/index.js
CHANGED
|
@@ -615,9 +615,9 @@ var verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes
|
|
|
615
615
|
return release;
|
|
616
616
|
};
|
|
617
617
|
// src/mobile/androidRelease.ts
|
|
618
|
-
import { createHash as
|
|
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,
|
|
@@ -702,6 +880,7 @@ import {
|
|
|
702
880
|
rm as rm2,
|
|
703
881
|
writeFile as writeFile3
|
|
704
882
|
} from "fs/promises";
|
|
883
|
+
import { createHash as createHash3, randomUUID } from "crypto";
|
|
705
884
|
import {
|
|
706
885
|
dirname,
|
|
707
886
|
isAbsolute,
|
|
@@ -713,7 +892,7 @@ import {
|
|
|
713
892
|
} from "path";
|
|
714
893
|
|
|
715
894
|
// src/mobile/capacitorProject.ts
|
|
716
|
-
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";
|
|
717
896
|
import { relative, resolve } from "path";
|
|
718
897
|
var CONFIG_FILE = "capacitor.config.ts";
|
|
719
898
|
var portableRelative = (root, path) => relative(root, path).replaceAll("\\", "/");
|
|
@@ -735,7 +914,7 @@ export default config;
|
|
|
735
914
|
`;
|
|
736
915
|
var exists = async (path) => {
|
|
737
916
|
try {
|
|
738
|
-
await
|
|
917
|
+
await access2(path);
|
|
739
918
|
return true;
|
|
740
919
|
} catch {
|
|
741
920
|
return false;
|
|
@@ -762,10 +941,12 @@ var writeAbsoluteCapacitorConfig = async (config, options) => {
|
|
|
762
941
|
// src/mobile/androidEmulatorController.ts
|
|
763
942
|
init_getDurationString();
|
|
764
943
|
var HASH_RADIX = 16;
|
|
944
|
+
var EXECUTABLE_MODE_MASK = 73;
|
|
945
|
+
var NATIVE_PUBLIC_PATH_SEGMENTS = 5;
|
|
765
946
|
var CAPACITOR_PROJECT_DIRECTORY_PATTERN = /project\(['"](:[^'"]+)['"]\)\.projectDir\s*=\s*new File\(['"]([^'"]+)['"]\)/gu;
|
|
766
|
-
var
|
|
947
|
+
var pathExists2 = async (path) => {
|
|
767
948
|
try {
|
|
768
|
-
await
|
|
949
|
+
await access3(path);
|
|
769
950
|
return true;
|
|
770
951
|
} catch {
|
|
771
952
|
return false;
|
|
@@ -792,7 +973,7 @@ var runCommand = async (command, options = {}) => {
|
|
|
792
973
|
]);
|
|
793
974
|
return exitCode;
|
|
794
975
|
};
|
|
795
|
-
var
|
|
976
|
+
var captureCommand2 = (command, options = {}) => {
|
|
796
977
|
try {
|
|
797
978
|
const result = Bun.spawnSync(command, {
|
|
798
979
|
cwd: options.cwd,
|
|
@@ -826,6 +1007,75 @@ var throwIfAborted = (signal) => {
|
|
|
826
1007
|
return;
|
|
827
1008
|
throw new DOMException("Android development startup was cancelled.", "AbortError");
|
|
828
1009
|
};
|
|
1010
|
+
var nativeDependencySources = async (nativeDirectory) => {
|
|
1011
|
+
const settings = await readFile3(join3(nativeDirectory, "capacitor.settings.gradle"), "utf8");
|
|
1012
|
+
const pattern = new RegExp(CAPACITOR_PROJECT_DIRECTORY_PATTERN.source, CAPACITOR_PROJECT_DIRECTORY_PATTERN.flags);
|
|
1013
|
+
const dependencies = [...settings.matchAll(pattern)].map((match) => ({
|
|
1014
|
+
name: (match[1] ?? "").slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_"),
|
|
1015
|
+
source: resolve2(nativeDirectory, match[2] ?? "")
|
|
1016
|
+
}));
|
|
1017
|
+
if (dependencies.length === 0) {
|
|
1018
|
+
throw new Error("Capacitor Android settings did not declare any native dependencies.");
|
|
1019
|
+
}
|
|
1020
|
+
return { dependencies, settings };
|
|
1021
|
+
};
|
|
1022
|
+
var shouldIgnoreNativePath = (relativePath, ignorePublicBundle) => {
|
|
1023
|
+
const parts = relativePath.split(sep);
|
|
1024
|
+
if (parts.includes(".gradle") || parts.includes("build") || parts.includes(".absolutejs-dependencies")) {
|
|
1025
|
+
return true;
|
|
1026
|
+
}
|
|
1027
|
+
return ignorePublicBundle && parts.slice(0, NATIVE_PUBLIC_PATH_SEGMENTS).join("/") === "app/src/main/assets/public";
|
|
1028
|
+
};
|
|
1029
|
+
var collectNativePath = async (root, label, path, isDirectory, isFile, isSymbolicLink, ignorePublicBundle) => {
|
|
1030
|
+
const relativePath = relative2(root, path);
|
|
1031
|
+
if (shouldIgnoreNativePath(relativePath, ignorePublicBundle))
|
|
1032
|
+
return [];
|
|
1033
|
+
const identity = `${label}:${relativePath.split(sep).join("/")}\x00`;
|
|
1034
|
+
if (isDirectory) {
|
|
1035
|
+
return collectNativeDirectory(root, label, path, ignorePublicBundle);
|
|
1036
|
+
}
|
|
1037
|
+
if (isSymbolicLink) {
|
|
1038
|
+
return [`link\x00${identity}${await readlink(path)}\x00`];
|
|
1039
|
+
}
|
|
1040
|
+
if (!isFile)
|
|
1041
|
+
return [];
|
|
1042
|
+
const [metadata, contents] = await Promise.all([
|
|
1043
|
+
lstat(path),
|
|
1044
|
+
readFile3(path)
|
|
1045
|
+
]);
|
|
1046
|
+
const contentDigest = createHash3("sha256").update(contents).digest("hex");
|
|
1047
|
+
return [
|
|
1048
|
+
`file\x00${identity}${metadata.mode & EXECUTABLE_MODE_MASK}\x00${contentDigest}\x00`
|
|
1049
|
+
];
|
|
1050
|
+
};
|
|
1051
|
+
var collectNativeDirectory = async (root, label, directory, ignorePublicBundle) => {
|
|
1052
|
+
const entries = await readdir2(directory, { withFileTypes: true });
|
|
1053
|
+
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
1054
|
+
const records = await Promise.all(entries.map((entry) => collectNativePath(root, label, join3(directory, entry.name), entry.isDirectory(), entry.isFile(), entry.isSymbolicLink(), ignorePublicBundle)));
|
|
1055
|
+
return records.flat();
|
|
1056
|
+
};
|
|
1057
|
+
var hashNativeTree = async (root, label, ignorePublicBundle) => {
|
|
1058
|
+
const resolvedRoot = await realpath(root);
|
|
1059
|
+
const records = await collectNativeDirectory(resolvedRoot, label, resolvedRoot, ignorePublicBundle);
|
|
1060
|
+
return createHash3("sha256").update(records.join("")).digest("hex");
|
|
1061
|
+
};
|
|
1062
|
+
var fingerprintAbsoluteAndroidNativeProject = async (project) => {
|
|
1063
|
+
const { dependencies } = await nativeDependencySources(project.nativeDirectory);
|
|
1064
|
+
const roots = [
|
|
1065
|
+
{
|
|
1066
|
+
ignorePublicBundle: true,
|
|
1067
|
+
label: "android",
|
|
1068
|
+
source: project.nativeDirectory
|
|
1069
|
+
},
|
|
1070
|
+
...dependencies.sort((left, right) => left.name.localeCompare(right.name)).map((dependency) => ({
|
|
1071
|
+
ignorePublicBundle: false,
|
|
1072
|
+
label: `dependency:${dependency.name}`,
|
|
1073
|
+
source: dependency.source
|
|
1074
|
+
}))
|
|
1075
|
+
];
|
|
1076
|
+
const treeDigests = await Promise.all(roots.map((root) => hashNativeTree(root.source, root.label, root.ignorePublicBundle)));
|
|
1077
|
+
return createHash3("sha256").update(treeDigests.join("\x00")).digest("hex");
|
|
1078
|
+
};
|
|
829
1079
|
var windowsPathFromWsl = (path, capture) => {
|
|
830
1080
|
const result = capture(["wslpath", "-w", path]);
|
|
831
1081
|
if (result.exitCode !== 0 || !result.stdout.trim()) {
|
|
@@ -851,12 +1101,13 @@ var mirroredCapacitorDependencies = async (project, capture) => {
|
|
|
851
1101
|
}
|
|
852
1102
|
return { dependencies, rewrittenSettings };
|
|
853
1103
|
};
|
|
854
|
-
var encodedWindowsGradleCommand = (windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task) => {
|
|
1104
|
+
var encodedWindowsGradleCommand = (windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task, gradleArguments) => {
|
|
855
1105
|
const sourceDirectory = Buffer.from(windowsSource, "utf8").toString("base64");
|
|
856
1106
|
const buildDirectory = Buffer.from(windowsDirectory, "utf8").toString("base64");
|
|
857
1107
|
const androidRoot = Buffer.from(windowsAndroidRoot, "utf8").toString("base64");
|
|
858
1108
|
const dependencyData = Buffer.from(JSON.stringify(dependencies), "utf8").toString("base64");
|
|
859
1109
|
const settingsData = Buffer.from(rewrittenSettings, "utf8").toString("base64");
|
|
1110
|
+
const argumentsData = Buffer.from(JSON.stringify(gradleArguments), "utf8").toString("base64");
|
|
860
1111
|
const source = [
|
|
861
1112
|
"$ErrorActionPreference = 'Stop'",
|
|
862
1113
|
`$source = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${sourceDirectory}'))`,
|
|
@@ -864,6 +1115,7 @@ var encodedWindowsGradleCommand = (windowsSource, windowsDirectory, windowsAndro
|
|
|
864
1115
|
`$androidHome = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${androidRoot}'))`,
|
|
865
1116
|
`$dependencies = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${dependencyData}')) | ConvertFrom-Json`,
|
|
866
1117
|
`$settings = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${settingsData}'))`,
|
|
1118
|
+
`$gradleArguments = @([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${argumentsData}')) | ConvertFrom-Json)`,
|
|
867
1119
|
"$env:ANDROID_HOME = $androidHome",
|
|
868
1120
|
"$env:ANDROID_SDK_ROOT = $androidHome",
|
|
869
1121
|
"New-Item -ItemType Directory -Force -Path $directory | Out-Null",
|
|
@@ -873,7 +1125,7 @@ var encodedWindowsGradleCommand = (windowsSource, windowsDirectory, windowsAndro
|
|
|
873
1125
|
"foreach ($dependency in @($dependencies)) { $target = Join-Path $directory ('.absolutejs-dependencies\\' + $dependency.name); New-Item -ItemType Directory -Force -Path $target | Out-Null; & robocopy.exe $dependency.windowsSource $target /MIR /XD .gradle build /NFL /NDL /NJH /NJS /NP; if ($LASTEXITCODE -ge 8) { exit $LASTEXITCODE } }",
|
|
874
1126
|
"[IO.File]::WriteAllText((Join-Path $directory 'capacitor.settings.gradle'), $settings)",
|
|
875
1127
|
"$wrapper = Join-Path $directory 'gradlew.bat'",
|
|
876
|
-
`& $wrapper --no-daemon --console=plain -p $directory ${task}`,
|
|
1128
|
+
`& $wrapper --no-daemon --console=plain -p $directory @gradleArguments ${task}`,
|
|
877
1129
|
"if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }",
|
|
878
1130
|
"exit 0"
|
|
879
1131
|
].join("; ");
|
|
@@ -891,16 +1143,17 @@ var gradleArtifactPath = (nativeDirectory, task, windows = false) => {
|
|
|
891
1143
|
};
|
|
892
1144
|
var resolveGradleArtifactPath = async (nativeDirectory, task) => {
|
|
893
1145
|
const primary = gradleArtifactPath(nativeDirectory, task);
|
|
894
|
-
if (task !== "assembleRelease" || await
|
|
1146
|
+
if (task !== "assembleRelease" || await pathExists2(primary)) {
|
|
895
1147
|
return primary;
|
|
896
1148
|
}
|
|
897
1149
|
return join3(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
|
|
898
1150
|
};
|
|
899
1151
|
var buildAbsoluteAndroidGradleArtifact = async (options) => {
|
|
900
1152
|
const { project, task } = options;
|
|
901
|
-
const capture = options.capture ??
|
|
1153
|
+
const capture = options.capture ?? captureCommand2;
|
|
902
1154
|
const run = options.run ?? runCommand;
|
|
903
1155
|
const env = options.env ?? process.env;
|
|
1156
|
+
const gradleArguments = options.gradleArguments ?? [];
|
|
904
1157
|
if (project.host === "wsl") {
|
|
905
1158
|
const windowsSource = windowsPathFromWsl(project.nativeDirectory, capture);
|
|
906
1159
|
const buildId = Bun.hash(project.projectRoot).toString(HASH_RADIX);
|
|
@@ -912,7 +1165,7 @@ var buildAbsoluteAndroidGradleArtifact = async (options) => {
|
|
|
912
1165
|
"powershell.exe",
|
|
913
1166
|
"-NoProfile",
|
|
914
1167
|
"-EncodedCommand",
|
|
915
|
-
encodedWindowsGradleCommand(windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task)
|
|
1168
|
+
encodedWindowsGradleCommand(windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task, gradleArguments)
|
|
916
1169
|
], "Android Gradle build", run, { env, signal: options.signal });
|
|
917
1170
|
const artifactPath2 = await resolveGradleArtifactPath(managedBuildDirectory, task);
|
|
918
1171
|
const unsigned = artifactPath2.endsWith("-unsigned.apk");
|
|
@@ -922,7 +1175,7 @@ var buildAbsoluteAndroidGradleArtifact = async (options) => {
|
|
|
922
1175
|
};
|
|
923
1176
|
}
|
|
924
1177
|
const wrapper = project.host === "windows" ? "gradlew.bat" : "./gradlew";
|
|
925
|
-
await requireSuccess([wrapper, "--no-daemon", "--console=plain", task], "Android Gradle build", run, { cwd: project.nativeDirectory, env, signal: options.signal });
|
|
1178
|
+
await requireSuccess([wrapper, "--no-daemon", "--console=plain", ...gradleArguments, task], "Android Gradle build", run, { cwd: project.nativeDirectory, env, signal: options.signal });
|
|
926
1179
|
const artifactPath = await resolveGradleArtifactPath(project.nativeDirectory, task);
|
|
927
1180
|
return { artifactPath, installPath: artifactPath };
|
|
928
1181
|
};
|
|
@@ -940,9 +1193,9 @@ var requireManifest = (value) => {
|
|
|
940
1193
|
runtime: value.runtime
|
|
941
1194
|
};
|
|
942
1195
|
};
|
|
943
|
-
var
|
|
1196
|
+
var pathExists3 = async (path) => {
|
|
944
1197
|
try {
|
|
945
|
-
await
|
|
1198
|
+
await access4(path);
|
|
946
1199
|
return true;
|
|
947
1200
|
} catch {
|
|
948
1201
|
return false;
|
|
@@ -983,7 +1236,7 @@ var verifyAabSignature = (artifactPath, capture, jarsigner) => {
|
|
|
983
1236
|
]);
|
|
984
1237
|
return result.exitCode === 0 && /jar verified/iu.test(result.stdout);
|
|
985
1238
|
};
|
|
986
|
-
var sha256File = async (path) =>
|
|
1239
|
+
var sha256File = async (path) => createHash4("sha256").update(await readFile4(path)).digest("hex");
|
|
987
1240
|
var safeOutputDirectory = (projectRoot, requested) => {
|
|
988
1241
|
const root = resolve3(projectRoot);
|
|
989
1242
|
const output = resolve3(root, requested ?? ".absolutejs/mobile/releases/android");
|
|
@@ -997,7 +1250,7 @@ var installRelease = async (artifactPath, metadata, outputRoot) => {
|
|
|
997
1250
|
const releaseRoot = join4(outputRoot, metadata.releaseId);
|
|
998
1251
|
const artifactName = "app-release.aab";
|
|
999
1252
|
const destination = join4(releaseRoot, artifactName);
|
|
1000
|
-
if (await
|
|
1253
|
+
if (await pathExists3(releaseRoot)) {
|
|
1001
1254
|
const existing = requireManifestIdentity(JSON.parse(await readFile4(join4(releaseRoot, "release.json"), "utf8")), metadata);
|
|
1002
1255
|
const [installedBytes, installedSha256] = await Promise.all([
|
|
1003
1256
|
stat(destination).then(({ size }) => size),
|
|
@@ -1037,12 +1290,32 @@ var requireManifestIdentity = (value, expected) => {
|
|
|
1037
1290
|
return { ...expected, artifact };
|
|
1038
1291
|
};
|
|
1039
1292
|
var buildAbsoluteAndroidRelease = async (options) => {
|
|
1293
|
+
if (options.versionCode !== undefined && options.prepareVersionCode) {
|
|
1294
|
+
throw new TypeError("Android release versionCode and prepareVersionCode cannot be combined.");
|
|
1295
|
+
}
|
|
1296
|
+
if (options.versionCode !== undefined && (!Number.isSafeInteger(options.versionCode) || options.versionCode < 1 || options.versionCode > 2100000000)) {
|
|
1297
|
+
throw new TypeError("Android versionCode must be an integer from 1 through 2100000000.");
|
|
1298
|
+
}
|
|
1040
1299
|
const projectRoot = resolve3(options.projectRoot);
|
|
1041
1300
|
const host = options.host ?? detectAbsoluteMobileHost();
|
|
1042
1301
|
const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host);
|
|
1043
1302
|
const nativeDirectory = join4(options.config.nativeProjectDirectory, "android");
|
|
1303
|
+
const manifest = requireManifest(JSON.parse(await readFile4(join4(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
1304
|
+
if (manifest.appId !== options.config.appId) {
|
|
1305
|
+
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
1306
|
+
}
|
|
1307
|
+
let { versionCode } = options;
|
|
1308
|
+
if (options.prepareVersionCode) {
|
|
1309
|
+
const nativeFingerprint = await fingerprintAbsoluteAndroidNativeProject({ nativeDirectory });
|
|
1310
|
+
const buildIdentity = createHash4("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}`).digest("hex");
|
|
1311
|
+
versionCode = await options.prepareVersionCode(buildIdentity);
|
|
1312
|
+
}
|
|
1313
|
+
if (versionCode !== undefined && (!Number.isSafeInteger(versionCode) || versionCode < 1 || versionCode > 2100000000)) {
|
|
1314
|
+
throw new TypeError("Android versionCode must be an integer from 1 through 2100000000.");
|
|
1315
|
+
}
|
|
1044
1316
|
const { artifactPath } = await buildAbsoluteAndroidGradleArtifact({
|
|
1045
1317
|
capture: options.capture,
|
|
1318
|
+
gradleArguments: versionCode === undefined ? [] : [`-Pandroid.injected.version.code=${versionCode}`],
|
|
1046
1319
|
project: {
|
|
1047
1320
|
androidRoot,
|
|
1048
1321
|
config: options.config,
|
|
@@ -1053,7 +1326,7 @@ var buildAbsoluteAndroidRelease = async (options) => {
|
|
|
1053
1326
|
run: options.run,
|
|
1054
1327
|
task: "bundleRelease"
|
|
1055
1328
|
});
|
|
1056
|
-
if (!await
|
|
1329
|
+
if (!await pathExists3(artifactPath)) {
|
|
1057
1330
|
throw new TypeError(`Android Gradle did not produce the expected App Bundle: ${artifactPath}`);
|
|
1058
1331
|
}
|
|
1059
1332
|
const capture = options.capture ?? defaultCapture;
|
|
@@ -1064,10 +1337,6 @@ var buildAbsoluteAndroidRelease = async (options) => {
|
|
|
1064
1337
|
if (!signed && !options.allowUnsigned) {
|
|
1065
1338
|
throw new TypeError("Android Gradle produced an unsigned App Bundle. Configure the release signingConfig in the source-owned Android project (prefer external Gradle properties), or pass --unsigned only for a non-publishable build.");
|
|
1066
1339
|
}
|
|
1067
|
-
const manifest = requireManifest(JSON.parse(await readFile4(join4(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
1068
|
-
if (manifest.appId !== options.config.appId) {
|
|
1069
|
-
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
1070
|
-
}
|
|
1071
1340
|
const [bytes, sha256] = await Promise.all([
|
|
1072
1341
|
stat(artifactPath).then(({ size }) => size),
|
|
1073
1342
|
sha256File(artifactPath)
|
|
@@ -1084,32 +1353,1208 @@ var buildAbsoluteAndroidRelease = async (options) => {
|
|
|
1084
1353
|
runtime: manifest.runtime,
|
|
1085
1354
|
sha256,
|
|
1086
1355
|
signed: signed === true,
|
|
1087
|
-
type: "aab"
|
|
1356
|
+
type: "aab",
|
|
1357
|
+
...versionCode === undefined ? {} : { versionCode }
|
|
1088
1358
|
};
|
|
1089
1359
|
return installRelease(artifactPath, metadata, safeOutputDirectory(projectRoot, options.outputDirectory));
|
|
1090
1360
|
};
|
|
1091
|
-
// src/mobile/
|
|
1361
|
+
// src/mobile/iosRelease.ts
|
|
1362
|
+
import { createHash as createHash5 } from "crypto";
|
|
1092
1363
|
import {
|
|
1093
|
-
access as
|
|
1364
|
+
access as access5,
|
|
1365
|
+
copyFile as copyFile3,
|
|
1094
1366
|
mkdir as mkdir4,
|
|
1367
|
+
mkdtemp as mkdtemp3,
|
|
1368
|
+
readdir as readdir3,
|
|
1095
1369
|
readFile as readFile5,
|
|
1096
1370
|
rename as rename5,
|
|
1097
1371
|
rm as rm4,
|
|
1372
|
+
stat as stat2,
|
|
1098
1373
|
writeFile as writeFile5
|
|
1099
1374
|
} from "fs/promises";
|
|
1100
|
-
import { resolve as
|
|
1375
|
+
import { dirname as dirname3, isAbsolute as isAbsolute3, join as join5, relative as relative4, resolve as resolve4, sep as sep3 } from "path";
|
|
1376
|
+
var ABSOLUTE_IOS_RELEASE_FORMAT = 1;
|
|
1377
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1378
|
+
var requireManifest2 = (value) => {
|
|
1379
|
+
if (!isRecord2(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
|
|
1380
|
+
throw new TypeError("Invalid embedded AbsoluteJS mobile manifest.");
|
|
1381
|
+
}
|
|
1382
|
+
return {
|
|
1383
|
+
appBuild: value.appBuild,
|
|
1384
|
+
appId: value.appId,
|
|
1385
|
+
runtime: value.runtime
|
|
1386
|
+
};
|
|
1387
|
+
};
|
|
1388
|
+
var pathExists4 = async (path) => {
|
|
1389
|
+
try {
|
|
1390
|
+
await access5(path);
|
|
1391
|
+
return true;
|
|
1392
|
+
} catch {
|
|
1393
|
+
return false;
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
var defaultRun = async (command, options = {}) => {
|
|
1397
|
+
const process2 = Bun.spawn(command, {
|
|
1398
|
+
cwd: options.cwd,
|
|
1399
|
+
env: options.env,
|
|
1400
|
+
stderr: "inherit",
|
|
1401
|
+
stdin: "inherit",
|
|
1402
|
+
stdout: "inherit"
|
|
1403
|
+
});
|
|
1404
|
+
return process2.exited;
|
|
1405
|
+
};
|
|
1406
|
+
var defaultCapture2 = (command, options = {}) => {
|
|
1407
|
+
try {
|
|
1408
|
+
const result = Bun.spawnSync(command, {
|
|
1409
|
+
cwd: options.cwd,
|
|
1410
|
+
env: options.env,
|
|
1411
|
+
stderr: "pipe",
|
|
1412
|
+
stdin: "ignore",
|
|
1413
|
+
stdout: "pipe"
|
|
1414
|
+
});
|
|
1415
|
+
return {
|
|
1416
|
+
exitCode: result.exitCode,
|
|
1417
|
+
stderr: result.stderr.toString(),
|
|
1418
|
+
stdout: result.stdout.toString()
|
|
1419
|
+
};
|
|
1420
|
+
} catch (error) {
|
|
1421
|
+
return {
|
|
1422
|
+
exitCode: 1,
|
|
1423
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
1424
|
+
stdout: ""
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
var ignoredFingerprintDirectories = new Set([
|
|
1429
|
+
"Pods",
|
|
1430
|
+
"DerivedData",
|
|
1431
|
+
"build",
|
|
1432
|
+
"xcuserdata"
|
|
1433
|
+
]);
|
|
1434
|
+
var fingerprintFiles = async (root, current = root) => {
|
|
1435
|
+
const entries = await readdir3(current, { withFileTypes: true });
|
|
1436
|
+
const nested = await Promise.all(entries.sort((left, right) => left.name.localeCompare(right.name)).map(async (entry) => {
|
|
1437
|
+
const path = join5(current, entry.name);
|
|
1438
|
+
const projectRelative = relative4(root, path).replaceAll("\\", "/");
|
|
1439
|
+
const ignored = entry.isDirectory() && (ignoredFingerprintDirectories.has(entry.name) || projectRelative === "App/App/public");
|
|
1440
|
+
if (ignored)
|
|
1441
|
+
return [];
|
|
1442
|
+
if (entry.isDirectory())
|
|
1443
|
+
return fingerprintFiles(root, path);
|
|
1444
|
+
return entry.isFile() ? [path] : [];
|
|
1445
|
+
}));
|
|
1446
|
+
return nested.flat();
|
|
1447
|
+
};
|
|
1448
|
+
var fingerprintAbsoluteIosNativeProject = async (nativeDirectory) => {
|
|
1449
|
+
const hasher = createHash5("sha256");
|
|
1450
|
+
const files = await fingerprintFiles(nativeDirectory);
|
|
1451
|
+
const contents = await Promise.all(files.map((file) => readFile5(file)));
|
|
1452
|
+
files.forEach((file, index) => {
|
|
1453
|
+
hasher.update(relative4(nativeDirectory, file).replaceAll("\\", "/"));
|
|
1454
|
+
hasher.update("\x00");
|
|
1455
|
+
hasher.update(contents[index] ?? new Uint8Array);
|
|
1456
|
+
hasher.update("\x00");
|
|
1457
|
+
});
|
|
1458
|
+
return hasher.digest("hex");
|
|
1459
|
+
};
|
|
1460
|
+
var safeOutputDirectory2 = (projectRoot, requested) => {
|
|
1461
|
+
const root = resolve4(projectRoot);
|
|
1462
|
+
const output = resolve4(root, requested ?? ".absolutejs/mobile/releases/ios");
|
|
1463
|
+
const projectRelative = relative4(root, output);
|
|
1464
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep3}`) || isAbsolute3(projectRelative)) {
|
|
1465
|
+
throw new TypeError("mobile build --outdir must remain inside the project.");
|
|
1466
|
+
}
|
|
1467
|
+
return output;
|
|
1468
|
+
};
|
|
1469
|
+
var sha256File2 = async (path) => createHash5("sha256").update(await readFile5(path)).digest("hex");
|
|
1470
|
+
var findByExtension = async (root, extension) => {
|
|
1471
|
+
if (!await pathExists4(root))
|
|
1472
|
+
return;
|
|
1473
|
+
const entries = await readdir3(root, { withFileTypes: true });
|
|
1474
|
+
const matches = await Promise.all(entries.map(async (entry) => {
|
|
1475
|
+
const path = join5(root, entry.name);
|
|
1476
|
+
if (entry.isDirectory() && entry.name.endsWith(extension))
|
|
1477
|
+
return path;
|
|
1478
|
+
if (entry.isFile() && entry.name.endsWith(extension))
|
|
1479
|
+
return path;
|
|
1480
|
+
return entry.isDirectory() ? findByExtension(path, extension) : undefined;
|
|
1481
|
+
}));
|
|
1482
|
+
return matches.find((match) => match !== undefined);
|
|
1483
|
+
};
|
|
1484
|
+
var exportOptions = () => `<?xml version="1.0" encoding="UTF-8"?>
|
|
1485
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1486
|
+
<plist version="1.0"><dict>
|
|
1487
|
+
<key>destination</key><string>export</string>
|
|
1488
|
+
<key>manageAppVersionAndBuildNumber</key><false/>
|
|
1489
|
+
<key>method</key><string>app-store-connect</string>
|
|
1490
|
+
<key>signingStyle</key><string>automatic</string>
|
|
1491
|
+
<key>stripSwiftSymbols</key><true/>
|
|
1492
|
+
<key>uploadSymbols</key><true/>
|
|
1493
|
+
</dict></plist>
|
|
1494
|
+
`;
|
|
1495
|
+
var requireBuildNumber = (value) => {
|
|
1496
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 1))
|
|
1497
|
+
throw new TypeError("iOS build number must be a positive integer.");
|
|
1498
|
+
return value;
|
|
1499
|
+
};
|
|
1500
|
+
var installRelease2 = async (artifactPath, metadata, outputRoot) => {
|
|
1501
|
+
const releaseRoot = join5(outputRoot, metadata.releaseId);
|
|
1502
|
+
const destination = join5(releaseRoot, "App.ipa");
|
|
1503
|
+
if (await pathExists4(releaseRoot)) {
|
|
1504
|
+
const value = JSON.parse(await readFile5(join5(releaseRoot, "release.json"), "utf8"));
|
|
1505
|
+
if (!isRecord2(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
|
|
1506
|
+
throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
|
|
1507
|
+
}
|
|
1508
|
+
const [bytes, sha256] = await Promise.all([
|
|
1509
|
+
stat2(destination).then(({ size }) => size),
|
|
1510
|
+
sha256File2(destination)
|
|
1511
|
+
]);
|
|
1512
|
+
if (bytes !== metadata.bytes || sha256 !== metadata.sha256)
|
|
1513
|
+
throw new TypeError(`Immutable iOS release ${metadata.releaseId} artifact is missing or modified.`);
|
|
1514
|
+
return {
|
|
1515
|
+
artifactPath: destination,
|
|
1516
|
+
metadata: {
|
|
1517
|
+
...metadata,
|
|
1518
|
+
artifact: "App.ipa"
|
|
1519
|
+
},
|
|
1520
|
+
releaseRoot
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
await mkdir4(dirname3(releaseRoot), { recursive: true });
|
|
1524
|
+
const staging = await mkdtemp3(join5(dirname3(releaseRoot), ".ios-stage-"));
|
|
1525
|
+
try {
|
|
1526
|
+
await copyFile3(artifactPath, join5(staging, "App.ipa"));
|
|
1527
|
+
const complete = {
|
|
1528
|
+
...metadata,
|
|
1529
|
+
artifact: "App.ipa"
|
|
1530
|
+
};
|
|
1531
|
+
await writeFile5(join5(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
|
|
1532
|
+
`, { flag: "wx" });
|
|
1533
|
+
await rename5(staging, releaseRoot);
|
|
1534
|
+
return { artifactPath: destination, metadata: complete, releaseRoot };
|
|
1535
|
+
} finally {
|
|
1536
|
+
await rm4(staging, { force: true, recursive: true }).catch(() => {
|
|
1537
|
+
return;
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
var buildAbsoluteIosRelease = async (options) => {
|
|
1542
|
+
if (options.buildNumber !== undefined && options.prepareBuildNumber)
|
|
1543
|
+
throw new TypeError("iOS release buildNumber and prepareBuildNumber cannot be combined.");
|
|
1544
|
+
if ((options.host ?? detectAbsoluteMobileHost()) !== "macos")
|
|
1545
|
+
throw new TypeError("iOS release builds require macOS and Xcode.");
|
|
1546
|
+
const marketingVersion = options.config.iosVersion;
|
|
1547
|
+
if (!marketingVersion)
|
|
1548
|
+
throw new TypeError("iOS release builds require mobile.ios.version in absolutejs.config.ts.");
|
|
1549
|
+
const manifest = requireManifest2(JSON.parse(await readFile5(join5(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
1550
|
+
if (manifest.appId !== options.config.appId)
|
|
1551
|
+
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
1552
|
+
const nativeDirectory = join5(options.config.nativeProjectDirectory, "ios");
|
|
1553
|
+
let buildNumber = requireBuildNumber(options.buildNumber);
|
|
1554
|
+
if (options.prepareBuildNumber) {
|
|
1555
|
+
const nativeFingerprint = await fingerprintAbsoluteIosNativeProject(nativeDirectory);
|
|
1556
|
+
const buildIdentity = createHash5("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}\x00${marketingVersion}`).digest("hex");
|
|
1557
|
+
buildNumber = requireBuildNumber(await options.prepareBuildNumber(buildIdentity));
|
|
1558
|
+
}
|
|
1559
|
+
const stagingParent = resolve4(options.projectRoot, ".absolutejs/mobile");
|
|
1560
|
+
await mkdir4(stagingParent, { recursive: true });
|
|
1561
|
+
const staging = await mkdtemp3(join5(stagingParent, ".ios-build-"));
|
|
1562
|
+
const archivePath = join5(staging, "App.xcarchive");
|
|
1563
|
+
const exportPath = join5(staging, "export");
|
|
1564
|
+
const exportPlist = join5(staging, "ExportOptions.plist");
|
|
1565
|
+
await mkdir4(exportPath, { recursive: true });
|
|
1566
|
+
await writeFile5(exportPlist, exportOptions());
|
|
1567
|
+
const run = options.run ?? defaultRun;
|
|
1568
|
+
try {
|
|
1569
|
+
const versionArguments = [
|
|
1570
|
+
`MARKETING_VERSION=${marketingVersion}`,
|
|
1571
|
+
...buildNumber === undefined ? [] : [`CURRENT_PROJECT_VERSION=${buildNumber}`]
|
|
1572
|
+
];
|
|
1573
|
+
const archiveExit = await run([
|
|
1574
|
+
"xcodebuild",
|
|
1575
|
+
"-workspace",
|
|
1576
|
+
join5(nativeDirectory, "App", "App.xcworkspace"),
|
|
1577
|
+
"-scheme",
|
|
1578
|
+
"App",
|
|
1579
|
+
"-configuration",
|
|
1580
|
+
"Release",
|
|
1581
|
+
"-destination",
|
|
1582
|
+
"generic/platform=iOS",
|
|
1583
|
+
"-archivePath",
|
|
1584
|
+
archivePath,
|
|
1585
|
+
...versionArguments,
|
|
1586
|
+
"archive"
|
|
1587
|
+
], { cwd: nativeDirectory });
|
|
1588
|
+
if (archiveExit !== 0)
|
|
1589
|
+
throw new TypeError("Xcode failed to archive the iOS app.");
|
|
1590
|
+
const archivedApp = await findByExtension(join5(archivePath, "Products", "Applications"), ".app");
|
|
1591
|
+
const capture = options.capture ?? defaultCapture2;
|
|
1592
|
+
const signed = archivedApp ? capture([
|
|
1593
|
+
"codesign",
|
|
1594
|
+
"--verify",
|
|
1595
|
+
"--deep",
|
|
1596
|
+
"--strict",
|
|
1597
|
+
archivedApp
|
|
1598
|
+
]).exitCode === 0 : false;
|
|
1599
|
+
if (!signed && !options.allowUnsigned)
|
|
1600
|
+
throw new TypeError("Xcode produced an unsigned iOS archive. Configure signing in the source-owned Xcode project, or pass --unsigned only for a non-publishable build.");
|
|
1601
|
+
const exportExit = await run([
|
|
1602
|
+
"xcodebuild",
|
|
1603
|
+
"-exportArchive",
|
|
1604
|
+
"-archivePath",
|
|
1605
|
+
archivePath,
|
|
1606
|
+
"-exportPath",
|
|
1607
|
+
exportPath,
|
|
1608
|
+
"-exportOptionsPlist",
|
|
1609
|
+
exportPlist
|
|
1610
|
+
], { cwd: nativeDirectory });
|
|
1611
|
+
if (exportExit !== 0)
|
|
1612
|
+
throw new TypeError("Xcode failed to export the App Store IPA.");
|
|
1613
|
+
const artifactPath = await findByExtension(exportPath, ".ipa");
|
|
1614
|
+
if (!artifactPath)
|
|
1615
|
+
throw new TypeError("Xcode did not produce an exported IPA.");
|
|
1616
|
+
const [bytes, sha256] = await Promise.all([
|
|
1617
|
+
stat2(artifactPath).then(({ size }) => size),
|
|
1618
|
+
sha256File2(artifactPath)
|
|
1619
|
+
]);
|
|
1620
|
+
const releaseId = `amobile_ios_${sha256}`;
|
|
1621
|
+
return await installRelease2(artifactPath, {
|
|
1622
|
+
appBuild: manifest.appBuild,
|
|
1623
|
+
appId: manifest.appId,
|
|
1624
|
+
...buildNumber === undefined ? {} : { buildNumber },
|
|
1625
|
+
bytes,
|
|
1626
|
+
engine: "capacitor",
|
|
1627
|
+
format: 1,
|
|
1628
|
+
marketingVersion,
|
|
1629
|
+
platform: "ios",
|
|
1630
|
+
releaseId,
|
|
1631
|
+
runtime: manifest.runtime,
|
|
1632
|
+
sha256,
|
|
1633
|
+
signed,
|
|
1634
|
+
type: "ipa"
|
|
1635
|
+
}, safeOutputDirectory2(options.projectRoot, options.outputDirectory));
|
|
1636
|
+
} finally {
|
|
1637
|
+
await rm4(staging, { force: true, recursive: true }).catch(() => {
|
|
1638
|
+
return;
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
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";
|
|
1702
|
+
import {
|
|
1703
|
+
access as access6,
|
|
1704
|
+
copyFile as copyFile4,
|
|
1705
|
+
mkdir as mkdir5,
|
|
1706
|
+
readFile as readFile7,
|
|
1707
|
+
rename as rename6,
|
|
1708
|
+
rm as rm5,
|
|
1709
|
+
writeFile as writeFile6
|
|
1710
|
+
} from "fs/promises";
|
|
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/associationFiles.ts
|
|
2537
|
+
import {
|
|
2538
|
+
access as access7,
|
|
2539
|
+
mkdir as mkdir6,
|
|
2540
|
+
readFile as readFile8,
|
|
2541
|
+
rename as rename7,
|
|
2542
|
+
rm as rm6,
|
|
2543
|
+
writeFile as writeFile7
|
|
2544
|
+
} from "fs/promises";
|
|
2545
|
+
import { resolve as resolve7 } from "path";
|
|
1101
2546
|
import { Elysia } from "elysia";
|
|
1102
2547
|
|
|
1103
2548
|
// src/mobile/config.ts
|
|
1104
|
-
import { resolve as
|
|
2549
|
+
import { resolve as resolve6 } from "path";
|
|
1105
2550
|
var APP_ID_PATTERN = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
1106
2551
|
var SCHEME_PATTERN = /^[a-z][a-z0-9+.-]*$/;
|
|
1107
2552
|
var APPLE_APP_ID_PREFIX_PATTERN = /^[A-Z0-9]{10}$/;
|
|
1108
2553
|
var CERTIFICATE_FINGERPRINT_PATTERN = /^[0-9A-F]{64}$/;
|
|
1109
2554
|
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])?))*$/;
|
|
1110
2555
|
var resolveProjectPath = (projectRoot, value, field) => {
|
|
1111
|
-
const root =
|
|
1112
|
-
const path =
|
|
2556
|
+
const root = resolve6(projectRoot);
|
|
2557
|
+
const path = resolve6(root, value);
|
|
1113
2558
|
if (path !== root && !path.startsWith(`${root}/`)) {
|
|
1114
2559
|
throw new TypeError(`${field} must remain inside the project root.`);
|
|
1115
2560
|
}
|
|
@@ -1172,6 +2617,15 @@ var normalizeAppleAppIdPrefix = (value) => {
|
|
|
1172
2617
|
}
|
|
1173
2618
|
return normalized;
|
|
1174
2619
|
};
|
|
2620
|
+
var normalizeIosVersion = (value) => {
|
|
2621
|
+
if (value === undefined)
|
|
2622
|
+
return;
|
|
2623
|
+
const normalized = requireText(value, "mobile.ios.version");
|
|
2624
|
+
if (!/^\d+(?:\.\d+){0,2}$/u.test(normalized)) {
|
|
2625
|
+
throw new TypeError("mobile.ios.version must contain one to three dot-separated integer components, for example 1.4.0.");
|
|
2626
|
+
}
|
|
2627
|
+
return normalized;
|
|
2628
|
+
};
|
|
1175
2629
|
var normalizeCertificateFingerprints = (values) => [
|
|
1176
2630
|
...new Set((values ?? []).map((value) => requireText(value, "mobile.deepLinks.android.sha256CertificateFingerprints").replaceAll(":", "").toUpperCase()).map((value) => {
|
|
1177
2631
|
if (!CERTIFICATE_FINGERPRINT_PATTERN.test(value)) {
|
|
@@ -1200,6 +2654,7 @@ var normalizeAbsoluteMobileConfig = (config, projectRoot) => {
|
|
|
1200
2654
|
deepLinkScheme,
|
|
1201
2655
|
engine: "capacitor",
|
|
1202
2656
|
entry: normalizeEntry(config.entry),
|
|
2657
|
+
iosVersion: normalizeIosVersion(config.ios?.version),
|
|
1203
2658
|
nativeProjectDirectory: resolveProjectPath(projectRoot, config.nativeProject?.directory ?? "mobile", "mobile.nativeProject.directory"),
|
|
1204
2659
|
platforms: normalizePlatforms(config.platforms),
|
|
1205
2660
|
productionOrigin
|
|
@@ -1287,7 +2742,7 @@ var createAbsoluteMobileAssociationPlugin = (mobile, projectRoot, options = {})
|
|
|
1287
2742
|
var writeAtomic = async (path, source) => {
|
|
1288
2743
|
let current;
|
|
1289
2744
|
try {
|
|
1290
|
-
current = await
|
|
2745
|
+
current = await readFile8(path, "utf8");
|
|
1291
2746
|
} catch (error) {
|
|
1292
2747
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
1293
2748
|
throw error;
|
|
@@ -1296,23 +2751,23 @@ var writeAtomic = async (path, source) => {
|
|
|
1296
2751
|
if (current === source)
|
|
1297
2752
|
return false;
|
|
1298
2753
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
1299
|
-
await
|
|
1300
|
-
await
|
|
2754
|
+
await writeFile7(temporary, source, { flag: "wx" });
|
|
2755
|
+
await rename7(temporary, path);
|
|
1301
2756
|
return true;
|
|
1302
2757
|
};
|
|
1303
2758
|
var exists2 = async (path) => {
|
|
1304
2759
|
try {
|
|
1305
|
-
await
|
|
2760
|
+
await access7(path);
|
|
1306
2761
|
return true;
|
|
1307
2762
|
} catch {
|
|
1308
2763
|
return false;
|
|
1309
2764
|
}
|
|
1310
2765
|
};
|
|
1311
2766
|
var assertOwnedOutput = async (root) => {
|
|
1312
|
-
const path =
|
|
2767
|
+
const path = resolve7(root, OWNERSHIP_FILE);
|
|
1313
2768
|
let ownership;
|
|
1314
2769
|
try {
|
|
1315
|
-
ownership = JSON.parse(await
|
|
2770
|
+
ownership = JSON.parse(await readFile8(path, "utf8"));
|
|
1316
2771
|
} catch {
|
|
1317
2772
|
throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
|
|
1318
2773
|
}
|
|
@@ -1326,22 +2781,22 @@ var publishGeneratedDirectory = async (temporary, root) => {
|
|
|
1326
2781
|
await assertOwnedOutput(root);
|
|
1327
2782
|
const backup = `${root}.${crypto.randomUUID()}.previous`;
|
|
1328
2783
|
if (hasCurrent)
|
|
1329
|
-
await
|
|
2784
|
+
await rename7(root, backup);
|
|
1330
2785
|
try {
|
|
1331
|
-
await
|
|
2786
|
+
await rename7(temporary, root);
|
|
1332
2787
|
} catch (error) {
|
|
1333
2788
|
if (hasCurrent)
|
|
1334
|
-
await
|
|
2789
|
+
await rename7(backup, root);
|
|
1335
2790
|
throw error;
|
|
1336
2791
|
}
|
|
1337
2792
|
if (hasCurrent)
|
|
1338
|
-
await
|
|
2793
|
+
await rm6(backup, { force: true, recursive: true });
|
|
1339
2794
|
};
|
|
1340
2795
|
var materializeHost = async (root, host, files) => {
|
|
1341
|
-
const directory =
|
|
1342
|
-
await
|
|
2796
|
+
const directory = resolve7(root, host, ".well-known");
|
|
2797
|
+
await mkdir6(directory, { recursive: true });
|
|
1343
2798
|
return Promise.all(files.map(async ([name, document]) => {
|
|
1344
|
-
const path =
|
|
2799
|
+
const path = resolve7(directory, name);
|
|
1345
2800
|
await writeAtomic(path, `${JSON.stringify(document, null, 2)}
|
|
1346
2801
|
`);
|
|
1347
2802
|
return path;
|
|
@@ -1366,7 +2821,7 @@ var associationEndpoints = (config, documents) => config.deepLinkHosts.flatMap((
|
|
|
1366
2821
|
return endpoints;
|
|
1367
2822
|
});
|
|
1368
2823
|
var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory) => {
|
|
1369
|
-
const root =
|
|
2824
|
+
const root = resolve7(outputDirectory);
|
|
1370
2825
|
const temporary = `${root}.${crypto.randomUUID()}.tmp`;
|
|
1371
2826
|
const documents = createAbsoluteMobileAssociationDocuments(config, {
|
|
1372
2827
|
requireAll: true
|
|
@@ -1377,16 +2832,16 @@ var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory)
|
|
|
1377
2832
|
if (documents.apple) {
|
|
1378
2833
|
files.push(["apple-app-site-association", documents.apple]);
|
|
1379
2834
|
}
|
|
1380
|
-
await
|
|
2835
|
+
await mkdir6(temporary, { recursive: true });
|
|
1381
2836
|
try {
|
|
1382
2837
|
const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host) => materializeHost(temporary, host, files)))).flat();
|
|
1383
|
-
await writeAtomic(
|
|
2838
|
+
await writeAtomic(resolve7(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
|
|
1384
2839
|
`);
|
|
1385
2840
|
await publishGeneratedDirectory(temporary, root);
|
|
1386
|
-
const written = temporaryPaths.map((path) =>
|
|
2841
|
+
const written = temporaryPaths.map((path) => resolve7(root, path.slice(temporary.length + 1)));
|
|
1387
2842
|
return { root, written };
|
|
1388
2843
|
} catch (error) {
|
|
1389
|
-
await
|
|
2844
|
+
await rm6(temporary, { force: true, recursive: true });
|
|
1390
2845
|
throw error;
|
|
1391
2846
|
}
|
|
1392
2847
|
};
|
|
@@ -1431,10 +2886,10 @@ var frameworks2 = new Set([
|
|
|
1431
2886
|
"svelte",
|
|
1432
2887
|
"vue"
|
|
1433
2888
|
]);
|
|
1434
|
-
var
|
|
2889
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1435
2890
|
var isPageFramework2 = (value) => typeof value === "string" && frameworks2.has(value);
|
|
1436
2891
|
var parseAbsoluteMobileBuildPageMetadata = (value) => {
|
|
1437
|
-
if (!
|
|
2892
|
+
if (!isRecord4(value))
|
|
1438
2893
|
return;
|
|
1439
2894
|
if (typeof value.bundleKey !== "string" || typeof value.contract !== "string" || !isPageFramework2(value.framework) || typeof value.pageId !== "string" || typeof value.propsSchemaHash !== "string") {
|
|
1440
2895
|
return;
|
|
@@ -1448,23 +2903,23 @@ var parseAbsoluteMobileBuildPageMetadata = (value) => {
|
|
|
1448
2903
|
};
|
|
1449
2904
|
};
|
|
1450
2905
|
// src/mobile/buildPipeline.ts
|
|
1451
|
-
import { readFile as
|
|
1452
|
-
import { join as
|
|
2906
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
2907
|
+
import { join as join10, resolve as resolve10 } from "path";
|
|
1453
2908
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1454
2909
|
|
|
1455
2910
|
// src/mobile/buildRelease.ts
|
|
1456
|
-
import { createHash as
|
|
1457
|
-
import { readFile as
|
|
1458
|
-
import { join as
|
|
1459
|
-
var sha256 = (bytes) =>
|
|
2911
|
+
import { createHash as createHash7 } from "crypto";
|
|
2912
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2913
|
+
import { join as join7, relative as relative6, resolve as resolve8 } from "path";
|
|
2914
|
+
var sha256 = (bytes) => createHash7("sha256").update(bytes).digest("hex");
|
|
1460
2915
|
var readPageMetadata = (route) => parseAbsoluteMobileBuildPageMetadata(route.hooks?.detail?.[ABSOLUTE_MOBILE_ROUTE_DETAIL]);
|
|
1461
2916
|
var resolveAssetPath = (buildDirectory, assetPath) => {
|
|
1462
|
-
const resolvedBuildDirectory =
|
|
1463
|
-
const resolvedAsset =
|
|
2917
|
+
const resolvedBuildDirectory = resolve8(buildDirectory);
|
|
2918
|
+
const resolvedAsset = resolve8(assetPath);
|
|
1464
2919
|
if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
|
|
1465
2920
|
return resolvedAsset;
|
|
1466
2921
|
}
|
|
1467
|
-
return
|
|
2922
|
+
return join7(buildDirectory, assetPath.replace(/^\/+/, ""));
|
|
1468
2923
|
};
|
|
1469
2924
|
var pageFor = async (metadata, manifest, buildDirectory) => {
|
|
1470
2925
|
const assetPath = manifest[metadata.bundleKey];
|
|
@@ -1472,8 +2927,8 @@ var pageFor = async (metadata, manifest, buildDirectory) => {
|
|
|
1472
2927
|
throw new TypeError(`Mobile page ${metadata.pageId} references missing manifest asset ${metadata.bundleKey}.`);
|
|
1473
2928
|
}
|
|
1474
2929
|
const resolvedAssetPath = resolveAssetPath(buildDirectory, assetPath);
|
|
1475
|
-
const bytes = await
|
|
1476
|
-
const bundlePath = `/${
|
|
2930
|
+
const bytes = await readFile9(resolvedAssetPath);
|
|
2931
|
+
const bundlePath = `/${relative6(resolve8(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
|
|
1477
2932
|
return {
|
|
1478
2933
|
bundleHash: sha256(bytes),
|
|
1479
2934
|
bundlePath,
|
|
@@ -1486,7 +2941,7 @@ var pageFor = async (metadata, manifest, buildDirectory) => {
|
|
|
1486
2941
|
var buildAbsoluteMobileCompatibilityRelease = async (options) => {
|
|
1487
2942
|
const [captured, producerBytes] = await Promise.all([
|
|
1488
2943
|
captureAbsoluteMobileRouteGraph(options.app),
|
|
1489
|
-
|
|
2944
|
+
readFile9(options.producerPath)
|
|
1490
2945
|
]);
|
|
1491
2946
|
if (captured.length === 0) {
|
|
1492
2947
|
throw new TypeError("No instrumented AbsoluteJS mobile page routes were found in the finalized Elysia route graph.");
|
|
@@ -1566,16 +3021,16 @@ var captureAbsoluteMobileRouteGraph = async (app) => {
|
|
|
1566
3021
|
|
|
1567
3022
|
// src/mobile/capacitorBundle.ts
|
|
1568
3023
|
import {
|
|
1569
|
-
copyFile as
|
|
1570
|
-
mkdir as
|
|
1571
|
-
mkdtemp as
|
|
1572
|
-
readFile as
|
|
1573
|
-
rename as
|
|
1574
|
-
rm as
|
|
1575
|
-
writeFile as
|
|
3024
|
+
copyFile as copyFile5,
|
|
3025
|
+
mkdir as mkdir7,
|
|
3026
|
+
mkdtemp as mkdtemp4,
|
|
3027
|
+
readFile as readFile10,
|
|
3028
|
+
rename as rename8,
|
|
3029
|
+
rm as rm7,
|
|
3030
|
+
writeFile as writeFile8
|
|
1576
3031
|
} from "fs/promises";
|
|
1577
3032
|
import { existsSync as existsSync2 } from "fs";
|
|
1578
|
-
import { basename, dirname as
|
|
3033
|
+
import { basename as basename2, dirname as dirname5, extname, join as join8, resolve as resolve9 } from "path";
|
|
1579
3034
|
|
|
1580
3035
|
// src/mobile/routeMatcher.ts
|
|
1581
3036
|
var REGEXP_SPECIAL_CHARACTERS = /[.*+?^${}()|[\]\\]/g;
|
|
@@ -1731,14 +3186,14 @@ var envelopeResponse = (response, status) => new Response(JSON.stringify({
|
|
|
1731
3186
|
protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
|
|
1732
3187
|
response
|
|
1733
3188
|
}), { headers: responseHeaders(), status });
|
|
1734
|
-
var
|
|
3189
|
+
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1735
3190
|
var normalizeJsonValue = (value) => {
|
|
1736
3191
|
const serialized = JSON.stringify(value);
|
|
1737
3192
|
if (serialized === undefined) {
|
|
1738
3193
|
throw new TypeError("Mobile page props must be JSON-serializable.");
|
|
1739
3194
|
}
|
|
1740
3195
|
const parsed = JSON.parse(serialized);
|
|
1741
|
-
if (!
|
|
3196
|
+
if (!isRecord5(parsed)) {
|
|
1742
3197
|
throw new TypeError("Mobile page props must serialize to an object.");
|
|
1743
3198
|
}
|
|
1744
3199
|
return parsed;
|
|
@@ -1863,14 +3318,14 @@ var upgradeReasons = new Set([
|
|
|
1863
3318
|
"protocol",
|
|
1864
3319
|
"runtime"
|
|
1865
3320
|
]);
|
|
1866
|
-
var
|
|
3321
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1867
3322
|
var isFramework = (value) => typeof value === "string" && frameworks4.has(value);
|
|
1868
3323
|
var isUpgradeReason = (value) => typeof value === "string" && upgradeReasons.has(value);
|
|
1869
3324
|
var parsePageResult = (value) => {
|
|
1870
3325
|
if (typeof value.contract !== "string" || !isFramework(value.framework) || typeof value.pageId !== "string" || typeof value.status !== "number") {
|
|
1871
3326
|
throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The mobile page response is missing required page metadata.");
|
|
1872
3327
|
}
|
|
1873
|
-
if (!
|
|
3328
|
+
if (!isRecord6(value.props)) {
|
|
1874
3329
|
throw new AbsoluteMobilePageProtocolError("invalid-props", "The mobile page response must contain an object props value.");
|
|
1875
3330
|
}
|
|
1876
3331
|
return {
|
|
@@ -1916,7 +3371,7 @@ var activateAbsoluteMobilePage = async (value, options) => {
|
|
|
1916
3371
|
};
|
|
1917
3372
|
};
|
|
1918
3373
|
var parseAbsoluteMobilePageEnvelope = (value) => {
|
|
1919
|
-
if (!
|
|
3374
|
+
if (!isRecord6(value) || !isRecord6(value.response)) {
|
|
1920
3375
|
throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The server did not return an AbsoluteJS mobile page envelope.");
|
|
1921
3376
|
}
|
|
1922
3377
|
if (value.protocol !== ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION) {
|
|
@@ -2009,7 +3464,7 @@ var INDEX_FILE = "index.html";
|
|
|
2009
3464
|
var CLIENT_IMPORT_PATTERN = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s*)["'](\/[^"']+)["']/gu;
|
|
2010
3465
|
var errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
|
|
2011
3466
|
var shellBootstrapModule = () => {
|
|
2012
|
-
const candidate = ["js", "ts"].map((extension) =>
|
|
3467
|
+
const candidate = ["js", "ts"].map((extension) => join8(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync2);
|
|
2013
3468
|
if (candidate)
|
|
2014
3469
|
return candidate;
|
|
2015
3470
|
throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
|
|
@@ -2030,8 +3485,8 @@ var indexHtml = (appName) => `<!doctype html>
|
|
|
2030
3485
|
</html>
|
|
2031
3486
|
`;
|
|
2032
3487
|
var sourceAssetPath = (buildDirectory, bundlePath) => {
|
|
2033
|
-
const root =
|
|
2034
|
-
const asset =
|
|
3488
|
+
const root = resolve9(buildDirectory);
|
|
3489
|
+
const asset = resolve9(root, bundlePath.replace(/^\/+/, ""));
|
|
2035
3490
|
if (!asset.startsWith(`${root}/`)) {
|
|
2036
3491
|
throw new TypeError("Mobile page bundle escaped the build directory.");
|
|
2037
3492
|
}
|
|
@@ -2039,8 +3494,8 @@ var sourceAssetPath = (buildDirectory, bundlePath) => {
|
|
|
2039
3494
|
};
|
|
2040
3495
|
var buildShellBootstrap = async (staging) => {
|
|
2041
3496
|
const modulePath = shellBootstrapModule();
|
|
2042
|
-
const entryPath =
|
|
2043
|
-
await
|
|
3497
|
+
const entryPath = join8(staging, ".absolute-mobile-entry.ts");
|
|
3498
|
+
await writeFile8(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
|
|
2044
3499
|
void startAbsoluteMobileShell();
|
|
2045
3500
|
`);
|
|
2046
3501
|
const build = await Bun.build({
|
|
@@ -2052,31 +3507,31 @@ void startAbsoluteMobileShell();
|
|
|
2052
3507
|
if (!build.success || build.outputs.length !== 1) {
|
|
2053
3508
|
throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
|
|
2054
3509
|
}
|
|
2055
|
-
await
|
|
2056
|
-
await
|
|
3510
|
+
await rename8(build.outputs[0]?.path ?? "", join8(staging, BOOTSTRAP_FILE));
|
|
3511
|
+
await rm7(entryPath, { force: true });
|
|
2057
3512
|
};
|
|
2058
3513
|
var removePreviousBundle = async (backup, moved) => {
|
|
2059
3514
|
if (!moved)
|
|
2060
3515
|
return;
|
|
2061
|
-
await
|
|
3516
|
+
await rm7(backup, { force: true, recursive: true });
|
|
2062
3517
|
};
|
|
2063
3518
|
var restorePreviousBundle = async (backup, destination, moved) => {
|
|
2064
3519
|
if (!moved)
|
|
2065
3520
|
return;
|
|
2066
|
-
await
|
|
3521
|
+
await rename8(backup, destination);
|
|
2067
3522
|
};
|
|
2068
3523
|
var installBundle = async (staging, destination) => {
|
|
2069
3524
|
const backup = `${destination}.previous-${crypto.randomUUID()}`;
|
|
2070
3525
|
let movedPrevious = false;
|
|
2071
3526
|
try {
|
|
2072
|
-
await
|
|
3527
|
+
await rename8(destination, backup);
|
|
2073
3528
|
movedPrevious = true;
|
|
2074
3529
|
} catch (error) {
|
|
2075
3530
|
if (!errorHasCode2(error, "ENOENT"))
|
|
2076
3531
|
throw error;
|
|
2077
3532
|
}
|
|
2078
3533
|
try {
|
|
2079
|
-
await
|
|
3534
|
+
await rename8(staging, destination);
|
|
2080
3535
|
await removePreviousBundle(backup, movedPrevious);
|
|
2081
3536
|
} catch (error) {
|
|
2082
3537
|
await restorePreviousBundle(backup, destination, movedPrevious);
|
|
@@ -2090,12 +3545,12 @@ var copyClientPage = async (page, buildDirectory, staging, copiedDependencies) =
|
|
|
2090
3545
|
const extension = extname(page.bundlePath) || ".js";
|
|
2091
3546
|
const localBundlePath = `./pages/${page.bundleHash}${extension}`;
|
|
2092
3547
|
const source = sourceAssetPath(buildDirectory, page.bundlePath);
|
|
2093
|
-
await
|
|
3548
|
+
await copyFile5(source, join8(staging, localBundlePath));
|
|
2094
3549
|
await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
|
|
2095
3550
|
return { ...page, localBundlePath };
|
|
2096
3551
|
};
|
|
2097
3552
|
var absoluteClientImports = async (sourcePath) => {
|
|
2098
|
-
const source = await
|
|
3553
|
+
const source = await readFile10(sourcePath, "utf8");
|
|
2099
3554
|
return [...source.matchAll(CLIENT_IMPORT_PATTERN)].flatMap((match) => {
|
|
2100
3555
|
const [specifier] = match.slice(1);
|
|
2101
3556
|
return specifier ? [specifier.split(/[?#]/u, 1)[0] ?? specifier] : [];
|
|
@@ -2106,9 +3561,9 @@ var copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, co
|
|
|
2106
3561
|
return;
|
|
2107
3562
|
copied.add(specifier);
|
|
2108
3563
|
const source = sourceAssetPath(buildDirectory, specifier);
|
|
2109
|
-
const destination =
|
|
2110
|
-
await
|
|
2111
|
-
await
|
|
3564
|
+
const destination = join8(staging, specifier.replace(/^\/+/, ""));
|
|
3565
|
+
await mkdir7(dirname5(destination), { recursive: true });
|
|
3566
|
+
await copyFile5(source, destination);
|
|
2112
3567
|
await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
|
|
2113
3568
|
};
|
|
2114
3569
|
var copyAbsoluteClientDependencies = async (sourcePath, buildDirectory, staging, copied) => {
|
|
@@ -2120,11 +3575,11 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
|
2120
3575
|
throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
|
|
2121
3576
|
}
|
|
2122
3577
|
const destination = options.config.bundleDirectory;
|
|
2123
|
-
await
|
|
2124
|
-
const staging = await
|
|
3578
|
+
await mkdir7(dirname5(destination), { recursive: true });
|
|
3579
|
+
const staging = await mkdtemp4(join8(dirname5(destination), `.${basename2(destination)}.stage-`));
|
|
2125
3580
|
try {
|
|
2126
|
-
const pageDirectory =
|
|
2127
|
-
await
|
|
3581
|
+
const pageDirectory = join8(staging, "pages");
|
|
3582
|
+
await mkdir7(pageDirectory, { recursive: true });
|
|
2128
3583
|
const copiedDependencies = new Set;
|
|
2129
3584
|
const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
|
|
2130
3585
|
const manifest = {
|
|
@@ -2141,48 +3596,48 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
|
2141
3596
|
runtime: options.artifact.runtime
|
|
2142
3597
|
};
|
|
2143
3598
|
await Promise.all([
|
|
2144
|
-
|
|
3599
|
+
writeFile8(join8(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
2145
3600
|
`),
|
|
2146
|
-
|
|
3601
|
+
writeFile8(join8(staging, INDEX_FILE), indexHtml(options.config.appName)),
|
|
2147
3602
|
buildShellBootstrap(staging)
|
|
2148
3603
|
]);
|
|
2149
3604
|
await installBundle(staging, destination);
|
|
2150
3605
|
return manifest;
|
|
2151
3606
|
} catch (error) {
|
|
2152
|
-
await
|
|
3607
|
+
await rm7(staging, { force: true, recursive: true });
|
|
2153
3608
|
throw error;
|
|
2154
3609
|
}
|
|
2155
3610
|
};
|
|
2156
3611
|
|
|
2157
3612
|
// src/mobile/materializedBundle.ts
|
|
2158
|
-
import { createHash as
|
|
3613
|
+
import { createHash as createHash8 } from "crypto";
|
|
2159
3614
|
import {
|
|
2160
|
-
access as
|
|
2161
|
-
mkdir as
|
|
2162
|
-
mkdtemp as
|
|
2163
|
-
readFile as
|
|
2164
|
-
rename as
|
|
2165
|
-
rm as
|
|
2166
|
-
writeFile as
|
|
3615
|
+
access as access8,
|
|
3616
|
+
mkdir as mkdir8,
|
|
3617
|
+
mkdtemp as mkdtemp5,
|
|
3618
|
+
readFile as readFile11,
|
|
3619
|
+
rename as rename9,
|
|
3620
|
+
rm as rm8,
|
|
3621
|
+
writeFile as writeFile9
|
|
2167
3622
|
} from "fs/promises";
|
|
2168
|
-
import { dirname as
|
|
3623
|
+
import { dirname as dirname6, join as join9, resolve as resolvePath2 } from "path";
|
|
2169
3624
|
import { pathToFileURL } from "url";
|
|
2170
3625
|
var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1;
|
|
2171
3626
|
var CURRENT_BUNDLE_FILE = "current.json";
|
|
2172
3627
|
var BUNDLES_DIRECTORY = "bundles";
|
|
2173
3628
|
var ARTIFACT_FILE2 = "artifact.json";
|
|
2174
3629
|
var BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
|
|
2175
|
-
var
|
|
3630
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2176
3631
|
var errorHasCode3 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
|
|
2177
3632
|
var bundleIdFor = (currentReleaseId, releases) => {
|
|
2178
3633
|
const identity = JSON.stringify({
|
|
2179
3634
|
currentReleaseId,
|
|
2180
3635
|
releases: releases.map(({ releaseId }) => releaseId)
|
|
2181
3636
|
});
|
|
2182
|
-
return `amb_${
|
|
3637
|
+
return `amb_${createHash8("sha256").update(identity).digest("hex")}`;
|
|
2183
3638
|
};
|
|
2184
3639
|
var parseBundleIndex = (value) => {
|
|
2185
|
-
if (!
|
|
3640
|
+
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)) {
|
|
2186
3641
|
throw new TypeError("Invalid materialized mobile compatibility bundle.");
|
|
2187
3642
|
}
|
|
2188
3643
|
const releases = value.releases.map(parseAbsoluteMobileCompatibilityArtifact);
|
|
@@ -2205,30 +3660,30 @@ var parseBundleIndex = (value) => {
|
|
|
2205
3660
|
};
|
|
2206
3661
|
};
|
|
2207
3662
|
var writeRelease = async (root, release) => {
|
|
2208
|
-
const directory =
|
|
2209
|
-
const producerPath =
|
|
2210
|
-
await
|
|
3663
|
+
const directory = join9(root, release.artifact.releaseId);
|
|
3664
|
+
const producerPath = join9(directory, release.artifact.producer.module);
|
|
3665
|
+
await mkdir8(dirname6(producerPath), { recursive: true });
|
|
2211
3666
|
await Promise.all([
|
|
2212
|
-
|
|
3667
|
+
writeFile9(join9(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
|
|
2213
3668
|
`),
|
|
2214
|
-
|
|
3669
|
+
writeFile9(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
|
|
2215
3670
|
]);
|
|
2216
3671
|
};
|
|
2217
3672
|
var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
|
|
2218
|
-
const destination =
|
|
3673
|
+
const destination = join9(bundlesRoot, bundleId);
|
|
2219
3674
|
try {
|
|
2220
|
-
await
|
|
3675
|
+
await access8(destination);
|
|
2221
3676
|
return destination;
|
|
2222
3677
|
} catch (error) {
|
|
2223
3678
|
if (!errorHasCode3(error, "ENOENT"))
|
|
2224
3679
|
throw error;
|
|
2225
3680
|
}
|
|
2226
|
-
const staging = await
|
|
3681
|
+
const staging = await mkdtemp5(join9(bundlesRoot, ".stage-"));
|
|
2227
3682
|
try {
|
|
2228
3683
|
await Promise.all(releases.map((release) => writeRelease(staging, release)));
|
|
2229
|
-
await
|
|
3684
|
+
await rename9(staging, destination);
|
|
2230
3685
|
} catch (error) {
|
|
2231
|
-
await
|
|
3686
|
+
await rm8(staging, { force: true, recursive: true });
|
|
2232
3687
|
if (errorHasCode3(error, "EEXIST") || errorHasCode3(error, "ENOTEMPTY")) {
|
|
2233
3688
|
return destination;
|
|
2234
3689
|
}
|
|
@@ -2239,7 +3694,7 @@ var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
|
|
|
2239
3694
|
var readCompatibilityModule = (modulePath) => import(pathToFileURL(modulePath).href);
|
|
2240
3695
|
var resolveProducerHandler = (loaded, exportName) => {
|
|
2241
3696
|
const value = loaded[exportName];
|
|
2242
|
-
if (!
|
|
3697
|
+
if (!isRecord7(value) || typeof value.handle !== "function") {
|
|
2243
3698
|
throw new TypeError(`Compatibility producer export ${exportName} must expose handle(request).`);
|
|
2244
3699
|
}
|
|
2245
3700
|
const { handle } = value;
|
|
@@ -2257,15 +3712,15 @@ var resolveProducerHandler = (loaded, exportName) => {
|
|
|
2257
3712
|
};
|
|
2258
3713
|
var loadAbsoluteMobileMaterializedBundle = async (root) => {
|
|
2259
3714
|
const resolvedRoot = resolvePath2(root);
|
|
2260
|
-
const serialized = await
|
|
3715
|
+
const serialized = await readFile11(join9(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
|
|
2261
3716
|
const parsed = JSON.parse(serialized);
|
|
2262
3717
|
const index = parseBundleIndex(parsed);
|
|
2263
|
-
const bundleRoot =
|
|
3718
|
+
const bundleRoot = join9(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
2264
3719
|
return {
|
|
2265
3720
|
artifacts: index.releases,
|
|
2266
3721
|
currentReleaseId: index.currentReleaseId,
|
|
2267
3722
|
loadProducer: async (artifact) => {
|
|
2268
|
-
const modulePath =
|
|
3723
|
+
const modulePath = join9(bundleRoot, artifact.releaseId, artifact.producer.module);
|
|
2269
3724
|
await verifyAbsoluteMobileCompatibilityProducer({
|
|
2270
3725
|
artifact,
|
|
2271
3726
|
producer: Bun.file(modulePath)
|
|
@@ -2292,8 +3747,8 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
|
|
|
2292
3747
|
return release;
|
|
2293
3748
|
});
|
|
2294
3749
|
const root = resolvePath2(input.root);
|
|
2295
|
-
const bundlesRoot =
|
|
2296
|
-
await
|
|
3750
|
+
const bundlesRoot = join9(root, BUNDLES_DIRECTORY);
|
|
3751
|
+
await mkdir8(bundlesRoot, { recursive: true });
|
|
2297
3752
|
const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
|
|
2298
3753
|
await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
|
|
2299
3754
|
const index = {
|
|
@@ -2302,22 +3757,22 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
|
|
|
2302
3757
|
format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
|
|
2303
3758
|
releases: artifacts
|
|
2304
3759
|
};
|
|
2305
|
-
const pointerPath =
|
|
2306
|
-
const temporaryPointerPath =
|
|
2307
|
-
await
|
|
3760
|
+
const pointerPath = join9(root, CURRENT_BUNDLE_FILE);
|
|
3761
|
+
const temporaryPointerPath = join9(root, `.current-${crypto.randomUUID()}.json`);
|
|
3762
|
+
await writeFile9(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
|
|
2308
3763
|
`, { flag: "wx" });
|
|
2309
|
-
await
|
|
3764
|
+
await rename9(temporaryPointerPath, pointerPath);
|
|
2310
3765
|
return index;
|
|
2311
3766
|
};
|
|
2312
3767
|
var readAbsoluteMobileMaterializedReleases = async (root) => {
|
|
2313
3768
|
const resolvedRoot = resolvePath2(root);
|
|
2314
3769
|
try {
|
|
2315
|
-
const serialized = await
|
|
3770
|
+
const serialized = await readFile11(join9(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
|
|
2316
3771
|
const parsed = JSON.parse(serialized);
|
|
2317
3772
|
const index = parseBundleIndex(parsed);
|
|
2318
|
-
const bundleRoot =
|
|
3773
|
+
const bundleRoot = join9(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
2319
3774
|
return Promise.all(index.releases.map(async (artifact) => {
|
|
2320
|
-
const producer = Bun.file(
|
|
3775
|
+
const producer = Bun.file(join9(bundleRoot, artifact.releaseId, artifact.producer.module));
|
|
2321
3776
|
await verifyAbsoluteMobileCompatibilityProducer({
|
|
2322
3777
|
artifact,
|
|
2323
3778
|
producer
|
|
@@ -2366,11 +3821,11 @@ var loadServerApp = async (producerPath) => {
|
|
|
2366
3821
|
return { app, exportName };
|
|
2367
3822
|
};
|
|
2368
3823
|
var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
2369
|
-
const buildDirectory =
|
|
3824
|
+
const buildDirectory = resolve10(options.buildDirectory);
|
|
2370
3825
|
const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
|
|
2371
|
-
const root =
|
|
3826
|
+
const root = join10(buildDirectory, ".absolutejs", "mobile-compatibility");
|
|
2372
3827
|
const [manifestSource, previous] = await Promise.all([
|
|
2373
|
-
|
|
3828
|
+
readFile12(join10(buildDirectory, "manifest.json"), "utf8"),
|
|
2374
3829
|
readAbsoluteMobileMaterializedReleases(root)
|
|
2375
3830
|
]);
|
|
2376
3831
|
const manifest = JSON.parse(manifestSource);
|
|
@@ -2381,7 +3836,7 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
2381
3836
|
process.env.ABSOLUTE_BUILD_DIR = buildDirectory;
|
|
2382
3837
|
let loaded;
|
|
2383
3838
|
try {
|
|
2384
|
-
loaded = await loadServerApp(
|
|
3839
|
+
loaded = await loadServerApp(resolve10(options.producerPath));
|
|
2385
3840
|
} finally {
|
|
2386
3841
|
restoreBuildDirectory(previousBuildDirectory);
|
|
2387
3842
|
}
|
|
@@ -2392,7 +3847,7 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
2392
3847
|
manifest,
|
|
2393
3848
|
previousArtifacts: previous.map(({ artifact }) => artifact),
|
|
2394
3849
|
producerExport: loaded.exportName,
|
|
2395
|
-
producerPath:
|
|
3850
|
+
producerPath: resolve10(options.producerPath),
|
|
2396
3851
|
runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
|
|
2397
3852
|
});
|
|
2398
3853
|
const releasesById = new Map([current, ...previous].map((release) => [
|
|
@@ -2486,20 +3941,20 @@ var createAbsoluteMobileCompatibilityDispatcher = (options) => {
|
|
|
2486
3941
|
}).as("global");
|
|
2487
3942
|
};
|
|
2488
3943
|
// src/mobile/nativeDeepLinks.ts
|
|
2489
|
-
import { readFile as
|
|
2490
|
-
import { join as
|
|
3944
|
+
import { readFile as readFile13, rename as rename10, writeFile as writeFile10 } from "fs/promises";
|
|
3945
|
+
import { join as join11 } from "path";
|
|
2491
3946
|
var START_MARKER = "<!-- absolutejs:deep-links:start -->";
|
|
2492
3947
|
var END_MARKER = "<!-- absolutejs:deep-links:end -->";
|
|
2493
3948
|
var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
|
|
2494
3949
|
var NOT_FOUND = -1;
|
|
2495
3950
|
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
2496
3951
|
var writeChangedFile = async (path, source) => {
|
|
2497
|
-
const current = await
|
|
3952
|
+
const current = await readFile13(path, "utf8");
|
|
2498
3953
|
if (current === source)
|
|
2499
3954
|
return false;
|
|
2500
3955
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
2501
|
-
await
|
|
2502
|
-
await
|
|
3956
|
+
await writeFile10(temporary, source, { flag: "wx" });
|
|
3957
|
+
await rename10(temporary, path);
|
|
2503
3958
|
return true;
|
|
2504
3959
|
};
|
|
2505
3960
|
var replaceManagedRegion = (source, region, insertAt) => {
|
|
@@ -2544,8 +3999,8 @@ ${hosts}
|
|
|
2544
3999
|
`;
|
|
2545
4000
|
};
|
|
2546
4001
|
var configureAndroid = async (config) => {
|
|
2547
|
-
const path =
|
|
2548
|
-
const source = await
|
|
4002
|
+
const path = join11(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
4003
|
+
const source = await readFile13(path, "utf8");
|
|
2549
4004
|
const mainActivity = source.indexOf('android:name=".MainActivity"');
|
|
2550
4005
|
if (mainActivity === NOT_FOUND) {
|
|
2551
4006
|
throw new TypeError("Android MainActivity was not found.");
|
|
@@ -2570,8 +4025,8 @@ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
|
|
|
2570
4025
|
${END_MARKER}
|
|
2571
4026
|
`;
|
|
2572
4027
|
var configureIosInfo = async (config) => {
|
|
2573
|
-
const path =
|
|
2574
|
-
const source = await
|
|
4028
|
+
const path = join11(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
4029
|
+
const source = await readFile13(path, "utf8");
|
|
2575
4030
|
const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
|
|
2576
4031
|
${END_MARKER}
|
|
2577
4032
|
`;
|
|
@@ -2594,10 +4049,10 @@ ${domains}
|
|
|
2594
4049
|
`;
|
|
2595
4050
|
};
|
|
2596
4051
|
var configureIosEntitlements = async (config) => {
|
|
2597
|
-
const path =
|
|
4052
|
+
const path = join11(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
2598
4053
|
let current = "";
|
|
2599
4054
|
try {
|
|
2600
|
-
current = await
|
|
4055
|
+
current = await readFile13(path, "utf8");
|
|
2601
4056
|
} catch (error) {
|
|
2602
4057
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
2603
4058
|
throw error;
|
|
@@ -2607,13 +4062,13 @@ var configureIosEntitlements = async (config) => {
|
|
|
2607
4062
|
if (current === source)
|
|
2608
4063
|
return false;
|
|
2609
4064
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
2610
|
-
await
|
|
2611
|
-
await
|
|
4065
|
+
await writeFile10(temporary, source, { flag: "wx" });
|
|
4066
|
+
await rename10(temporary, path);
|
|
2612
4067
|
return true;
|
|
2613
4068
|
};
|
|
2614
4069
|
var configureIosProject = async (config) => {
|
|
2615
|
-
const path =
|
|
2616
|
-
const source = await
|
|
4070
|
+
const path = join11(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
4071
|
+
const source = await readFile13(path, "utf8");
|
|
2617
4072
|
const declarations = [
|
|
2618
4073
|
...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
|
|
2619
4074
|
].map((match) => match[1]);
|
|
@@ -2650,15 +4105,113 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
|
|
|
2650
4105
|
changed: results.filter(({ didChange }) => didChange).map(({ platform }) => platform)
|
|
2651
4106
|
};
|
|
2652
4107
|
};
|
|
4108
|
+
// src/mobile/releasePublisher.ts
|
|
4109
|
+
import { access as access9 } from "fs/promises";
|
|
4110
|
+
import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve11, sep as sep5 } from "path";
|
|
4111
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
4112
|
+
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
4113
|
+
if (typeof publisher.prepareIosRelease !== "function") {
|
|
4114
|
+
throw new TypeError("App Store Connect publishing requires a registry module created with @absolutejs/deploy/app-store-connect.");
|
|
4115
|
+
}
|
|
4116
|
+
const { buildNumber } = await publisher.prepareIosRelease(options);
|
|
4117
|
+
if (!Number.isSafeInteger(buildNumber) || buildNumber < 1) {
|
|
4118
|
+
throw new TypeError("App Store Connect publisher returned an invalid iOS build number.");
|
|
4119
|
+
}
|
|
4120
|
+
return buildNumber;
|
|
4121
|
+
};
|
|
4122
|
+
var prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
4123
|
+
if (typeof publisher.prepareAndroidRelease !== "function") {
|
|
4124
|
+
throw new TypeError("Google Play publishing requires a registry module created with @absolutejs/deploy/google-play.");
|
|
4125
|
+
}
|
|
4126
|
+
const prepared = await publisher.prepareAndroidRelease(options);
|
|
4127
|
+
const { versionCode } = prepared;
|
|
4128
|
+
if (typeof versionCode !== "number" || !Number.isSafeInteger(versionCode) || versionCode < 1 || versionCode > 2100000000) {
|
|
4129
|
+
throw new TypeError("Google Play publisher returned an invalid Android versionCode.");
|
|
4130
|
+
}
|
|
4131
|
+
return versionCode;
|
|
4132
|
+
};
|
|
4133
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4134
|
+
var isPublisher = (value) => isRecord8(value) && typeof value.publish === "function";
|
|
4135
|
+
var publisherModulePath = (projectRoot, requested) => {
|
|
4136
|
+
const root = resolve11(projectRoot);
|
|
4137
|
+
const path = resolve11(root, requested);
|
|
4138
|
+
const projectRelative = relative7(root, path);
|
|
4139
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute5(projectRelative)) {
|
|
4140
|
+
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
4141
|
+
}
|
|
4142
|
+
return path;
|
|
4143
|
+
};
|
|
4144
|
+
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
4145
|
+
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
4146
|
+
await access9(modulePath).catch(() => {
|
|
4147
|
+
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
4148
|
+
});
|
|
4149
|
+
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
4150
|
+
const publisher = isRecord8(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
4151
|
+
if (!isPublisher(publisher)) {
|
|
4152
|
+
throw new TypeError("Native release registry module must default-export a registry with publish(options).");
|
|
4153
|
+
}
|
|
4154
|
+
return publisher;
|
|
4155
|
+
};
|
|
4156
|
+
var publishAbsoluteAndroidRelease = async (options) => {
|
|
4157
|
+
const publisher = await loadAbsoluteNativeReleasePublisher(options.projectRoot, options.modulePath);
|
|
4158
|
+
const publication = await publisher.publish({
|
|
4159
|
+
allowUnsigned: options.allowUnsigned,
|
|
4160
|
+
channel: options.channel,
|
|
4161
|
+
googlePlay: options.googlePlay,
|
|
4162
|
+
releaseRoot: options.release.releaseRoot,
|
|
4163
|
+
signal: options.signal
|
|
4164
|
+
});
|
|
4165
|
+
const expected = options.release.metadata;
|
|
4166
|
+
const actual = publication.record?.metadata;
|
|
4167
|
+
if (!actual || actual.appId !== expected.appId || actual.platform !== "android" || actual.releaseId !== expected.releaseId || actual.sha256 !== expected.sha256 || actual.signed !== expected.signed || actual.versionCode !== expected.versionCode || typeof publication.reused !== "boolean") {
|
|
4168
|
+
throw new TypeError("Native release registry returned a different Android release identity.");
|
|
4169
|
+
}
|
|
4170
|
+
if (options.channel !== undefined && (publication.channel?.channel !== options.channel || publication.channel.releaseId !== expected.releaseId)) {
|
|
4171
|
+
throw new TypeError("Native release registry did not promote the requested channel.");
|
|
4172
|
+
}
|
|
4173
|
+
const { googlePlay } = publication;
|
|
4174
|
+
if (options.googlePlay) {
|
|
4175
|
+
if (!expected.versionCode || !googlePlay || googlePlay.receipt.provider !== "google-play" || googlePlay.receipt.packageName !== expected.appId || googlePlay.receipt.releaseId !== expected.releaseId || googlePlay.receipt.sha256 !== expected.sha256 || googlePlay.receipt.stage !== "committed" || googlePlay.receipt.intent.track !== options.googlePlay.track || typeof googlePlay.receipt.versionCode !== "string" || !/^\d+$/.test(googlePlay.receipt.versionCode) || Number(googlePlay.receipt.versionCode) !== expected.versionCode || typeof googlePlay.reused !== "boolean") {
|
|
4176
|
+
throw new TypeError("Native release publisher did not commit the requested Google Play release.");
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
return publication;
|
|
4180
|
+
};
|
|
4181
|
+
var publishAbsoluteIosRelease = async (options) => {
|
|
4182
|
+
const publisher = await loadAbsoluteNativeReleasePublisher(options.projectRoot, options.modulePath);
|
|
4183
|
+
const publication = await publisher.publish({
|
|
4184
|
+
allowUnsigned: options.allowUnsigned,
|
|
4185
|
+
appStoreConnect: options.appStoreConnect,
|
|
4186
|
+
channel: options.channel,
|
|
4187
|
+
releaseRoot: options.release.releaseRoot,
|
|
4188
|
+
signal: options.signal
|
|
4189
|
+
});
|
|
4190
|
+
const expected = options.release.metadata;
|
|
4191
|
+
const actual = publication.record?.metadata;
|
|
4192
|
+
if (!actual || actual.appId !== expected.appId || actual.platform !== "ios" || actual.releaseId !== expected.releaseId || actual.sha256 !== expected.sha256 || actual.signed !== expected.signed || actual.buildNumber !== expected.buildNumber || actual.marketingVersion !== expected.marketingVersion || typeof publication.reused !== "boolean") {
|
|
4193
|
+
throw new TypeError("Native release registry returned a different iOS release identity.");
|
|
4194
|
+
}
|
|
4195
|
+
if (options.channel !== undefined && (publication.channel?.channel !== options.channel || publication.channel.releaseId !== expected.releaseId)) {
|
|
4196
|
+
throw new TypeError("Native release registry did not promote the requested channel.");
|
|
4197
|
+
}
|
|
4198
|
+
if (options.appStoreConnect) {
|
|
4199
|
+
const distributed = publication.appStoreConnect;
|
|
4200
|
+
if (!expected.buildNumber || !distributed || distributed.receipt.provider !== "app-store-connect" || distributed.receipt.releaseId !== expected.releaseId || distributed.receipt.sha256 !== expected.sha256 || distributed.receipt.buildNumber !== expected.buildNumber || distributed.receipt.marketingVersion !== expected.marketingVersion || !["distributed", "review-submitted"].includes(distributed.receipt.stage) || JSON.stringify([...distributed.receipt.intent.groups].sort()) !== JSON.stringify([...options.appStoreConnect.groups ?? []].sort()) || distributed.receipt.intent.submitForReview !== (options.appStoreConnect.submitForReview ?? false) || typeof distributed.reused !== "boolean") {
|
|
4201
|
+
throw new TypeError("Native release publisher did not complete the requested App Store Connect release.");
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
return publication;
|
|
4205
|
+
};
|
|
2653
4206
|
// src/mobile/routeMetadataTransform.ts
|
|
2654
4207
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
2655
|
-
import { dirname as
|
|
4208
|
+
import { dirname as dirname7, extname as extname2, relative as relative8, resolve as resolve12 } from "path";
|
|
2656
4209
|
import ts from "typescript";
|
|
2657
4210
|
var ROUTE_METHODS = new Set(["get", "head"]);
|
|
2658
4211
|
var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
|
|
2659
4212
|
var PAGE_HANDLER = "handleReactPageRequest";
|
|
2660
4213
|
var posixPath = (value) => value.replace(/\\/g, "/");
|
|
2661
|
-
var findTsconfig = (entry, projectRoot) => ts.findConfigFile(
|
|
4214
|
+
var findTsconfig = (entry, projectRoot) => ts.findConfigFile(dirname7(entry), existsSync3, "tsconfig.json") ?? ts.findConfigFile(projectRoot, existsSync3, "tsconfig.json");
|
|
2662
4215
|
var createProgram = (entry, projectRoot) => {
|
|
2663
4216
|
const configPath = findTsconfig(entry, projectRoot);
|
|
2664
4217
|
if (!configPath) {
|
|
@@ -2670,7 +4223,7 @@ var createProgram = (entry, projectRoot) => {
|
|
|
2670
4223
|
target: ts.ScriptTarget.ESNext
|
|
2671
4224
|
});
|
|
2672
4225
|
}
|
|
2673
|
-
const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) => readFileSync2(path, "utf8")).config, ts.sys,
|
|
4226
|
+
const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) => readFileSync2(path, "utf8")).config, ts.sys, dirname7(configPath));
|
|
2674
4227
|
if (!parsed.fileNames.includes(entry))
|
|
2675
4228
|
parsed.fileNames.push(entry);
|
|
2676
4229
|
return ts.createProgram(parsed.fileNames, parsed.options);
|
|
@@ -2776,7 +4329,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
|
|
|
2776
4329
|
const declaration = symbol?.declarations?.[0];
|
|
2777
4330
|
const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
|
|
2778
4331
|
const exportedName = symbol?.name ?? expression.getText(sourceFile);
|
|
2779
|
-
const source = posixPath(
|
|
4332
|
+
const source = posixPath(relative8(projectRoot, file));
|
|
2780
4333
|
return `${source}#${exportedName}`;
|
|
2781
4334
|
};
|
|
2782
4335
|
var resolveAlias = (symbol, checker) => {
|
|
@@ -2883,7 +4436,7 @@ var analyzeProgram = (program, projectRoot) => {
|
|
|
2883
4436
|
const checker = program.getTypeChecker();
|
|
2884
4437
|
const analyzed = new Map;
|
|
2885
4438
|
for (const sourceFile of program.getSourceFiles()) {
|
|
2886
|
-
const resolvedFile =
|
|
4439
|
+
const resolvedFile = resolve12(sourceFile.fileName);
|
|
2887
4440
|
if (!isProjectSource(sourceFile, resolvedFile, projectRoot))
|
|
2888
4441
|
continue;
|
|
2889
4442
|
const analysis = analyzeSourceFile(sourceFile, checker, projectRoot);
|
|
@@ -2966,14 +4519,14 @@ var transformFile = (source, fileName, analysis) => {
|
|
|
2966
4519
|
};
|
|
2967
4520
|
var ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL = ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION;
|
|
2968
4521
|
var createAbsoluteMobileRouteMetadataPlugin = (options) => {
|
|
2969
|
-
const projectRoot =
|
|
2970
|
-
const entry =
|
|
4522
|
+
const projectRoot = resolve12(options.projectRoot ?? process.cwd());
|
|
4523
|
+
const entry = resolve12(options.entry);
|
|
2971
4524
|
const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
|
|
2972
4525
|
return {
|
|
2973
4526
|
name: "absolute-mobile-route-metadata",
|
|
2974
4527
|
setup(build) {
|
|
2975
4528
|
build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
|
|
2976
|
-
const analysis = analyzed.get(
|
|
4529
|
+
const analysis = analyzed.get(resolve12(path));
|
|
2977
4530
|
if (!analysis)
|
|
2978
4531
|
return;
|
|
2979
4532
|
const source = await Bun.file(path).text();
|
|
@@ -2986,38 +4539,56 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
|
|
|
2986
4539
|
};
|
|
2987
4540
|
};
|
|
2988
4541
|
var inspectAbsoluteMobileRouteMetadata = (options) => {
|
|
2989
|
-
const projectRoot =
|
|
2990
|
-
const entry =
|
|
4542
|
+
const projectRoot = resolve12(options.projectRoot ?? process.cwd());
|
|
4543
|
+
const entry = resolve12(options.entry);
|
|
2991
4544
|
const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
|
|
2992
4545
|
return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
|
|
2993
|
-
file: posixPath(
|
|
4546
|
+
file: posixPath(relative8(projectRoot, file)),
|
|
2994
4547
|
metadata
|
|
2995
4548
|
})));
|
|
2996
4549
|
};
|
|
2997
4550
|
export {
|
|
2998
4551
|
writeAbsoluteCapacitorConfig,
|
|
4552
|
+
waitForAbsoluteIosHmrLog,
|
|
2999
4553
|
verifyAbsoluteMobileCompatibilityProducer,
|
|
3000
4554
|
verifyAbsoluteMobileAssociationFiles,
|
|
4555
|
+
startAbsoluteIosDevSession,
|
|
3001
4556
|
runWithAbsoluteMobileProducer,
|
|
3002
4557
|
retainAbsoluteMobileCompatibilityArtifacts,
|
|
3003
4558
|
resolveAbsoluteMobileRoute,
|
|
3004
4559
|
resolveAbsoluteMobileDeepLink,
|
|
3005
4560
|
resolveAbsoluteMobileCompatibilityRelease,
|
|
4561
|
+
repairAbsoluteIosDevSession,
|
|
4562
|
+
redactAbsoluteIosLog,
|
|
3006
4563
|
readAbsoluteMobileMaterializedReleases,
|
|
4564
|
+
publishAbsoluteIosRelease,
|
|
4565
|
+
publishAbsoluteAndroidRelease,
|
|
4566
|
+
prepareAbsoluteIosRelease,
|
|
4567
|
+
prepareAbsoluteIosDevProject,
|
|
4568
|
+
prepareAbsoluteAndroidRelease,
|
|
4569
|
+
parseIosSimulators,
|
|
4570
|
+
parseIosRuntimes,
|
|
4571
|
+
parseIosDeviceTypes,
|
|
3007
4572
|
parseAbsoluteMobilePageRequest,
|
|
3008
4573
|
parseAbsoluteMobilePageEnvelope,
|
|
3009
4574
|
parseAbsoluteMobileCompatibilityArtifact,
|
|
3010
4575
|
parseAbsoluteMobileBuildPageMetadata,
|
|
4576
|
+
parseAbsoluteIosLogLine,
|
|
4577
|
+
parseAbsoluteIosHmrLog,
|
|
3011
4578
|
normalizeAbsoluteMobileConfig,
|
|
3012
4579
|
navigateAbsoluteMobilePage,
|
|
3013
4580
|
materializeAbsoluteMobileCompatibilityBundle,
|
|
3014
4581
|
materializeAbsoluteMobileAssociationFiles,
|
|
3015
4582
|
materializeAbsoluteCapacitorWebBundle,
|
|
3016
4583
|
matchesAbsoluteMobileRoutePattern,
|
|
4584
|
+
loadAbsoluteNativeReleasePublisher,
|
|
3017
4585
|
loadAbsoluteMobileMaterializedBundle,
|
|
4586
|
+
isAbsoluteIosNativeRootInput,
|
|
3018
4587
|
inspectAbsoluteMobileRouteMetadata,
|
|
3019
4588
|
hashAbsoluteMobilePropsSchema,
|
|
3020
4589
|
getCurrentAbsoluteMobileProducerContext,
|
|
4590
|
+
fingerprintAbsoluteIosNativeProject,
|
|
4591
|
+
fingerprintAbsoluteIosDevProject,
|
|
3021
4592
|
finalizeAbsoluteMobilePage,
|
|
3022
4593
|
finalizeAbsoluteMobileCompatibilityBuild,
|
|
3023
4594
|
fetchAbsoluteMobilePage,
|
|
@@ -3032,9 +4603,11 @@ export {
|
|
|
3032
4603
|
createAbsoluteMobileBlobArtifactStore,
|
|
3033
4604
|
createAbsoluteMobileAssociationPlugin,
|
|
3034
4605
|
createAbsoluteMobileAssociationDocuments,
|
|
4606
|
+
createAbsoluteIosNativeWatcher,
|
|
3035
4607
|
carryForwardAbsoluteMobileCompatibilityReleases,
|
|
3036
4608
|
captureAbsoluteMobileRouteGraph,
|
|
3037
4609
|
buildAbsoluteMobileCompatibilityRelease,
|
|
4610
|
+
buildAbsoluteIosRelease,
|
|
3038
4611
|
buildAbsoluteAndroidRelease,
|
|
3039
4612
|
applyAbsoluteNativeDeepLinks,
|
|
3040
4613
|
activateAbsoluteMobilePage,
|
|
@@ -3051,8 +4624,10 @@ export {
|
|
|
3051
4624
|
ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
|
|
3052
4625
|
ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT,
|
|
3053
4626
|
ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
|
|
4627
|
+
ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
4628
|
+
ABSOLUTE_IOS_RELEASE_FORMAT,
|
|
3054
4629
|
ABSOLUTE_ANDROID_RELEASE_FORMAT
|
|
3055
4630
|
};
|
|
3056
4631
|
|
|
3057
|
-
//# debugId=
|
|
4632
|
+
//# debugId=048422942BB9C32864756E2164756E21
|
|
3058
4633
|
//# sourceMappingURL=index.js.map
|