@absolutejs/absolute 0.20.0-beta.2 → 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 +2192 -949
- package/dist/index.js +15 -1
- package/dist/index.js.map +5 -5
- package/dist/mobile/index.js +1254 -168
- package/dist/mobile/index.js.map +6 -3
- package/dist/src/core/prepare.d.ts +6 -0
- package/dist/src/dev/clientManager.d.ts +2 -0
- package/dist/src/mobile/index.d.ts +3 -0
- package/dist/src/mobile/iosConformance.d.ts +15 -0
- package/dist/src/mobile/iosNativeWatcher.d.ts +19 -0
- package/dist/src/mobile/iosRelease.d.ts +2 -2
- package/dist/src/mobile/iosSimulatorController.d.ts +89 -0
- package/dist/src/plugins/hmr.d.ts +3 -0
- package/package.json +8 -7
package/dist/mobile/index.js
CHANGED
|
@@ -617,7 +617,7 @@ var verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes
|
|
|
617
617
|
// src/mobile/androidRelease.ts
|
|
618
618
|
import { createHash as createHash4 } from "crypto";
|
|
619
619
|
import {
|
|
620
|
-
access as
|
|
620
|
+
access as access4,
|
|
621
621
|
copyFile as copyFile2,
|
|
622
622
|
mkdir as mkdir3,
|
|
623
623
|
mkdtemp as mkdtemp2,
|
|
@@ -630,6 +630,7 @@ import {
|
|
|
630
630
|
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join4, relative as relative3, resolve as resolve3, sep as sep2 } from "path";
|
|
631
631
|
|
|
632
632
|
// src/mobile/emulatorDoctor.ts
|
|
633
|
+
import { access } from "fs/promises";
|
|
633
634
|
import { homedir } from "os";
|
|
634
635
|
import { join as join2 } from "path";
|
|
635
636
|
|
|
@@ -647,6 +648,34 @@ var isWSLEnvironment = () => {
|
|
|
647
648
|
};
|
|
648
649
|
|
|
649
650
|
// src/mobile/emulatorDoctor.ts
|
|
651
|
+
var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36";
|
|
652
|
+
var captureCommand = (command) => {
|
|
653
|
+
try {
|
|
654
|
+
const result = Bun.spawnSync(command, {
|
|
655
|
+
stderr: "ignore",
|
|
656
|
+
stdout: "pipe"
|
|
657
|
+
});
|
|
658
|
+
return {
|
|
659
|
+
exitCode: result.exitCode,
|
|
660
|
+
stdout: result.stdout.toString()
|
|
661
|
+
};
|
|
662
|
+
} catch {
|
|
663
|
+
return { exitCode: 1, stdout: "" };
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var hasAvailableIosRuntime = (output) => {
|
|
667
|
+
try {
|
|
668
|
+
const parsed = JSON.parse(output);
|
|
669
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
670
|
+
return false;
|
|
671
|
+
const runtimes = Reflect.get(parsed, "runtimes");
|
|
672
|
+
if (!Array.isArray(runtimes))
|
|
673
|
+
return false;
|
|
674
|
+
return runtimes.some((runtime) => typeof runtime === "object" && runtime !== null && Reflect.get(runtime, "isAvailable") === true && typeof Reflect.get(runtime, "identifier") === "string" && String(Reflect.get(runtime, "identifier")).includes("iOS"));
|
|
675
|
+
} catch {
|
|
676
|
+
return false;
|
|
677
|
+
}
|
|
678
|
+
};
|
|
650
679
|
var windowsPathToWsl = (path) => {
|
|
651
680
|
const match = /^([a-z]):[\\/](.*)$/i.exec(path.trim());
|
|
652
681
|
if (!match)
|
|
@@ -678,6 +707,14 @@ var absoluteManagedAndroidSdkRoot = (host, env = process.env) => {
|
|
|
678
707
|
}
|
|
679
708
|
return join2(homedir(), ".absolutejs", "android-sdk");
|
|
680
709
|
};
|
|
710
|
+
var pathExists = async (path) => {
|
|
711
|
+
try {
|
|
712
|
+
await access(path);
|
|
713
|
+
return true;
|
|
714
|
+
} catch {
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
};
|
|
681
718
|
var detectAbsoluteMobileHost = (platform = process.platform, wsl = isWSLEnvironment()) => {
|
|
682
719
|
if (platform === "darwin")
|
|
683
720
|
return "macos";
|
|
@@ -687,10 +724,151 @@ var detectAbsoluteMobileHost = (platform = process.platform, wsl = isWSLEnvironm
|
|
|
687
724
|
return "wsl";
|
|
688
725
|
return "linux";
|
|
689
726
|
};
|
|
727
|
+
var executableNames = (host, name) => {
|
|
728
|
+
if (host === "wsl")
|
|
729
|
+
return [`${name}.exe`, `${name}.bat`, name];
|
|
730
|
+
if (host === "windows")
|
|
731
|
+
return [name, `${name}.exe`, `${name}.bat`];
|
|
732
|
+
return [name];
|
|
733
|
+
};
|
|
734
|
+
var findExecutable = async (name, paths, options, host) => {
|
|
735
|
+
const existing = await Promise.all(paths.map(async (path) => await options.exists(path) ? path : undefined));
|
|
736
|
+
const configured = existing.find((path) => path !== undefined);
|
|
737
|
+
if (configured)
|
|
738
|
+
return configured;
|
|
739
|
+
for (const candidate of executableNames(host, name)) {
|
|
740
|
+
const path = options.which(candidate);
|
|
741
|
+
if (path)
|
|
742
|
+
return path;
|
|
743
|
+
}
|
|
744
|
+
return;
|
|
745
|
+
};
|
|
746
|
+
var toolCheck = (id, label, platform, path, remediation) => path ? {
|
|
747
|
+
id,
|
|
748
|
+
label,
|
|
749
|
+
path,
|
|
750
|
+
platform,
|
|
751
|
+
status: "pass"
|
|
752
|
+
} : {
|
|
753
|
+
id,
|
|
754
|
+
label,
|
|
755
|
+
platform,
|
|
756
|
+
remediation,
|
|
757
|
+
status: "fail"
|
|
758
|
+
};
|
|
759
|
+
var inspectAbsoluteMobileToolchain = async (input = {}) => {
|
|
760
|
+
const env = input.env ?? process.env;
|
|
761
|
+
const host = input.host ?? detectAbsoluteMobileHost();
|
|
762
|
+
const exists = input.exists ?? pathExists;
|
|
763
|
+
const which = input.which ?? ((command) => Bun.which(command));
|
|
764
|
+
const capture = input.capture ?? captureCommand;
|
|
765
|
+
const androidRoot = input.androidRoot === null ? undefined : input.androidRoot ?? env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host, env);
|
|
766
|
+
const windowsAndroidTools = host === "windows" || host === "wsl";
|
|
767
|
+
const android = (segments) => androidRoot ? join2(androidRoot, ...segments) : undefined;
|
|
768
|
+
const paths = (values) => values.filter((value) => Boolean(value));
|
|
769
|
+
const adb = await findExecutable("adb", paths([
|
|
770
|
+
android(["platform-tools", windowsAndroidTools ? "adb.exe" : "adb"])
|
|
771
|
+
]), { exists, which }, host);
|
|
772
|
+
const emulator = await findExecutable("emulator", paths([
|
|
773
|
+
android([
|
|
774
|
+
"emulator",
|
|
775
|
+
windowsAndroidTools ? "emulator.exe" : "emulator"
|
|
776
|
+
])
|
|
777
|
+
]), { exists, which }, host);
|
|
778
|
+
const sdkmanager = await findExecutable("sdkmanager", paths([
|
|
779
|
+
android([
|
|
780
|
+
"cmdline-tools",
|
|
781
|
+
"latest",
|
|
782
|
+
"bin",
|
|
783
|
+
windowsAndroidTools ? "sdkmanager.bat" : "sdkmanager"
|
|
784
|
+
])
|
|
785
|
+
]), { exists, which }, host);
|
|
786
|
+
const avdmanager = await findExecutable("avdmanager", paths([
|
|
787
|
+
android([
|
|
788
|
+
"cmdline-tools",
|
|
789
|
+
"latest",
|
|
790
|
+
"bin",
|
|
791
|
+
windowsAndroidTools ? "avdmanager.bat" : "avdmanager"
|
|
792
|
+
])
|
|
793
|
+
]), { exists, which }, host);
|
|
794
|
+
const java = await findExecutable("java", [], { exists, which }, host);
|
|
795
|
+
const checks = [
|
|
796
|
+
{
|
|
797
|
+
id: "host",
|
|
798
|
+
label: `Development host: ${host}`,
|
|
799
|
+
platform: "host",
|
|
800
|
+
status: "pass"
|
|
801
|
+
},
|
|
802
|
+
toolCheck("android.adb", "Android Debug Bridge", "android", adb, "Install Android SDK Platform Tools or expose adb on PATH."),
|
|
803
|
+
toolCheck("android.emulator", "Android Emulator", "android", emulator, "Install the Android Emulator from Android Studio SDK Manager."),
|
|
804
|
+
toolCheck("android.sdkmanager", "Android SDK Manager", "android", sdkmanager, "Install Android SDK Command-line Tools (latest)."),
|
|
805
|
+
toolCheck("android.avdmanager", "Android Virtual Device Manager", "android", avdmanager, "Install Android SDK Command-line Tools (latest)."),
|
|
806
|
+
toolCheck("android.java", "Java runtime", "android", java, "Install the JDK required by the configured Android Gradle plugin.")
|
|
807
|
+
];
|
|
808
|
+
if (emulator) {
|
|
809
|
+
const avds = capture([emulator, "-list-avds"]);
|
|
810
|
+
const hasManagedAvd = avds.exitCode === 0 && avds.stdout.split(/\r?\n/).includes(ABSOLUTE_ANDROID_AVD_NAME);
|
|
811
|
+
checks.push({
|
|
812
|
+
id: "android.avd",
|
|
813
|
+
label: `AbsoluteJS Android emulator (${ABSOLUTE_ANDROID_AVD_NAME})`,
|
|
814
|
+
platform: "android",
|
|
815
|
+
remediation: hasManagedAvd ? undefined : "Run absolute mobile doctor android --fix to provision the managed emulator.",
|
|
816
|
+
status: hasManagedAvd ? "pass" : "fail"
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
if (host === "wsl") {
|
|
820
|
+
checks.push({
|
|
821
|
+
id: "android.virtualization",
|
|
822
|
+
label: adb?.endsWith(".exe") ? "Windows-host Android bridge available to WSL" : "WSL requires a Windows-host emulator bridge or Linux KVM",
|
|
823
|
+
platform: "android",
|
|
824
|
+
remediation: adb?.endsWith(".exe") ? undefined : "Expose the Windows Android SDK adb.exe to WSL, or enable /dev/kvm for a Linux SDK.",
|
|
825
|
+
status: adb?.endsWith(".exe") ? "pass" : "warn"
|
|
826
|
+
});
|
|
827
|
+
} else if (host === "linux") {
|
|
828
|
+
const hasKvm = await exists("/dev/kvm");
|
|
829
|
+
checks.push({
|
|
830
|
+
id: "android.virtualization",
|
|
831
|
+
label: "Linux KVM acceleration",
|
|
832
|
+
platform: "android",
|
|
833
|
+
remediation: hasKvm ? undefined : "Enable KVM and grant the current user access to /dev/kvm.",
|
|
834
|
+
status: hasKvm ? "pass" : "warn"
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
if (host !== "macos") {
|
|
838
|
+
checks.push({
|
|
839
|
+
id: "ios.simulator",
|
|
840
|
+
label: "iOS Simulator requires macOS and Xcode",
|
|
841
|
+
platform: "ios",
|
|
842
|
+
status: "skip"
|
|
843
|
+
});
|
|
844
|
+
return checks;
|
|
845
|
+
}
|
|
846
|
+
const xcrun = await findExecutable("xcrun", [], { exists, which }, host);
|
|
847
|
+
const xcodebuild = await findExecutable("xcodebuild", [], { exists, which }, host);
|
|
848
|
+
checks.push(toolCheck("ios.xcrun", "Xcode command runner", "ios", xcrun, "Install Xcode and select it with xcode-select."), toolCheck("ios.xcodebuild", "Xcode build system", "ios", xcodebuild, "Install Xcode and select it with xcode-select."));
|
|
849
|
+
if (xcrun) {
|
|
850
|
+
const runtimes = capture([
|
|
851
|
+
xcrun,
|
|
852
|
+
"simctl",
|
|
853
|
+
"list",
|
|
854
|
+
"runtimes",
|
|
855
|
+
"--json"
|
|
856
|
+
]);
|
|
857
|
+
const hasRuntime = runtimes.exitCode === 0 && hasAvailableIosRuntime(runtimes.stdout);
|
|
858
|
+
checks.push({
|
|
859
|
+
id: "ios.runtime",
|
|
860
|
+
label: "iOS Simulator runtime",
|
|
861
|
+
platform: "ios",
|
|
862
|
+
remediation: hasRuntime ? undefined : "Run absolute mobile doctor ios --fix to download an iOS Simulator runtime.",
|
|
863
|
+
status: hasRuntime ? "pass" : "fail"
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
return checks;
|
|
867
|
+
};
|
|
690
868
|
|
|
691
869
|
// src/mobile/androidEmulatorController.ts
|
|
692
870
|
import {
|
|
693
|
-
access as
|
|
871
|
+
access as access3,
|
|
694
872
|
copyFile,
|
|
695
873
|
lstat,
|
|
696
874
|
mkdir as mkdir2,
|
|
@@ -714,7 +892,7 @@ import {
|
|
|
714
892
|
} from "path";
|
|
715
893
|
|
|
716
894
|
// src/mobile/capacitorProject.ts
|
|
717
|
-
import { access, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
|
|
895
|
+
import { access as access2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
|
|
718
896
|
import { relative, resolve } from "path";
|
|
719
897
|
var CONFIG_FILE = "capacitor.config.ts";
|
|
720
898
|
var portableRelative = (root, path) => relative(root, path).replaceAll("\\", "/");
|
|
@@ -736,7 +914,7 @@ export default config;
|
|
|
736
914
|
`;
|
|
737
915
|
var exists = async (path) => {
|
|
738
916
|
try {
|
|
739
|
-
await
|
|
917
|
+
await access2(path);
|
|
740
918
|
return true;
|
|
741
919
|
} catch {
|
|
742
920
|
return false;
|
|
@@ -766,9 +944,9 @@ var HASH_RADIX = 16;
|
|
|
766
944
|
var EXECUTABLE_MODE_MASK = 73;
|
|
767
945
|
var NATIVE_PUBLIC_PATH_SEGMENTS = 5;
|
|
768
946
|
var CAPACITOR_PROJECT_DIRECTORY_PATTERN = /project\(['"](:[^'"]+)['"]\)\.projectDir\s*=\s*new File\(['"]([^'"]+)['"]\)/gu;
|
|
769
|
-
var
|
|
947
|
+
var pathExists2 = async (path) => {
|
|
770
948
|
try {
|
|
771
|
-
await
|
|
949
|
+
await access3(path);
|
|
772
950
|
return true;
|
|
773
951
|
} catch {
|
|
774
952
|
return false;
|
|
@@ -795,7 +973,7 @@ var runCommand = async (command, options = {}) => {
|
|
|
795
973
|
]);
|
|
796
974
|
return exitCode;
|
|
797
975
|
};
|
|
798
|
-
var
|
|
976
|
+
var captureCommand2 = (command, options = {}) => {
|
|
799
977
|
try {
|
|
800
978
|
const result = Bun.spawnSync(command, {
|
|
801
979
|
cwd: options.cwd,
|
|
@@ -965,14 +1143,14 @@ var gradleArtifactPath = (nativeDirectory, task, windows = false) => {
|
|
|
965
1143
|
};
|
|
966
1144
|
var resolveGradleArtifactPath = async (nativeDirectory, task) => {
|
|
967
1145
|
const primary = gradleArtifactPath(nativeDirectory, task);
|
|
968
|
-
if (task !== "assembleRelease" || await
|
|
1146
|
+
if (task !== "assembleRelease" || await pathExists2(primary)) {
|
|
969
1147
|
return primary;
|
|
970
1148
|
}
|
|
971
1149
|
return join3(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
|
|
972
1150
|
};
|
|
973
1151
|
var buildAbsoluteAndroidGradleArtifact = async (options) => {
|
|
974
1152
|
const { project, task } = options;
|
|
975
|
-
const capture = options.capture ??
|
|
1153
|
+
const capture = options.capture ?? captureCommand2;
|
|
976
1154
|
const run = options.run ?? runCommand;
|
|
977
1155
|
const env = options.env ?? process.env;
|
|
978
1156
|
const gradleArguments = options.gradleArguments ?? [];
|
|
@@ -1015,9 +1193,9 @@ var requireManifest = (value) => {
|
|
|
1015
1193
|
runtime: value.runtime
|
|
1016
1194
|
};
|
|
1017
1195
|
};
|
|
1018
|
-
var
|
|
1196
|
+
var pathExists3 = async (path) => {
|
|
1019
1197
|
try {
|
|
1020
|
-
await
|
|
1198
|
+
await access4(path);
|
|
1021
1199
|
return true;
|
|
1022
1200
|
} catch {
|
|
1023
1201
|
return false;
|
|
@@ -1072,7 +1250,7 @@ var installRelease = async (artifactPath, metadata, outputRoot) => {
|
|
|
1072
1250
|
const releaseRoot = join4(outputRoot, metadata.releaseId);
|
|
1073
1251
|
const artifactName = "app-release.aab";
|
|
1074
1252
|
const destination = join4(releaseRoot, artifactName);
|
|
1075
|
-
if (await
|
|
1253
|
+
if (await pathExists3(releaseRoot)) {
|
|
1076
1254
|
const existing = requireManifestIdentity(JSON.parse(await readFile4(join4(releaseRoot, "release.json"), "utf8")), metadata);
|
|
1077
1255
|
const [installedBytes, installedSha256] = await Promise.all([
|
|
1078
1256
|
stat(destination).then(({ size }) => size),
|
|
@@ -1148,7 +1326,7 @@ var buildAbsoluteAndroidRelease = async (options) => {
|
|
|
1148
1326
|
run: options.run,
|
|
1149
1327
|
task: "bundleRelease"
|
|
1150
1328
|
});
|
|
1151
|
-
if (!await
|
|
1329
|
+
if (!await pathExists3(artifactPath)) {
|
|
1152
1330
|
throw new TypeError(`Android Gradle did not produce the expected App Bundle: ${artifactPath}`);
|
|
1153
1331
|
}
|
|
1154
1332
|
const capture = options.capture ?? defaultCapture;
|
|
@@ -1183,7 +1361,7 @@ var buildAbsoluteAndroidRelease = async (options) => {
|
|
|
1183
1361
|
// src/mobile/iosRelease.ts
|
|
1184
1362
|
import { createHash as createHash5 } from "crypto";
|
|
1185
1363
|
import {
|
|
1186
|
-
access as
|
|
1364
|
+
access as access5,
|
|
1187
1365
|
copyFile as copyFile3,
|
|
1188
1366
|
mkdir as mkdir4,
|
|
1189
1367
|
mkdtemp as mkdtemp3,
|
|
@@ -1207,9 +1385,9 @@ var requireManifest2 = (value) => {
|
|
|
1207
1385
|
runtime: value.runtime
|
|
1208
1386
|
};
|
|
1209
1387
|
};
|
|
1210
|
-
var
|
|
1388
|
+
var pathExists4 = async (path) => {
|
|
1211
1389
|
try {
|
|
1212
|
-
await
|
|
1390
|
+
await access5(path);
|
|
1213
1391
|
return true;
|
|
1214
1392
|
} catch {
|
|
1215
1393
|
return false;
|
|
@@ -1290,7 +1468,7 @@ var safeOutputDirectory2 = (projectRoot, requested) => {
|
|
|
1290
1468
|
};
|
|
1291
1469
|
var sha256File2 = async (path) => createHash5("sha256").update(await readFile5(path)).digest("hex");
|
|
1292
1470
|
var findByExtension = async (root, extension) => {
|
|
1293
|
-
if (!await
|
|
1471
|
+
if (!await pathExists4(root))
|
|
1294
1472
|
return;
|
|
1295
1473
|
const entries = await readdir3(root, { withFileTypes: true });
|
|
1296
1474
|
const matches = await Promise.all(entries.map(async (entry) => {
|
|
@@ -1322,7 +1500,7 @@ var requireBuildNumber = (value) => {
|
|
|
1322
1500
|
var installRelease2 = async (artifactPath, metadata, outputRoot) => {
|
|
1323
1501
|
const releaseRoot = join5(outputRoot, metadata.releaseId);
|
|
1324
1502
|
const destination = join5(releaseRoot, "App.ipa");
|
|
1325
|
-
if (await
|
|
1503
|
+
if (await pathExists4(releaseRoot)) {
|
|
1326
1504
|
const value = JSON.parse(await readFile5(join5(releaseRoot, "release.json"), "utf8"));
|
|
1327
1505
|
if (!isRecord2(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
|
|
1328
1506
|
throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
|
|
@@ -1461,28 +1639,922 @@ var buildAbsoluteIosRelease = async (options) => {
|
|
|
1461
1639
|
});
|
|
1462
1640
|
}
|
|
1463
1641
|
};
|
|
1464
|
-
// src/mobile/
|
|
1642
|
+
// src/mobile/iosConformance.ts
|
|
1643
|
+
import { readFile as readFile6, stat as stat3 } from "fs/promises";
|
|
1644
|
+
var HMR_LINE = new RegExp(String.raw`\[hmr:ios\]\s+([^\n]*?)\s+(applied in|falling back to reload after|failed after)\s+(\d+)ms(?:; server\s+(\d+)ms, client\s+(\d+)ms)?`, "u");
|
|
1645
|
+
var parseAbsoluteIosHmrLog = (line) => {
|
|
1646
|
+
const match = HMR_LINE.exec(line);
|
|
1647
|
+
if (!match)
|
|
1648
|
+
return null;
|
|
1649
|
+
const [, , action, durationValue, serverValue, clientValue] = match;
|
|
1650
|
+
let outcome = "reloaded";
|
|
1651
|
+
if (action === "applied in")
|
|
1652
|
+
outcome = "applied";
|
|
1653
|
+
if (action === "failed after")
|
|
1654
|
+
outcome = "failed";
|
|
1655
|
+
const serverMs = serverValue === undefined ? undefined : Number(serverValue);
|
|
1656
|
+
const clientMs = clientValue === undefined ? undefined : Number(clientValue);
|
|
1657
|
+
return {
|
|
1658
|
+
...clientMs === undefined ? {} : { clientMs },
|
|
1659
|
+
duration: Number(durationValue),
|
|
1660
|
+
line: match[0],
|
|
1661
|
+
outcome,
|
|
1662
|
+
...serverMs === undefined ? {} : { serverMs }
|
|
1663
|
+
};
|
|
1664
|
+
};
|
|
1665
|
+
var findHmrApply = (lines) => {
|
|
1666
|
+
const apply = lines.map((line) => parseAbsoluteIosHmrLog(line)).find((candidate) => candidate !== null);
|
|
1667
|
+
if (apply?.outcome === "failed")
|
|
1668
|
+
throw new Error(`iOS HMR client reported a failed apply: ${apply.line}`);
|
|
1669
|
+
return apply;
|
|
1670
|
+
};
|
|
1671
|
+
var waitForAbsoluteIosHmrLog = async (options) => {
|
|
1672
|
+
const sleep = options.sleep ?? Bun.sleep;
|
|
1673
|
+
const timeoutMs = options.timeoutMs ?? 30000;
|
|
1674
|
+
const deadline = Date.now() + timeoutMs;
|
|
1675
|
+
let offset = options.startOffset ?? await stat3(options.logPath).then(({ size }) => size).catch(() => 0);
|
|
1676
|
+
let buffered = "";
|
|
1677
|
+
const poll = async () => {
|
|
1678
|
+
if (Date.now() > deadline)
|
|
1679
|
+
throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
|
|
1680
|
+
options.signal?.throwIfAborted();
|
|
1681
|
+
const contents = await readFile6(options.logPath).catch(() => Buffer.alloc(0));
|
|
1682
|
+
if (contents.byteLength < offset) {
|
|
1683
|
+
offset = 0;
|
|
1684
|
+
buffered = "";
|
|
1685
|
+
}
|
|
1686
|
+
if (contents.byteLength > offset) {
|
|
1687
|
+
buffered += contents.subarray(offset).toString("utf8");
|
|
1688
|
+
offset = contents.byteLength;
|
|
1689
|
+
const lines = buffered.split(/\r?\n/u);
|
|
1690
|
+
buffered = lines.pop() ?? "";
|
|
1691
|
+
const apply = findHmrApply(lines);
|
|
1692
|
+
if (apply)
|
|
1693
|
+
return apply;
|
|
1694
|
+
}
|
|
1695
|
+
await sleep(100);
|
|
1696
|
+
return poll();
|
|
1697
|
+
};
|
|
1698
|
+
return poll();
|
|
1699
|
+
};
|
|
1700
|
+
// src/mobile/iosSimulatorController.ts
|
|
1701
|
+
import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
|
|
1465
1702
|
import {
|
|
1466
|
-
access as
|
|
1703
|
+
access as access6,
|
|
1704
|
+
copyFile as copyFile4,
|
|
1467
1705
|
mkdir as mkdir5,
|
|
1468
|
-
readFile as
|
|
1706
|
+
readFile as readFile7,
|
|
1469
1707
|
rename as rename6,
|
|
1470
1708
|
rm as rm5,
|
|
1471
1709
|
writeFile as writeFile6
|
|
1472
1710
|
} from "fs/promises";
|
|
1473
|
-
import { resolve as
|
|
1711
|
+
import { dirname as dirname4, isAbsolute as isAbsolute4, join as join6, relative as relative5, resolve as resolve5, sep as sep4 } from "path";
|
|
1712
|
+
init_getDurationString();
|
|
1713
|
+
var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone";
|
|
1714
|
+
var BOOT_TIMEOUT_MS = 180000;
|
|
1715
|
+
var BOOT_POLL_MS = 1000;
|
|
1716
|
+
var DEV_JOURNAL_FORMAT = 1;
|
|
1717
|
+
var NATIVE_CACHE_FORMAT = 1;
|
|
1718
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1719
|
+
var pathExists5 = async (path) => {
|
|
1720
|
+
try {
|
|
1721
|
+
await access6(path);
|
|
1722
|
+
return true;
|
|
1723
|
+
} catch {
|
|
1724
|
+
return false;
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
var throwIfAborted2 = (signal) => signal?.throwIfAborted();
|
|
1728
|
+
var defaultCapture3 = (command, options = {}) => {
|
|
1729
|
+
try {
|
|
1730
|
+
const result = Bun.spawnSync(command, {
|
|
1731
|
+
cwd: options.cwd,
|
|
1732
|
+
env: options.env,
|
|
1733
|
+
stderr: "pipe",
|
|
1734
|
+
stdin: "ignore",
|
|
1735
|
+
stdout: "pipe"
|
|
1736
|
+
});
|
|
1737
|
+
return {
|
|
1738
|
+
exitCode: result.exitCode,
|
|
1739
|
+
stderr: result.stderr.toString(),
|
|
1740
|
+
stdout: result.stdout.toString()
|
|
1741
|
+
};
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
return {
|
|
1744
|
+
exitCode: 1,
|
|
1745
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
1746
|
+
stdout: ""
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
var defaultRun2 = async (command, options = {}) => {
|
|
1751
|
+
const process2 = Bun.spawn(command, {
|
|
1752
|
+
cwd: options.cwd,
|
|
1753
|
+
env: options.env,
|
|
1754
|
+
signal: options.signal,
|
|
1755
|
+
stderr: "inherit",
|
|
1756
|
+
stdin: "inherit",
|
|
1757
|
+
stdout: "inherit"
|
|
1758
|
+
});
|
|
1759
|
+
return process2.exited;
|
|
1760
|
+
};
|
|
1761
|
+
var defaultSpawn = (command, options = {}) => {
|
|
1762
|
+
Bun.spawn(command, {
|
|
1763
|
+
cwd: options.cwd,
|
|
1764
|
+
env: options.env,
|
|
1765
|
+
signal: options.signal,
|
|
1766
|
+
stderr: "ignore",
|
|
1767
|
+
stdin: "ignore",
|
|
1768
|
+
stdout: "ignore"
|
|
1769
|
+
});
|
|
1770
|
+
};
|
|
1771
|
+
var consumeLines = async (stream, onLine) => {
|
|
1772
|
+
const reader = stream.getReader();
|
|
1773
|
+
const decoder = new TextDecoder;
|
|
1774
|
+
let buffered = "";
|
|
1775
|
+
const pump = async () => {
|
|
1776
|
+
const { done, value } = await reader.read();
|
|
1777
|
+
if (done)
|
|
1778
|
+
return;
|
|
1779
|
+
buffered += decoder.decode(value, { stream: true });
|
|
1780
|
+
const lines = buffered.split(/\r?\n/u);
|
|
1781
|
+
buffered = lines.pop() ?? "";
|
|
1782
|
+
lines.forEach(onLine);
|
|
1783
|
+
await pump();
|
|
1784
|
+
};
|
|
1785
|
+
try {
|
|
1786
|
+
await pump();
|
|
1787
|
+
buffered += decoder.decode();
|
|
1788
|
+
if (buffered)
|
|
1789
|
+
onLine(buffered);
|
|
1790
|
+
} finally {
|
|
1791
|
+
reader.releaseLock();
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
var defaultStartNativeLogs = (command, options, onLine) => {
|
|
1795
|
+
const process2 = Bun.spawn(command, {
|
|
1796
|
+
cwd: options.cwd,
|
|
1797
|
+
env: options.env,
|
|
1798
|
+
signal: options.signal,
|
|
1799
|
+
stderr: "pipe",
|
|
1800
|
+
stdin: "ignore",
|
|
1801
|
+
stdout: "pipe"
|
|
1802
|
+
});
|
|
1803
|
+
consumeLines(process2.stdout, onLine);
|
|
1804
|
+
consumeLines(process2.stderr, onLine);
|
|
1805
|
+
return {
|
|
1806
|
+
close: async () => {
|
|
1807
|
+
try {
|
|
1808
|
+
process2.kill();
|
|
1809
|
+
} catch {}
|
|
1810
|
+
await process2.exited.catch(() => {
|
|
1811
|
+
return;
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
};
|
|
1816
|
+
var requireSuccess2 = async (command, label, run, options) => {
|
|
1817
|
+
const exitCode = await run(command, options);
|
|
1818
|
+
if (exitCode !== 0)
|
|
1819
|
+
throw new Error(`${label} failed with status ${exitCode}.`);
|
|
1820
|
+
};
|
|
1821
|
+
var requireCapturedSuccess = (result, label) => {
|
|
1822
|
+
if (result.exitCode !== 0) {
|
|
1823
|
+
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
1824
|
+
}
|
|
1825
|
+
return result.stdout.trim();
|
|
1826
|
+
};
|
|
1827
|
+
var parseJson = (source, label) => {
|
|
1828
|
+
try {
|
|
1829
|
+
const parsed = JSON.parse(source);
|
|
1830
|
+
if (isRecord3(parsed))
|
|
1831
|
+
return parsed;
|
|
1832
|
+
} catch {}
|
|
1833
|
+
throw new Error(`Invalid ${label} JSON from simctl.`);
|
|
1834
|
+
};
|
|
1835
|
+
var parseIosDeviceTypes = (source) => {
|
|
1836
|
+
const parsed = parseJson(source, "device type");
|
|
1837
|
+
const types = parsed.devicetypes;
|
|
1838
|
+
if (!Array.isArray(types))
|
|
1839
|
+
return [];
|
|
1840
|
+
return types.flatMap((type) => {
|
|
1841
|
+
if (!isRecord3(type))
|
|
1842
|
+
return [];
|
|
1843
|
+
const { identifier } = type;
|
|
1844
|
+
const { name } = type;
|
|
1845
|
+
return typeof identifier === "string" && typeof name === "string" ? [{ identifier, name }] : [];
|
|
1846
|
+
});
|
|
1847
|
+
};
|
|
1848
|
+
var parseIosRuntimes = (source) => {
|
|
1849
|
+
const parsed = parseJson(source, "runtime");
|
|
1850
|
+
const { runtimes } = parsed;
|
|
1851
|
+
if (!Array.isArray(runtimes))
|
|
1852
|
+
return [];
|
|
1853
|
+
return runtimes.flatMap((runtime) => {
|
|
1854
|
+
if (!isRecord3(runtime))
|
|
1855
|
+
return [];
|
|
1856
|
+
const { identifier } = runtime;
|
|
1857
|
+
const { name } = runtime;
|
|
1858
|
+
const { version } = runtime;
|
|
1859
|
+
if (typeof identifier !== "string" || typeof name !== "string" || typeof version !== "string")
|
|
1860
|
+
return [];
|
|
1861
|
+
return [
|
|
1862
|
+
{
|
|
1863
|
+
identifier,
|
|
1864
|
+
isAvailable: runtime.isAvailable !== false,
|
|
1865
|
+
name,
|
|
1866
|
+
version
|
|
1867
|
+
}
|
|
1868
|
+
];
|
|
1869
|
+
});
|
|
1870
|
+
};
|
|
1871
|
+
var parseIosSimulators = (source) => {
|
|
1872
|
+
const parsed = parseJson(source, "device");
|
|
1873
|
+
const { devices } = parsed;
|
|
1874
|
+
if (!isRecord3(devices))
|
|
1875
|
+
return [];
|
|
1876
|
+
return Object.entries(devices).flatMap(([runtime, values]) => {
|
|
1877
|
+
if (!Array.isArray(values))
|
|
1878
|
+
return [];
|
|
1879
|
+
return values.flatMap((device) => {
|
|
1880
|
+
if (!isRecord3(device))
|
|
1881
|
+
return [];
|
|
1882
|
+
const { name } = device;
|
|
1883
|
+
const { state } = device;
|
|
1884
|
+
const { udid } = device;
|
|
1885
|
+
if (typeof name !== "string" || typeof state !== "string" || typeof udid !== "string")
|
|
1886
|
+
return [];
|
|
1887
|
+
return [
|
|
1888
|
+
{
|
|
1889
|
+
isAvailable: device.isAvailable !== false,
|
|
1890
|
+
name,
|
|
1891
|
+
runtime,
|
|
1892
|
+
state,
|
|
1893
|
+
udid
|
|
1894
|
+
}
|
|
1895
|
+
];
|
|
1896
|
+
});
|
|
1897
|
+
});
|
|
1898
|
+
};
|
|
1899
|
+
var versionParts = (version) => version.split(".").map((part) => Number(part));
|
|
1900
|
+
var compareVersions = (left, right) => {
|
|
1901
|
+
const leftParts = versionParts(left);
|
|
1902
|
+
const rightParts = versionParts(right);
|
|
1903
|
+
const length = Math.max(leftParts.length, rightParts.length);
|
|
1904
|
+
for (let index = 0;index < length; index++) {
|
|
1905
|
+
const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
|
|
1906
|
+
if (difference !== 0)
|
|
1907
|
+
return difference;
|
|
1908
|
+
}
|
|
1909
|
+
return 0;
|
|
1910
|
+
};
|
|
1911
|
+
var latestIosRuntime = (runtimes) => runtimes.filter((runtime) => runtime.isAvailable && runtime.identifier.includes("SimRuntime.iOS-")).sort((left, right) => compareVersions(right.version, left.version))[0];
|
|
1912
|
+
var iphoneGeneration = (name) => Number(/iPhone\s+(\d+)/u.exec(name)?.[1] ?? 0);
|
|
1913
|
+
var preferredIphoneType = (types) => types.filter((type) => type.name.startsWith("iPhone")).sort((left, right) => {
|
|
1914
|
+
const generation = iphoneGeneration(right.name) - iphoneGeneration(left.name);
|
|
1915
|
+
if (generation !== 0)
|
|
1916
|
+
return generation;
|
|
1917
|
+
const rightPro = right.name.includes("Pro") ? 1 : 0;
|
|
1918
|
+
const leftPro = left.name.includes("Pro") ? 1 : 0;
|
|
1919
|
+
return rightPro - leftPro;
|
|
1920
|
+
})[0];
|
|
1921
|
+
var journalPaths = (projectRoot) => {
|
|
1922
|
+
const root = join6(projectRoot, ".absolutejs", "mobile", "ios-dev-session");
|
|
1923
|
+
return {
|
|
1924
|
+
configBackup: join6(root, "capacitor-config.backup"),
|
|
1925
|
+
infoBackup: join6(root, "Info.plist.backup"),
|
|
1926
|
+
journal: join6(root, "journal.json"),
|
|
1927
|
+
root
|
|
1928
|
+
};
|
|
1929
|
+
};
|
|
1930
|
+
var nativeCachePath = (projectRoot) => join6(projectRoot, ".absolutejs", "mobile", "ios-native-cache.json");
|
|
1931
|
+
var isInside = (root, path) => {
|
|
1932
|
+
const value = relative5(resolve5(root), resolve5(path));
|
|
1933
|
+
return value === "" || !value.startsWith(`..${sep4}`) && value !== ".." && !isAbsolute4(value);
|
|
1934
|
+
};
|
|
1935
|
+
var parseJournal = (value) => {
|
|
1936
|
+
if (!isRecord3(value) || value.format !== DEV_JOURNAL_FORMAT)
|
|
1937
|
+
return null;
|
|
1938
|
+
const { configBackupPath } = value;
|
|
1939
|
+
const { infoBackupPath } = value;
|
|
1940
|
+
const { infoPath } = value;
|
|
1941
|
+
const { nativeConfigPath } = value;
|
|
1942
|
+
if (typeof configBackupPath !== "string" || typeof infoBackupPath !== "string" || typeof infoPath !== "string" || typeof nativeConfigPath !== "string")
|
|
1943
|
+
return null;
|
|
1944
|
+
return {
|
|
1945
|
+
configBackupPath,
|
|
1946
|
+
format: DEV_JOURNAL_FORMAT,
|
|
1947
|
+
infoBackupPath,
|
|
1948
|
+
infoPath,
|
|
1949
|
+
nativeConfigPath
|
|
1950
|
+
};
|
|
1951
|
+
};
|
|
1952
|
+
var repairAbsoluteIosDevSession = async (projectRoot) => {
|
|
1953
|
+
const paths = journalPaths(projectRoot);
|
|
1954
|
+
if (!await pathExists5(paths.journal)) {
|
|
1955
|
+
await rm5(paths.root, { force: true, recursive: true });
|
|
1956
|
+
return false;
|
|
1957
|
+
}
|
|
1958
|
+
const journal = await readFile7(paths.journal, "utf8").then((source) => parseJournal(JSON.parse(source))).catch(() => null);
|
|
1959
|
+
if (!journal || !isInside(projectRoot, journal.nativeConfigPath) || !isInside(projectRoot, journal.infoPath) || !isInside(paths.root, journal.configBackupPath) || !isInside(paths.root, journal.infoBackupPath)) {
|
|
1960
|
+
throw new Error(`Refusing unsafe or invalid iOS dev journal at ${paths.journal}.`);
|
|
1961
|
+
}
|
|
1962
|
+
if (await pathExists5(journal.configBackupPath))
|
|
1963
|
+
await copyFile4(journal.configBackupPath, journal.nativeConfigPath);
|
|
1964
|
+
if (await pathExists5(journal.infoBackupPath))
|
|
1965
|
+
await copyFile4(journal.infoBackupPath, journal.infoPath);
|
|
1966
|
+
await rm5(paths.root, { force: true, recursive: true });
|
|
1967
|
+
return true;
|
|
1968
|
+
};
|
|
1969
|
+
var iosDevelopmentInfoPlist = (source, cleartext) => {
|
|
1970
|
+
if (!cleartext)
|
|
1971
|
+
return source;
|
|
1972
|
+
const arbitraryLoads = /(<key>NSAllowsArbitraryLoads<\/key>\s*)<false\s*\/>/u;
|
|
1973
|
+
if (arbitraryLoads.test(source))
|
|
1974
|
+
return source.replace(arbitraryLoads, "$1<true/>");
|
|
1975
|
+
if (/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(source))
|
|
1976
|
+
return source;
|
|
1977
|
+
const transport = /(<key>NSAppTransportSecurity<\/key>\s*<dict>)/u;
|
|
1978
|
+
if (transport.test(source))
|
|
1979
|
+
return source.replace(transport, `$1
|
|
1980
|
+
<key>NSAllowsArbitraryLoads</key>
|
|
1981
|
+
<true/>`);
|
|
1982
|
+
return source.replace(/<dict>/u, `<dict>
|
|
1983
|
+
<key>NSAppTransportSecurity</key>
|
|
1984
|
+
<dict>
|
|
1985
|
+
<key>NSAllowsArbitraryLoads</key>
|
|
1986
|
+
<true/>
|
|
1987
|
+
</dict>`);
|
|
1988
|
+
};
|
|
1989
|
+
var writeDevProjection = async (project, port, https) => {
|
|
1990
|
+
const paths = journalPaths(project.projectRoot);
|
|
1991
|
+
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
1992
|
+
const nativeConfigPath = join6(project.nativeDirectory, "App", "App", "capacitor.config.json");
|
|
1993
|
+
const infoPath = join6(project.nativeDirectory, "App", "App", "Info.plist");
|
|
1994
|
+
const [configSource, infoSource] = await Promise.all([
|
|
1995
|
+
readFile7(nativeConfigPath, "utf8"),
|
|
1996
|
+
readFile7(infoPath, "utf8")
|
|
1997
|
+
]);
|
|
1998
|
+
const parsed = JSON.parse(configSource);
|
|
1999
|
+
if (!isRecord3(parsed))
|
|
2000
|
+
throw new Error(`Invalid Capacitor native config at ${nativeConfigPath}.`);
|
|
2001
|
+
await mkdir5(paths.root, { recursive: true });
|
|
2002
|
+
await Promise.all([
|
|
2003
|
+
writeFile6(paths.configBackup, configSource, { flag: "wx" }),
|
|
2004
|
+
writeFile6(paths.infoBackup, infoSource, { flag: "wx" })
|
|
2005
|
+
]);
|
|
2006
|
+
const journal = {
|
|
2007
|
+
configBackupPath: paths.configBackup,
|
|
2008
|
+
format: DEV_JOURNAL_FORMAT,
|
|
2009
|
+
infoBackupPath: paths.infoBackup,
|
|
2010
|
+
infoPath,
|
|
2011
|
+
nativeConfigPath
|
|
2012
|
+
};
|
|
2013
|
+
await writeFile6(paths.journal, `${JSON.stringify(journal, null, "\t")}
|
|
2014
|
+
`, {
|
|
2015
|
+
flag: "wx"
|
|
2016
|
+
});
|
|
2017
|
+
const developmentUrl = new URL(`${https ? "https" : "http"}://localhost:${port}${project.config.entry}`);
|
|
2018
|
+
developmentUrl.searchParams.set("__absolute_target", "capacitor-ios");
|
|
2019
|
+
const existingServer = parsed.server;
|
|
2020
|
+
parsed.server = {
|
|
2021
|
+
...isRecord3(existingServer) ? existingServer : {},
|
|
2022
|
+
cleartext: !https,
|
|
2023
|
+
url: developmentUrl.href
|
|
2024
|
+
};
|
|
2025
|
+
await Promise.all([
|
|
2026
|
+
writeFile6(nativeConfigPath, `${JSON.stringify(parsed, null, "\t")}
|
|
2027
|
+
`),
|
|
2028
|
+
writeFile6(infoPath, iosDevelopmentInfoPlist(infoSource, !https))
|
|
2029
|
+
]);
|
|
2030
|
+
};
|
|
2031
|
+
var parseNativeCache = (value) => {
|
|
2032
|
+
if (!isRecord3(value))
|
|
2033
|
+
return null;
|
|
2034
|
+
const { appId, fingerprint, format, installations } = value;
|
|
2035
|
+
if (format !== NATIVE_CACHE_FORMAT || typeof appId !== "string" || typeof fingerprint !== "string" || !isRecord3(installations) || !Object.values(installations).every((identity) => typeof identity === "string"))
|
|
2036
|
+
return null;
|
|
2037
|
+
return {
|
|
2038
|
+
appId,
|
|
2039
|
+
fingerprint,
|
|
2040
|
+
format,
|
|
2041
|
+
installations: Object.fromEntries(Object.entries(installations).map(([udid, identity]) => [
|
|
2042
|
+
udid,
|
|
2043
|
+
String(identity)
|
|
2044
|
+
]))
|
|
2045
|
+
};
|
|
2046
|
+
};
|
|
2047
|
+
var readNativeCache = (projectRoot) => readFile7(nativeCachePath(projectRoot), "utf8").then((source) => parseNativeCache(JSON.parse(source))).catch(() => null);
|
|
2048
|
+
var writeNativeCache = async (projectRoot, cache) => {
|
|
2049
|
+
const destination = nativeCachePath(projectRoot);
|
|
2050
|
+
const temporary = `${destination}.${process.pid}.${randomUUID2()}.tmp`;
|
|
2051
|
+
await mkdir5(dirname4(destination), { recursive: true });
|
|
2052
|
+
try {
|
|
2053
|
+
await writeFile6(temporary, `${JSON.stringify(cache, null, "\t")}
|
|
2054
|
+
`, {
|
|
2055
|
+
flag: "wx"
|
|
2056
|
+
});
|
|
2057
|
+
await rename6(temporary, destination);
|
|
2058
|
+
} finally {
|
|
2059
|
+
await rm5(temporary, { force: true }).catch(() => {
|
|
2060
|
+
return;
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
2064
|
+
var fingerprintAbsoluteIosDevProject = async (project) => fingerprintAbsoluteIosNativeProject(project.nativeDirectory);
|
|
2065
|
+
var simulatorInventory = (xcrun, capture) => {
|
|
2066
|
+
const result = capture([
|
|
2067
|
+
xcrun,
|
|
2068
|
+
"simctl",
|
|
2069
|
+
"list",
|
|
2070
|
+
"devices",
|
|
2071
|
+
"available",
|
|
2072
|
+
"-j"
|
|
2073
|
+
]);
|
|
2074
|
+
return parseIosSimulators(requireCapturedSuccess(result, "iOS simulator discovery"));
|
|
2075
|
+
};
|
|
2076
|
+
var ensureManagedSimulator = async (project, capture) => {
|
|
2077
|
+
const runtimes = parseIosRuntimes(requireCapturedSuccess(capture([project.xcrun, "simctl", "list", "runtimes", "-j"]), "iOS runtime discovery"));
|
|
2078
|
+
const runtime = latestIosRuntime(runtimes);
|
|
2079
|
+
if (!runtime)
|
|
2080
|
+
throw new Error("No available iOS Simulator runtime. Run absolute mobile doctor ios --fix.");
|
|
2081
|
+
const [existing] = simulatorInventory(project.xcrun, capture).filter((device) => device.isAvailable && device.name === ABSOLUTE_IOS_SIMULATOR_NAME && device.runtime === runtime.identifier).sort((left, right) => Number(right.state === "Booted") - Number(left.state === "Booted"));
|
|
2082
|
+
if (existing)
|
|
2083
|
+
return { created: false, device: existing };
|
|
2084
|
+
const types = parseIosDeviceTypes(requireCapturedSuccess(capture([project.xcrun, "simctl", "list", "devicetypes", "-j"]), "iOS device-type discovery"));
|
|
2085
|
+
const type = preferredIphoneType(types);
|
|
2086
|
+
if (!type)
|
|
2087
|
+
throw new Error("Xcode did not report an iPhone simulator type.");
|
|
2088
|
+
const [udid] = requireCapturedSuccess(capture([
|
|
2089
|
+
project.xcrun,
|
|
2090
|
+
"simctl",
|
|
2091
|
+
"create",
|
|
2092
|
+
ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
2093
|
+
type.identifier,
|
|
2094
|
+
runtime.identifier
|
|
2095
|
+
]), "iOS simulator creation").split(/\s/u);
|
|
2096
|
+
if (!udid)
|
|
2097
|
+
throw new Error("simctl did not return the created simulator UDID.");
|
|
2098
|
+
return {
|
|
2099
|
+
created: true,
|
|
2100
|
+
device: {
|
|
2101
|
+
isAvailable: true,
|
|
2102
|
+
name: ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
2103
|
+
runtime: runtime.identifier,
|
|
2104
|
+
state: "Shutdown",
|
|
2105
|
+
udid
|
|
2106
|
+
}
|
|
2107
|
+
};
|
|
2108
|
+
};
|
|
2109
|
+
var waitForBootedSimulator = async (project, udid, capture, sleep, signal) => {
|
|
2110
|
+
const deadline = Date.now() + BOOT_TIMEOUT_MS;
|
|
2111
|
+
const poll = async () => {
|
|
2112
|
+
throwIfAborted2(signal);
|
|
2113
|
+
const device = simulatorInventory(project.xcrun, capture).find((candidate) => candidate.udid === udid);
|
|
2114
|
+
if (device?.state === "Booted")
|
|
2115
|
+
return;
|
|
2116
|
+
if (Date.now() > deadline)
|
|
2117
|
+
throw new Error(`iOS simulator ${udid} did not finish booting within ${BOOT_TIMEOUT_MS / 1000}s.`);
|
|
2118
|
+
await sleep(BOOT_POLL_MS);
|
|
2119
|
+
await poll();
|
|
2120
|
+
};
|
|
2121
|
+
return poll();
|
|
2122
|
+
};
|
|
2123
|
+
var bootSimulator = (project, device, capture) => {
|
|
2124
|
+
if (device.state === "Booted")
|
|
2125
|
+
return;
|
|
2126
|
+
requireCapturedSuccess(capture([project.xcrun, "simctl", "boot", device.udid]), "iOS simulator boot");
|
|
2127
|
+
};
|
|
2128
|
+
var installedAppIdentity = (project, udid, capture) => {
|
|
2129
|
+
const result = capture([
|
|
2130
|
+
project.xcrun,
|
|
2131
|
+
"simctl",
|
|
2132
|
+
"get_app_container",
|
|
2133
|
+
udid,
|
|
2134
|
+
project.config.appId,
|
|
2135
|
+
"app"
|
|
2136
|
+
]);
|
|
2137
|
+
return result.exitCode === 0 && result.stdout.trim() ? result.stdout.trim() : undefined;
|
|
2138
|
+
};
|
|
2139
|
+
var buildIosDebugApp = async (project, udid, fingerprint, run, signal) => {
|
|
2140
|
+
const derivedDataPath = join6(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash6("sha256").update(project.config.appId).digest("hex").slice(0, 16));
|
|
2141
|
+
await mkdir5(derivedDataPath, { recursive: true });
|
|
2142
|
+
await requireSuccess2([
|
|
2143
|
+
project.xcodebuild,
|
|
2144
|
+
"-workspace",
|
|
2145
|
+
join6(project.nativeDirectory, "App", "App.xcworkspace"),
|
|
2146
|
+
"-scheme",
|
|
2147
|
+
"App",
|
|
2148
|
+
"-configuration",
|
|
2149
|
+
"Debug",
|
|
2150
|
+
"-destination",
|
|
2151
|
+
`platform=iOS Simulator,id=${udid}`,
|
|
2152
|
+
"-derivedDataPath",
|
|
2153
|
+
derivedDataPath,
|
|
2154
|
+
"build"
|
|
2155
|
+
], "iOS simulator build", run, { cwd: project.nativeDirectory, signal });
|
|
2156
|
+
const appPath = join6(derivedDataPath, "Build", "Products", "Debug-iphonesimulator", "App.app");
|
|
2157
|
+
if (!await pathExists5(appPath))
|
|
2158
|
+
throw new Error(`Xcode did not produce the simulator app at ${appPath}.`);
|
|
2159
|
+
return appPath;
|
|
2160
|
+
};
|
|
2161
|
+
var ensureIosDebugApp = async (options) => {
|
|
2162
|
+
const installed = installedAppIdentity(options.project, options.udid, options.capture);
|
|
2163
|
+
const cacheHit = options.cache?.appId === options.project.config.appId && options.cache.fingerprint === options.fingerprint && installed !== undefined && options.cache.installations[options.udid] === installed;
|
|
2164
|
+
if (cacheHit) {
|
|
2165
|
+
options.log(`iOS native app is unchanged on ${options.udid}; skipped Xcode build and install.`);
|
|
2166
|
+
return true;
|
|
2167
|
+
}
|
|
2168
|
+
options.log("iOS native inputs changed or the installed app is stale; rebuilding.");
|
|
2169
|
+
options.transition("building");
|
|
2170
|
+
const appPath = await buildIosDebugApp(options.project, options.udid, options.fingerprint, options.run, options.signal);
|
|
2171
|
+
throwIfAborted2(options.signal);
|
|
2172
|
+
options.transition("installing");
|
|
2173
|
+
await requireSuccess2([options.project.xcrun, "simctl", "install", options.udid, appPath], "iOS simulator app installation", options.run, { signal: options.signal });
|
|
2174
|
+
const updated = installedAppIdentity(options.project, options.udid, options.capture);
|
|
2175
|
+
if (updated) {
|
|
2176
|
+
await writeNativeCache(options.project.projectRoot, {
|
|
2177
|
+
appId: options.project.config.appId,
|
|
2178
|
+
fingerprint: options.fingerprint,
|
|
2179
|
+
format: NATIVE_CACHE_FORMAT,
|
|
2180
|
+
installations: { [options.udid]: updated }
|
|
2181
|
+
}).catch((error) => options.log(`iOS native cache could not be saved: ${error instanceof Error ? error.message : String(error)}`));
|
|
2182
|
+
}
|
|
2183
|
+
return false;
|
|
2184
|
+
};
|
|
2185
|
+
var SECRET_VALUE = /((?:authorization|cookie|password|secret|token|oauth[_-]?code)\s*[:=]\s*)([^\s,;]+)/giu;
|
|
2186
|
+
var BEARER_VALUE = new RegExp(String.raw`\bBearer\s+[A-Za-z0-9._~+/-]+=*`, "giu");
|
|
2187
|
+
var JWT_VALUE = /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu;
|
|
2188
|
+
var IOS_LOG_PATTERN = new RegExp(String.raw`\s(Debug|Info|Notice|Error|Fault)\s+.*?\[[^\]]+\]\s+\[([^\]]+)\]\s*(.*)$`, "iu");
|
|
2189
|
+
var parseAbsoluteIosLogLine = (line) => {
|
|
2190
|
+
const sanitized = redactAbsoluteIosLog(line).trim();
|
|
2191
|
+
if (!sanitized)
|
|
2192
|
+
return null;
|
|
2193
|
+
const match = IOS_LOG_PATTERN.exec(sanitized);
|
|
2194
|
+
const candidate = match?.[1]?.toLowerCase();
|
|
2195
|
+
const level = candidate === "debug" || candidate === "error" || candidate === "fault" || candidate === "notice" ? candidate : "info";
|
|
2196
|
+
return {
|
|
2197
|
+
level,
|
|
2198
|
+
message: match?.[3]?.trim() || sanitized,
|
|
2199
|
+
tag: match?.[2]?.trim() || "App"
|
|
2200
|
+
};
|
|
2201
|
+
};
|
|
2202
|
+
var redactAbsoluteIosLog = (value) => value.replaceAll(BEARER_VALUE, "Bearer [REDACTED]").replaceAll(JWT_VALUE, "[REDACTED_JWT]").replaceAll(SECRET_VALUE, "$1[REDACTED]").replaceAll(/\p{C}/gu, "");
|
|
2203
|
+
var attachNativeLogs = (project, udid, options) => {
|
|
2204
|
+
if (!options.nativeLog)
|
|
2205
|
+
return null;
|
|
2206
|
+
const start = options.startNativeLogs ?? defaultStartNativeLogs;
|
|
2207
|
+
return start([
|
|
2208
|
+
project.xcrun,
|
|
2209
|
+
"simctl",
|
|
2210
|
+
"spawn",
|
|
2211
|
+
udid,
|
|
2212
|
+
"log",
|
|
2213
|
+
"stream",
|
|
2214
|
+
"--style",
|
|
2215
|
+
"compact",
|
|
2216
|
+
"--level",
|
|
2217
|
+
"debug",
|
|
2218
|
+
"--predicate",
|
|
2219
|
+
'process == "App"'
|
|
2220
|
+
], { signal: options.signal }, (line) => {
|
|
2221
|
+
const entry = parseAbsoluteIosLogLine(line);
|
|
2222
|
+
if (entry)
|
|
2223
|
+
options.nativeLog?.(entry);
|
|
2224
|
+
});
|
|
2225
|
+
};
|
|
2226
|
+
var IOS_TIMING_PHASES = [
|
|
2227
|
+
["syncing", "Capacitor sync"],
|
|
2228
|
+
["configuring", "dev config"],
|
|
2229
|
+
["fingerprinting", "fingerprint"],
|
|
2230
|
+
["booting", "simulator"],
|
|
2231
|
+
["connecting", "device ready"],
|
|
2232
|
+
["checking-native", "app check"],
|
|
2233
|
+
["building", "Xcode"],
|
|
2234
|
+
["installing", "install"],
|
|
2235
|
+
["launching", "launch"],
|
|
2236
|
+
["streaming-logs", "logs"]
|
|
2237
|
+
];
|
|
2238
|
+
var timingSummary = (timings) => IOS_TIMING_PHASES.map(([phase, label]) => {
|
|
2239
|
+
const duration = timings[phase];
|
|
2240
|
+
return duration === undefined ? null : `${label} ${getDurationString(duration)}`;
|
|
2241
|
+
}).filter((value) => value !== null).join(", ");
|
|
2242
|
+
var prepareAbsoluteIosDevProject = async (config, options) => {
|
|
2243
|
+
if (detectAbsoluteMobileHost() !== "macos")
|
|
2244
|
+
throw new Error("iOS simulation requires macOS and Xcode.");
|
|
2245
|
+
const projectRoot = resolve5(options.projectRoot);
|
|
2246
|
+
const checks = await inspectAbsoluteMobileToolchain({ host: "macos" });
|
|
2247
|
+
const failed = checks.filter((check) => check.platform === "ios" && (check.status === "fail" || check.status === "warn"));
|
|
2248
|
+
if (failed.length > 0)
|
|
2249
|
+
throw new Error(`iOS simulation is not ready: ${failed.map(({ label }) => label).join(", ")}.`);
|
|
2250
|
+
const xcrun = checks.find((check) => check.id === "ios.xcrun")?.path;
|
|
2251
|
+
const xcodebuild = checks.find((check) => check.id === "ios.xcodebuild")?.path;
|
|
2252
|
+
if (!xcrun || !xcodebuild)
|
|
2253
|
+
throw new Error("Xcode tools disappeared after readiness checks.");
|
|
2254
|
+
const cap = join6(projectRoot, "node_modules", ".bin", "cap");
|
|
2255
|
+
if (!await pathExists5(cap))
|
|
2256
|
+
throw new Error("Capacitor CLI is not installed. Run absolute mobile init first.");
|
|
2257
|
+
await writeAbsoluteCapacitorConfig(config, { projectRoot });
|
|
2258
|
+
await mkdir5(config.bundleDirectory, { recursive: true });
|
|
2259
|
+
const placeholder = join6(config.bundleDirectory, "index.html");
|
|
2260
|
+
if (!await pathExists5(placeholder))
|
|
2261
|
+
await writeFile6(placeholder, `<!doctype html><title>AbsoluteJS mobile development</title>
|
|
2262
|
+
`);
|
|
2263
|
+
const nativeDirectory = join6(config.nativeProjectDirectory, "ios");
|
|
2264
|
+
if (!await pathExists5(nativeDirectory)) {
|
|
2265
|
+
if (!options.createNativeProject)
|
|
2266
|
+
throw new Error("iOS native project has not been created.");
|
|
2267
|
+
const run = options.run ?? defaultRun2;
|
|
2268
|
+
if (await run([cap, "add", "ios"], { cwd: projectRoot }) !== 0)
|
|
2269
|
+
throw new Error("Capacitor iOS project creation failed.");
|
|
2270
|
+
}
|
|
2271
|
+
return {
|
|
2272
|
+
cap,
|
|
2273
|
+
config,
|
|
2274
|
+
nativeDirectory,
|
|
2275
|
+
projectRoot,
|
|
2276
|
+
xcodebuild,
|
|
2277
|
+
xcrun
|
|
2278
|
+
};
|
|
2279
|
+
};
|
|
2280
|
+
var startAbsoluteIosDevSession = async (options) => {
|
|
2281
|
+
const { project } = options;
|
|
2282
|
+
const capture = options.capture ?? defaultCapture3;
|
|
2283
|
+
const run = options.run ?? defaultRun2;
|
|
2284
|
+
const sleep = options.sleep ?? Bun.sleep;
|
|
2285
|
+
const spawn = options.spawn ?? defaultSpawn;
|
|
2286
|
+
const log = options.log ?? console.log;
|
|
2287
|
+
const startedAt = performance.now();
|
|
2288
|
+
let phaseStartedAt = performance.now();
|
|
2289
|
+
const timings = {};
|
|
2290
|
+
let state = "syncing";
|
|
2291
|
+
const transition = (next) => {
|
|
2292
|
+
if (next === state) {
|
|
2293
|
+
options.onStateChange?.(next);
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2296
|
+
const now = performance.now();
|
|
2297
|
+
const durationMs = now - phaseStartedAt;
|
|
2298
|
+
timings[state] = (timings[state] ?? 0) + durationMs;
|
|
2299
|
+
options.onPhaseTiming?.({
|
|
2300
|
+
durationMs,
|
|
2301
|
+
phase: state,
|
|
2302
|
+
totalMs: now - startedAt
|
|
2303
|
+
});
|
|
2304
|
+
state = next;
|
|
2305
|
+
phaseStartedAt = now;
|
|
2306
|
+
options.onStateChange?.(next);
|
|
2307
|
+
};
|
|
2308
|
+
let nativeLogs = null;
|
|
2309
|
+
const closeLogs = async () => {
|
|
2310
|
+
const stream = nativeLogs;
|
|
2311
|
+
nativeLogs = null;
|
|
2312
|
+
await stream?.close().catch(() => {
|
|
2313
|
+
return;
|
|
2314
|
+
});
|
|
2315
|
+
};
|
|
2316
|
+
try {
|
|
2317
|
+
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
2318
|
+
throwIfAborted2(options.signal);
|
|
2319
|
+
transition("syncing");
|
|
2320
|
+
await requireSuccess2([project.cap, "sync", "ios"], "Capacitor iOS synchronization", run, { cwd: project.projectRoot, signal: options.signal });
|
|
2321
|
+
transition("configuring");
|
|
2322
|
+
await writeDevProjection(project, options.port, options.https === true);
|
|
2323
|
+
throwIfAborted2(options.signal);
|
|
2324
|
+
const fingerprintStartedAt = performance.now();
|
|
2325
|
+
const fingerprintPromise = fingerprintAbsoluteIosDevProject(project).then((fingerprint2) => {
|
|
2326
|
+
timings.fingerprinting = performance.now() - fingerprintStartedAt;
|
|
2327
|
+
return fingerprint2;
|
|
2328
|
+
});
|
|
2329
|
+
transition("booting");
|
|
2330
|
+
const { created, device } = await ensureManagedSimulator(project, capture);
|
|
2331
|
+
const startedSimulator = created || device.state !== "Booted";
|
|
2332
|
+
bootSimulator(project, device, capture);
|
|
2333
|
+
spawn([
|
|
2334
|
+
"open",
|
|
2335
|
+
"-a",
|
|
2336
|
+
"Simulator",
|
|
2337
|
+
"--args",
|
|
2338
|
+
"-CurrentDeviceUDID",
|
|
2339
|
+
device.udid
|
|
2340
|
+
]);
|
|
2341
|
+
transition("connecting");
|
|
2342
|
+
await waitForBootedSimulator(project, device.udid, capture, sleep, options.signal);
|
|
2343
|
+
await requireSuccess2([project.xcrun, "simctl", "bootstatus", device.udid, "-b"], "iOS simulator boot readiness", run, { signal: options.signal });
|
|
2344
|
+
const fingerprint = await fingerprintPromise;
|
|
2345
|
+
transition("checking-native");
|
|
2346
|
+
const nativeCacheHit = await ensureIosDebugApp({
|
|
2347
|
+
cache: await readNativeCache(project.projectRoot),
|
|
2348
|
+
capture,
|
|
2349
|
+
fingerprint,
|
|
2350
|
+
log,
|
|
2351
|
+
project,
|
|
2352
|
+
run,
|
|
2353
|
+
signal: options.signal,
|
|
2354
|
+
transition,
|
|
2355
|
+
udid: device.udid
|
|
2356
|
+
});
|
|
2357
|
+
throwIfAborted2(options.signal);
|
|
2358
|
+
if (options.nativeLog)
|
|
2359
|
+
transition("streaming-logs");
|
|
2360
|
+
nativeLogs = attachNativeLogs(project, device.udid, options);
|
|
2361
|
+
transition("launching");
|
|
2362
|
+
await requireSuccess2([
|
|
2363
|
+
project.xcrun,
|
|
2364
|
+
"simctl",
|
|
2365
|
+
"launch",
|
|
2366
|
+
"--terminate-running-process",
|
|
2367
|
+
device.udid,
|
|
2368
|
+
project.config.appId
|
|
2369
|
+
], "iOS app launch", run, { signal: options.signal });
|
|
2370
|
+
transition("ready");
|
|
2371
|
+
timings.total = performance.now() - startedAt;
|
|
2372
|
+
log(`iOS simulator connected (${device.udid}) with HMR on port ${options.port} in ${getDurationString(timings.total)} (${nativeCacheHit ? "native cache hit" : "native build installed"}).`);
|
|
2373
|
+
log(`iOS startup: ${timingSummary(timings)}.`);
|
|
2374
|
+
let closed = false;
|
|
2375
|
+
const close = async () => {
|
|
2376
|
+
if (closed)
|
|
2377
|
+
return;
|
|
2378
|
+
closed = true;
|
|
2379
|
+
transition("closing");
|
|
2380
|
+
await closeLogs();
|
|
2381
|
+
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
2382
|
+
transition("closed");
|
|
2383
|
+
};
|
|
2384
|
+
return {
|
|
2385
|
+
close,
|
|
2386
|
+
nativeCacheHit,
|
|
2387
|
+
startedSimulator,
|
|
2388
|
+
timings: { ...timings },
|
|
2389
|
+
udid: device.udid,
|
|
2390
|
+
rebuild: async () => {
|
|
2391
|
+
if (closed)
|
|
2392
|
+
throw new Error("iOS development session is closed.");
|
|
2393
|
+
log("iOS native inputs changed; rebuilding without restarting the dev server.");
|
|
2394
|
+
await close();
|
|
2395
|
+
return startAbsoluteIosDevSession(options);
|
|
2396
|
+
},
|
|
2397
|
+
relaunch: async () => {
|
|
2398
|
+
if (closed)
|
|
2399
|
+
throw new Error("iOS development session is closed.");
|
|
2400
|
+
transition("launching");
|
|
2401
|
+
try {
|
|
2402
|
+
await requireSuccess2([
|
|
2403
|
+
project.xcrun,
|
|
2404
|
+
"simctl",
|
|
2405
|
+
"launch",
|
|
2406
|
+
"--terminate-running-process",
|
|
2407
|
+
device.udid,
|
|
2408
|
+
project.config.appId
|
|
2409
|
+
], "iOS app relaunch", run, { signal: options.signal });
|
|
2410
|
+
transition("ready");
|
|
2411
|
+
log(`iOS app relaunched on ${device.udid}.`);
|
|
2412
|
+
} catch (error) {
|
|
2413
|
+
transition("failed");
|
|
2414
|
+
throw error;
|
|
2415
|
+
}
|
|
2416
|
+
},
|
|
2417
|
+
screenshot: async (destination) => {
|
|
2418
|
+
const resolved = resolve5(project.projectRoot, destination);
|
|
2419
|
+
if (!isInside(project.projectRoot, resolved))
|
|
2420
|
+
throw new Error("iOS screenshot destination must remain inside the project.");
|
|
2421
|
+
await mkdir5(dirname4(resolved), { recursive: true });
|
|
2422
|
+
await requireSuccess2([
|
|
2423
|
+
project.xcrun,
|
|
2424
|
+
"simctl",
|
|
2425
|
+
"io",
|
|
2426
|
+
device.udid,
|
|
2427
|
+
"screenshot",
|
|
2428
|
+
resolved
|
|
2429
|
+
], "iOS simulator screenshot", run, { signal: options.signal });
|
|
2430
|
+
return resolved;
|
|
2431
|
+
},
|
|
2432
|
+
get state() {
|
|
2433
|
+
return state;
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
} catch (error) {
|
|
2437
|
+
transition("failed");
|
|
2438
|
+
await closeLogs();
|
|
2439
|
+
await repairAbsoluteIosDevSession(project.projectRoot);
|
|
2440
|
+
throw error;
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2443
|
+
// src/mobile/iosNativeWatcher.ts
|
|
2444
|
+
import { watch } from "fs";
|
|
2445
|
+
import { basename } from "path";
|
|
2446
|
+
var NATIVE_CHANGE_DEBOUNCE_MS = 500;
|
|
2447
|
+
var ROOT_NATIVE_INPUTS = new Set([
|
|
2448
|
+
"absolute.config.js",
|
|
2449
|
+
"absolute.config.mjs",
|
|
2450
|
+
"absolute.config.ts",
|
|
2451
|
+
"absolutejs.config.js",
|
|
2452
|
+
"absolutejs.config.mjs",
|
|
2453
|
+
"absolutejs.config.ts",
|
|
2454
|
+
"bun.lock",
|
|
2455
|
+
"bun.lockb",
|
|
2456
|
+
"capacitor.config.js",
|
|
2457
|
+
"capacitor.config.ts",
|
|
2458
|
+
"package.json"
|
|
2459
|
+
]);
|
|
2460
|
+
var createAbsoluteIosNativeWatcher = async (options) => {
|
|
2461
|
+
let fingerprint = await fingerprintAbsoluteIosDevProject(options.project);
|
|
2462
|
+
let closed = false;
|
|
2463
|
+
let running = false;
|
|
2464
|
+
let timer;
|
|
2465
|
+
let rootInputChanged = false;
|
|
2466
|
+
const changedPaths = new Set;
|
|
2467
|
+
const watchers = [];
|
|
2468
|
+
const debounceMs = options.debounceMs ?? NATIVE_CHANGE_DEBOUNCE_MS;
|
|
2469
|
+
const close = () => {
|
|
2470
|
+
if (closed)
|
|
2471
|
+
return;
|
|
2472
|
+
closed = true;
|
|
2473
|
+
if (timer)
|
|
2474
|
+
clearTimeout(timer);
|
|
2475
|
+
watchers.forEach((watcher) => watcher.close());
|
|
2476
|
+
options.signal?.removeEventListener("abort", close);
|
|
2477
|
+
};
|
|
2478
|
+
const schedule = () => {
|
|
2479
|
+
if (closed || running)
|
|
2480
|
+
return;
|
|
2481
|
+
if (timer)
|
|
2482
|
+
clearTimeout(timer);
|
|
2483
|
+
timer = setTimeout(() => void flush(), debounceMs);
|
|
2484
|
+
};
|
|
2485
|
+
const flush = async () => {
|
|
2486
|
+
timer = undefined;
|
|
2487
|
+
if (closed || running || changedPaths.size === 0)
|
|
2488
|
+
return;
|
|
2489
|
+
running = true;
|
|
2490
|
+
const paths = [...changedPaths].sort();
|
|
2491
|
+
const forced = rootInputChanged;
|
|
2492
|
+
changedPaths.clear();
|
|
2493
|
+
rootInputChanged = false;
|
|
2494
|
+
try {
|
|
2495
|
+
const next = await fingerprintAbsoluteIosDevProject(options.project);
|
|
2496
|
+
if (!forced && next === fingerprint)
|
|
2497
|
+
return;
|
|
2498
|
+
await options.onChange({
|
|
2499
|
+
afterFingerprint: next,
|
|
2500
|
+
beforeFingerprint: fingerprint,
|
|
2501
|
+
paths,
|
|
2502
|
+
rootInputChanged: forced
|
|
2503
|
+
});
|
|
2504
|
+
fingerprint = await fingerprintAbsoluteIosDevProject(options.project);
|
|
2505
|
+
} catch (error) {
|
|
2506
|
+
options.onError?.(error);
|
|
2507
|
+
} finally {
|
|
2508
|
+
running = false;
|
|
2509
|
+
if (changedPaths.size > 0)
|
|
2510
|
+
schedule();
|
|
2511
|
+
}
|
|
2512
|
+
};
|
|
2513
|
+
const record = (path, force) => {
|
|
2514
|
+
if (closed)
|
|
2515
|
+
return;
|
|
2516
|
+
changedPaths.add(path);
|
|
2517
|
+
rootInputChanged ||= force;
|
|
2518
|
+
schedule();
|
|
2519
|
+
};
|
|
2520
|
+
watchers.push(watch(options.project.nativeDirectory, { recursive: true }, (_event, filename) => {
|
|
2521
|
+
if (filename)
|
|
2522
|
+
record(String(filename), false);
|
|
2523
|
+
}));
|
|
2524
|
+
watchers.push(watch(options.project.projectRoot, (_event, filename) => {
|
|
2525
|
+
if (!filename)
|
|
2526
|
+
return;
|
|
2527
|
+
const path = String(filename);
|
|
2528
|
+
if (isAbsoluteIosNativeRootInput(path))
|
|
2529
|
+
record(path, true);
|
|
2530
|
+
}));
|
|
2531
|
+
watchers.forEach((watcher) => watcher.on("error", (error) => options.onError?.(error)));
|
|
2532
|
+
options.signal?.addEventListener("abort", close, { once: true });
|
|
2533
|
+
return { close };
|
|
2534
|
+
};
|
|
2535
|
+
var isAbsoluteIosNativeRootInput = (path) => ROOT_NATIVE_INPUTS.has(basename(path));
|
|
2536
|
+
// src/mobile/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";
|
|
1474
2546
|
import { Elysia } from "elysia";
|
|
1475
2547
|
|
|
1476
2548
|
// src/mobile/config.ts
|
|
1477
|
-
import { resolve as
|
|
2549
|
+
import { resolve as resolve6 } from "path";
|
|
1478
2550
|
var APP_ID_PATTERN = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
1479
2551
|
var SCHEME_PATTERN = /^[a-z][a-z0-9+.-]*$/;
|
|
1480
2552
|
var APPLE_APP_ID_PREFIX_PATTERN = /^[A-Z0-9]{10}$/;
|
|
1481
2553
|
var CERTIFICATE_FINGERPRINT_PATTERN = /^[0-9A-F]{64}$/;
|
|
1482
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])?))*$/;
|
|
1483
2555
|
var resolveProjectPath = (projectRoot, value, field) => {
|
|
1484
|
-
const root =
|
|
1485
|
-
const path =
|
|
2556
|
+
const root = resolve6(projectRoot);
|
|
2557
|
+
const path = resolve6(root, value);
|
|
1486
2558
|
if (path !== root && !path.startsWith(`${root}/`)) {
|
|
1487
2559
|
throw new TypeError(`${field} must remain inside the project root.`);
|
|
1488
2560
|
}
|
|
@@ -1670,7 +2742,7 @@ var createAbsoluteMobileAssociationPlugin = (mobile, projectRoot, options = {})
|
|
|
1670
2742
|
var writeAtomic = async (path, source) => {
|
|
1671
2743
|
let current;
|
|
1672
2744
|
try {
|
|
1673
|
-
current = await
|
|
2745
|
+
current = await readFile8(path, "utf8");
|
|
1674
2746
|
} catch (error) {
|
|
1675
2747
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
1676
2748
|
throw error;
|
|
@@ -1679,23 +2751,23 @@ var writeAtomic = async (path, source) => {
|
|
|
1679
2751
|
if (current === source)
|
|
1680
2752
|
return false;
|
|
1681
2753
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
1682
|
-
await
|
|
1683
|
-
await
|
|
2754
|
+
await writeFile7(temporary, source, { flag: "wx" });
|
|
2755
|
+
await rename7(temporary, path);
|
|
1684
2756
|
return true;
|
|
1685
2757
|
};
|
|
1686
2758
|
var exists2 = async (path) => {
|
|
1687
2759
|
try {
|
|
1688
|
-
await
|
|
2760
|
+
await access7(path);
|
|
1689
2761
|
return true;
|
|
1690
2762
|
} catch {
|
|
1691
2763
|
return false;
|
|
1692
2764
|
}
|
|
1693
2765
|
};
|
|
1694
2766
|
var assertOwnedOutput = async (root) => {
|
|
1695
|
-
const path =
|
|
2767
|
+
const path = resolve7(root, OWNERSHIP_FILE);
|
|
1696
2768
|
let ownership;
|
|
1697
2769
|
try {
|
|
1698
|
-
ownership = JSON.parse(await
|
|
2770
|
+
ownership = JSON.parse(await readFile8(path, "utf8"));
|
|
1699
2771
|
} catch {
|
|
1700
2772
|
throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
|
|
1701
2773
|
}
|
|
@@ -1709,22 +2781,22 @@ var publishGeneratedDirectory = async (temporary, root) => {
|
|
|
1709
2781
|
await assertOwnedOutput(root);
|
|
1710
2782
|
const backup = `${root}.${crypto.randomUUID()}.previous`;
|
|
1711
2783
|
if (hasCurrent)
|
|
1712
|
-
await
|
|
2784
|
+
await rename7(root, backup);
|
|
1713
2785
|
try {
|
|
1714
|
-
await
|
|
2786
|
+
await rename7(temporary, root);
|
|
1715
2787
|
} catch (error) {
|
|
1716
2788
|
if (hasCurrent)
|
|
1717
|
-
await
|
|
2789
|
+
await rename7(backup, root);
|
|
1718
2790
|
throw error;
|
|
1719
2791
|
}
|
|
1720
2792
|
if (hasCurrent)
|
|
1721
|
-
await
|
|
2793
|
+
await rm6(backup, { force: true, recursive: true });
|
|
1722
2794
|
};
|
|
1723
2795
|
var materializeHost = async (root, host, files) => {
|
|
1724
|
-
const directory =
|
|
1725
|
-
await
|
|
2796
|
+
const directory = resolve7(root, host, ".well-known");
|
|
2797
|
+
await mkdir6(directory, { recursive: true });
|
|
1726
2798
|
return Promise.all(files.map(async ([name, document]) => {
|
|
1727
|
-
const path =
|
|
2799
|
+
const path = resolve7(directory, name);
|
|
1728
2800
|
await writeAtomic(path, `${JSON.stringify(document, null, 2)}
|
|
1729
2801
|
`);
|
|
1730
2802
|
return path;
|
|
@@ -1749,7 +2821,7 @@ var associationEndpoints = (config, documents) => config.deepLinkHosts.flatMap((
|
|
|
1749
2821
|
return endpoints;
|
|
1750
2822
|
});
|
|
1751
2823
|
var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory) => {
|
|
1752
|
-
const root =
|
|
2824
|
+
const root = resolve7(outputDirectory);
|
|
1753
2825
|
const temporary = `${root}.${crypto.randomUUID()}.tmp`;
|
|
1754
2826
|
const documents = createAbsoluteMobileAssociationDocuments(config, {
|
|
1755
2827
|
requireAll: true
|
|
@@ -1760,16 +2832,16 @@ var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory)
|
|
|
1760
2832
|
if (documents.apple) {
|
|
1761
2833
|
files.push(["apple-app-site-association", documents.apple]);
|
|
1762
2834
|
}
|
|
1763
|
-
await
|
|
2835
|
+
await mkdir6(temporary, { recursive: true });
|
|
1764
2836
|
try {
|
|
1765
2837
|
const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host) => materializeHost(temporary, host, files)))).flat();
|
|
1766
|
-
await writeAtomic(
|
|
2838
|
+
await writeAtomic(resolve7(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
|
|
1767
2839
|
`);
|
|
1768
2840
|
await publishGeneratedDirectory(temporary, root);
|
|
1769
|
-
const written = temporaryPaths.map((path) =>
|
|
2841
|
+
const written = temporaryPaths.map((path) => resolve7(root, path.slice(temporary.length + 1)));
|
|
1770
2842
|
return { root, written };
|
|
1771
2843
|
} catch (error) {
|
|
1772
|
-
await
|
|
2844
|
+
await rm6(temporary, { force: true, recursive: true });
|
|
1773
2845
|
throw error;
|
|
1774
2846
|
}
|
|
1775
2847
|
};
|
|
@@ -1814,10 +2886,10 @@ var frameworks2 = new Set([
|
|
|
1814
2886
|
"svelte",
|
|
1815
2887
|
"vue"
|
|
1816
2888
|
]);
|
|
1817
|
-
var
|
|
2889
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1818
2890
|
var isPageFramework2 = (value) => typeof value === "string" && frameworks2.has(value);
|
|
1819
2891
|
var parseAbsoluteMobileBuildPageMetadata = (value) => {
|
|
1820
|
-
if (!
|
|
2892
|
+
if (!isRecord4(value))
|
|
1821
2893
|
return;
|
|
1822
2894
|
if (typeof value.bundleKey !== "string" || typeof value.contract !== "string" || !isPageFramework2(value.framework) || typeof value.pageId !== "string" || typeof value.propsSchemaHash !== "string") {
|
|
1823
2895
|
return;
|
|
@@ -1831,23 +2903,23 @@ var parseAbsoluteMobileBuildPageMetadata = (value) => {
|
|
|
1831
2903
|
};
|
|
1832
2904
|
};
|
|
1833
2905
|
// src/mobile/buildPipeline.ts
|
|
1834
|
-
import { readFile as
|
|
1835
|
-
import { join as
|
|
2906
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
2907
|
+
import { join as join10, resolve as resolve10 } from "path";
|
|
1836
2908
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1837
2909
|
|
|
1838
2910
|
// src/mobile/buildRelease.ts
|
|
1839
|
-
import { createHash as
|
|
1840
|
-
import { readFile as
|
|
1841
|
-
import { join as
|
|
1842
|
-
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");
|
|
1843
2915
|
var readPageMetadata = (route) => parseAbsoluteMobileBuildPageMetadata(route.hooks?.detail?.[ABSOLUTE_MOBILE_ROUTE_DETAIL]);
|
|
1844
2916
|
var resolveAssetPath = (buildDirectory, assetPath) => {
|
|
1845
|
-
const resolvedBuildDirectory =
|
|
1846
|
-
const resolvedAsset =
|
|
2917
|
+
const resolvedBuildDirectory = resolve8(buildDirectory);
|
|
2918
|
+
const resolvedAsset = resolve8(assetPath);
|
|
1847
2919
|
if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
|
|
1848
2920
|
return resolvedAsset;
|
|
1849
2921
|
}
|
|
1850
|
-
return
|
|
2922
|
+
return join7(buildDirectory, assetPath.replace(/^\/+/, ""));
|
|
1851
2923
|
};
|
|
1852
2924
|
var pageFor = async (metadata, manifest, buildDirectory) => {
|
|
1853
2925
|
const assetPath = manifest[metadata.bundleKey];
|
|
@@ -1855,8 +2927,8 @@ var pageFor = async (metadata, manifest, buildDirectory) => {
|
|
|
1855
2927
|
throw new TypeError(`Mobile page ${metadata.pageId} references missing manifest asset ${metadata.bundleKey}.`);
|
|
1856
2928
|
}
|
|
1857
2929
|
const resolvedAssetPath = resolveAssetPath(buildDirectory, assetPath);
|
|
1858
|
-
const bytes = await
|
|
1859
|
-
const bundlePath = `/${
|
|
2930
|
+
const bytes = await readFile9(resolvedAssetPath);
|
|
2931
|
+
const bundlePath = `/${relative6(resolve8(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
|
|
1860
2932
|
return {
|
|
1861
2933
|
bundleHash: sha256(bytes),
|
|
1862
2934
|
bundlePath,
|
|
@@ -1869,7 +2941,7 @@ var pageFor = async (metadata, manifest, buildDirectory) => {
|
|
|
1869
2941
|
var buildAbsoluteMobileCompatibilityRelease = async (options) => {
|
|
1870
2942
|
const [captured, producerBytes] = await Promise.all([
|
|
1871
2943
|
captureAbsoluteMobileRouteGraph(options.app),
|
|
1872
|
-
|
|
2944
|
+
readFile9(options.producerPath)
|
|
1873
2945
|
]);
|
|
1874
2946
|
if (captured.length === 0) {
|
|
1875
2947
|
throw new TypeError("No instrumented AbsoluteJS mobile page routes were found in the finalized Elysia route graph.");
|
|
@@ -1949,16 +3021,16 @@ var captureAbsoluteMobileRouteGraph = async (app) => {
|
|
|
1949
3021
|
|
|
1950
3022
|
// src/mobile/capacitorBundle.ts
|
|
1951
3023
|
import {
|
|
1952
|
-
copyFile as
|
|
1953
|
-
mkdir as
|
|
3024
|
+
copyFile as copyFile5,
|
|
3025
|
+
mkdir as mkdir7,
|
|
1954
3026
|
mkdtemp as mkdtemp4,
|
|
1955
|
-
readFile as
|
|
1956
|
-
rename as
|
|
1957
|
-
rm as
|
|
1958
|
-
writeFile as
|
|
3027
|
+
readFile as readFile10,
|
|
3028
|
+
rename as rename8,
|
|
3029
|
+
rm as rm7,
|
|
3030
|
+
writeFile as writeFile8
|
|
1959
3031
|
} from "fs/promises";
|
|
1960
3032
|
import { existsSync as existsSync2 } from "fs";
|
|
1961
|
-
import { basename, dirname as
|
|
3033
|
+
import { basename as basename2, dirname as dirname5, extname, join as join8, resolve as resolve9 } from "path";
|
|
1962
3034
|
|
|
1963
3035
|
// src/mobile/routeMatcher.ts
|
|
1964
3036
|
var REGEXP_SPECIAL_CHARACTERS = /[.*+?^${}()|[\]\\]/g;
|
|
@@ -2114,14 +3186,14 @@ var envelopeResponse = (response, status) => new Response(JSON.stringify({
|
|
|
2114
3186
|
protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
|
|
2115
3187
|
response
|
|
2116
3188
|
}), { headers: responseHeaders(), status });
|
|
2117
|
-
var
|
|
3189
|
+
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2118
3190
|
var normalizeJsonValue = (value) => {
|
|
2119
3191
|
const serialized = JSON.stringify(value);
|
|
2120
3192
|
if (serialized === undefined) {
|
|
2121
3193
|
throw new TypeError("Mobile page props must be JSON-serializable.");
|
|
2122
3194
|
}
|
|
2123
3195
|
const parsed = JSON.parse(serialized);
|
|
2124
|
-
if (!
|
|
3196
|
+
if (!isRecord5(parsed)) {
|
|
2125
3197
|
throw new TypeError("Mobile page props must serialize to an object.");
|
|
2126
3198
|
}
|
|
2127
3199
|
return parsed;
|
|
@@ -2246,14 +3318,14 @@ var upgradeReasons = new Set([
|
|
|
2246
3318
|
"protocol",
|
|
2247
3319
|
"runtime"
|
|
2248
3320
|
]);
|
|
2249
|
-
var
|
|
3321
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2250
3322
|
var isFramework = (value) => typeof value === "string" && frameworks4.has(value);
|
|
2251
3323
|
var isUpgradeReason = (value) => typeof value === "string" && upgradeReasons.has(value);
|
|
2252
3324
|
var parsePageResult = (value) => {
|
|
2253
3325
|
if (typeof value.contract !== "string" || !isFramework(value.framework) || typeof value.pageId !== "string" || typeof value.status !== "number") {
|
|
2254
3326
|
throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The mobile page response is missing required page metadata.");
|
|
2255
3327
|
}
|
|
2256
|
-
if (!
|
|
3328
|
+
if (!isRecord6(value.props)) {
|
|
2257
3329
|
throw new AbsoluteMobilePageProtocolError("invalid-props", "The mobile page response must contain an object props value.");
|
|
2258
3330
|
}
|
|
2259
3331
|
return {
|
|
@@ -2299,7 +3371,7 @@ var activateAbsoluteMobilePage = async (value, options) => {
|
|
|
2299
3371
|
};
|
|
2300
3372
|
};
|
|
2301
3373
|
var parseAbsoluteMobilePageEnvelope = (value) => {
|
|
2302
|
-
if (!
|
|
3374
|
+
if (!isRecord6(value) || !isRecord6(value.response)) {
|
|
2303
3375
|
throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The server did not return an AbsoluteJS mobile page envelope.");
|
|
2304
3376
|
}
|
|
2305
3377
|
if (value.protocol !== ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION) {
|
|
@@ -2392,7 +3464,7 @@ var INDEX_FILE = "index.html";
|
|
|
2392
3464
|
var CLIENT_IMPORT_PATTERN = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s*)["'](\/[^"']+)["']/gu;
|
|
2393
3465
|
var errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
|
|
2394
3466
|
var shellBootstrapModule = () => {
|
|
2395
|
-
const candidate = ["js", "ts"].map((extension) =>
|
|
3467
|
+
const candidate = ["js", "ts"].map((extension) => join8(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync2);
|
|
2396
3468
|
if (candidate)
|
|
2397
3469
|
return candidate;
|
|
2398
3470
|
throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
|
|
@@ -2413,8 +3485,8 @@ var indexHtml = (appName) => `<!doctype html>
|
|
|
2413
3485
|
</html>
|
|
2414
3486
|
`;
|
|
2415
3487
|
var sourceAssetPath = (buildDirectory, bundlePath) => {
|
|
2416
|
-
const root =
|
|
2417
|
-
const asset =
|
|
3488
|
+
const root = resolve9(buildDirectory);
|
|
3489
|
+
const asset = resolve9(root, bundlePath.replace(/^\/+/, ""));
|
|
2418
3490
|
if (!asset.startsWith(`${root}/`)) {
|
|
2419
3491
|
throw new TypeError("Mobile page bundle escaped the build directory.");
|
|
2420
3492
|
}
|
|
@@ -2422,8 +3494,8 @@ var sourceAssetPath = (buildDirectory, bundlePath) => {
|
|
|
2422
3494
|
};
|
|
2423
3495
|
var buildShellBootstrap = async (staging) => {
|
|
2424
3496
|
const modulePath = shellBootstrapModule();
|
|
2425
|
-
const entryPath =
|
|
2426
|
-
await
|
|
3497
|
+
const entryPath = join8(staging, ".absolute-mobile-entry.ts");
|
|
3498
|
+
await writeFile8(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
|
|
2427
3499
|
void startAbsoluteMobileShell();
|
|
2428
3500
|
`);
|
|
2429
3501
|
const build = await Bun.build({
|
|
@@ -2435,31 +3507,31 @@ void startAbsoluteMobileShell();
|
|
|
2435
3507
|
if (!build.success || build.outputs.length !== 1) {
|
|
2436
3508
|
throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
|
|
2437
3509
|
}
|
|
2438
|
-
await
|
|
2439
|
-
await
|
|
3510
|
+
await rename8(build.outputs[0]?.path ?? "", join8(staging, BOOTSTRAP_FILE));
|
|
3511
|
+
await rm7(entryPath, { force: true });
|
|
2440
3512
|
};
|
|
2441
3513
|
var removePreviousBundle = async (backup, moved) => {
|
|
2442
3514
|
if (!moved)
|
|
2443
3515
|
return;
|
|
2444
|
-
await
|
|
3516
|
+
await rm7(backup, { force: true, recursive: true });
|
|
2445
3517
|
};
|
|
2446
3518
|
var restorePreviousBundle = async (backup, destination, moved) => {
|
|
2447
3519
|
if (!moved)
|
|
2448
3520
|
return;
|
|
2449
|
-
await
|
|
3521
|
+
await rename8(backup, destination);
|
|
2450
3522
|
};
|
|
2451
3523
|
var installBundle = async (staging, destination) => {
|
|
2452
3524
|
const backup = `${destination}.previous-${crypto.randomUUID()}`;
|
|
2453
3525
|
let movedPrevious = false;
|
|
2454
3526
|
try {
|
|
2455
|
-
await
|
|
3527
|
+
await rename8(destination, backup);
|
|
2456
3528
|
movedPrevious = true;
|
|
2457
3529
|
} catch (error) {
|
|
2458
3530
|
if (!errorHasCode2(error, "ENOENT"))
|
|
2459
3531
|
throw error;
|
|
2460
3532
|
}
|
|
2461
3533
|
try {
|
|
2462
|
-
await
|
|
3534
|
+
await rename8(staging, destination);
|
|
2463
3535
|
await removePreviousBundle(backup, movedPrevious);
|
|
2464
3536
|
} catch (error) {
|
|
2465
3537
|
await restorePreviousBundle(backup, destination, movedPrevious);
|
|
@@ -2473,12 +3545,12 @@ var copyClientPage = async (page, buildDirectory, staging, copiedDependencies) =
|
|
|
2473
3545
|
const extension = extname(page.bundlePath) || ".js";
|
|
2474
3546
|
const localBundlePath = `./pages/${page.bundleHash}${extension}`;
|
|
2475
3547
|
const source = sourceAssetPath(buildDirectory, page.bundlePath);
|
|
2476
|
-
await
|
|
3548
|
+
await copyFile5(source, join8(staging, localBundlePath));
|
|
2477
3549
|
await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
|
|
2478
3550
|
return { ...page, localBundlePath };
|
|
2479
3551
|
};
|
|
2480
3552
|
var absoluteClientImports = async (sourcePath) => {
|
|
2481
|
-
const source = await
|
|
3553
|
+
const source = await readFile10(sourcePath, "utf8");
|
|
2482
3554
|
return [...source.matchAll(CLIENT_IMPORT_PATTERN)].flatMap((match) => {
|
|
2483
3555
|
const [specifier] = match.slice(1);
|
|
2484
3556
|
return specifier ? [specifier.split(/[?#]/u, 1)[0] ?? specifier] : [];
|
|
@@ -2489,9 +3561,9 @@ var copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, co
|
|
|
2489
3561
|
return;
|
|
2490
3562
|
copied.add(specifier);
|
|
2491
3563
|
const source = sourceAssetPath(buildDirectory, specifier);
|
|
2492
|
-
const destination =
|
|
2493
|
-
await
|
|
2494
|
-
await
|
|
3564
|
+
const destination = join8(staging, specifier.replace(/^\/+/, ""));
|
|
3565
|
+
await mkdir7(dirname5(destination), { recursive: true });
|
|
3566
|
+
await copyFile5(source, destination);
|
|
2495
3567
|
await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
|
|
2496
3568
|
};
|
|
2497
3569
|
var copyAbsoluteClientDependencies = async (sourcePath, buildDirectory, staging, copied) => {
|
|
@@ -2503,11 +3575,11 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
|
2503
3575
|
throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
|
|
2504
3576
|
}
|
|
2505
3577
|
const destination = options.config.bundleDirectory;
|
|
2506
|
-
await
|
|
2507
|
-
const staging = await mkdtemp4(
|
|
3578
|
+
await mkdir7(dirname5(destination), { recursive: true });
|
|
3579
|
+
const staging = await mkdtemp4(join8(dirname5(destination), `.${basename2(destination)}.stage-`));
|
|
2508
3580
|
try {
|
|
2509
|
-
const pageDirectory =
|
|
2510
|
-
await
|
|
3581
|
+
const pageDirectory = join8(staging, "pages");
|
|
3582
|
+
await mkdir7(pageDirectory, { recursive: true });
|
|
2511
3583
|
const copiedDependencies = new Set;
|
|
2512
3584
|
const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
|
|
2513
3585
|
const manifest = {
|
|
@@ -2524,48 +3596,48 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
|
2524
3596
|
runtime: options.artifact.runtime
|
|
2525
3597
|
};
|
|
2526
3598
|
await Promise.all([
|
|
2527
|
-
|
|
3599
|
+
writeFile8(join8(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
2528
3600
|
`),
|
|
2529
|
-
|
|
3601
|
+
writeFile8(join8(staging, INDEX_FILE), indexHtml(options.config.appName)),
|
|
2530
3602
|
buildShellBootstrap(staging)
|
|
2531
3603
|
]);
|
|
2532
3604
|
await installBundle(staging, destination);
|
|
2533
3605
|
return manifest;
|
|
2534
3606
|
} catch (error) {
|
|
2535
|
-
await
|
|
3607
|
+
await rm7(staging, { force: true, recursive: true });
|
|
2536
3608
|
throw error;
|
|
2537
3609
|
}
|
|
2538
3610
|
};
|
|
2539
3611
|
|
|
2540
3612
|
// src/mobile/materializedBundle.ts
|
|
2541
|
-
import { createHash as
|
|
3613
|
+
import { createHash as createHash8 } from "crypto";
|
|
2542
3614
|
import {
|
|
2543
|
-
access as
|
|
2544
|
-
mkdir as
|
|
3615
|
+
access as access8,
|
|
3616
|
+
mkdir as mkdir8,
|
|
2545
3617
|
mkdtemp as mkdtemp5,
|
|
2546
|
-
readFile as
|
|
2547
|
-
rename as
|
|
2548
|
-
rm as
|
|
2549
|
-
writeFile as
|
|
3618
|
+
readFile as readFile11,
|
|
3619
|
+
rename as rename9,
|
|
3620
|
+
rm as rm8,
|
|
3621
|
+
writeFile as writeFile9
|
|
2550
3622
|
} from "fs/promises";
|
|
2551
|
-
import { dirname as
|
|
3623
|
+
import { dirname as dirname6, join as join9, resolve as resolvePath2 } from "path";
|
|
2552
3624
|
import { pathToFileURL } from "url";
|
|
2553
3625
|
var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1;
|
|
2554
3626
|
var CURRENT_BUNDLE_FILE = "current.json";
|
|
2555
3627
|
var BUNDLES_DIRECTORY = "bundles";
|
|
2556
3628
|
var ARTIFACT_FILE2 = "artifact.json";
|
|
2557
3629
|
var BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
|
|
2558
|
-
var
|
|
3630
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2559
3631
|
var errorHasCode3 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
|
|
2560
3632
|
var bundleIdFor = (currentReleaseId, releases) => {
|
|
2561
3633
|
const identity = JSON.stringify({
|
|
2562
3634
|
currentReleaseId,
|
|
2563
3635
|
releases: releases.map(({ releaseId }) => releaseId)
|
|
2564
3636
|
});
|
|
2565
|
-
return `amb_${
|
|
3637
|
+
return `amb_${createHash8("sha256").update(identity).digest("hex")}`;
|
|
2566
3638
|
};
|
|
2567
3639
|
var parseBundleIndex = (value) => {
|
|
2568
|
-
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)) {
|
|
2569
3641
|
throw new TypeError("Invalid materialized mobile compatibility bundle.");
|
|
2570
3642
|
}
|
|
2571
3643
|
const releases = value.releases.map(parseAbsoluteMobileCompatibilityArtifact);
|
|
@@ -2588,30 +3660,30 @@ var parseBundleIndex = (value) => {
|
|
|
2588
3660
|
};
|
|
2589
3661
|
};
|
|
2590
3662
|
var writeRelease = async (root, release) => {
|
|
2591
|
-
const directory =
|
|
2592
|
-
const producerPath =
|
|
2593
|
-
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 });
|
|
2594
3666
|
await Promise.all([
|
|
2595
|
-
|
|
3667
|
+
writeFile9(join9(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
|
|
2596
3668
|
`),
|
|
2597
|
-
|
|
3669
|
+
writeFile9(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
|
|
2598
3670
|
]);
|
|
2599
3671
|
};
|
|
2600
3672
|
var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
|
|
2601
|
-
const destination =
|
|
3673
|
+
const destination = join9(bundlesRoot, bundleId);
|
|
2602
3674
|
try {
|
|
2603
|
-
await
|
|
3675
|
+
await access8(destination);
|
|
2604
3676
|
return destination;
|
|
2605
3677
|
} catch (error) {
|
|
2606
3678
|
if (!errorHasCode3(error, "ENOENT"))
|
|
2607
3679
|
throw error;
|
|
2608
3680
|
}
|
|
2609
|
-
const staging = await mkdtemp5(
|
|
3681
|
+
const staging = await mkdtemp5(join9(bundlesRoot, ".stage-"));
|
|
2610
3682
|
try {
|
|
2611
3683
|
await Promise.all(releases.map((release) => writeRelease(staging, release)));
|
|
2612
|
-
await
|
|
3684
|
+
await rename9(staging, destination);
|
|
2613
3685
|
} catch (error) {
|
|
2614
|
-
await
|
|
3686
|
+
await rm8(staging, { force: true, recursive: true });
|
|
2615
3687
|
if (errorHasCode3(error, "EEXIST") || errorHasCode3(error, "ENOTEMPTY")) {
|
|
2616
3688
|
return destination;
|
|
2617
3689
|
}
|
|
@@ -2622,7 +3694,7 @@ var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
|
|
|
2622
3694
|
var readCompatibilityModule = (modulePath) => import(pathToFileURL(modulePath).href);
|
|
2623
3695
|
var resolveProducerHandler = (loaded, exportName) => {
|
|
2624
3696
|
const value = loaded[exportName];
|
|
2625
|
-
if (!
|
|
3697
|
+
if (!isRecord7(value) || typeof value.handle !== "function") {
|
|
2626
3698
|
throw new TypeError(`Compatibility producer export ${exportName} must expose handle(request).`);
|
|
2627
3699
|
}
|
|
2628
3700
|
const { handle } = value;
|
|
@@ -2640,15 +3712,15 @@ var resolveProducerHandler = (loaded, exportName) => {
|
|
|
2640
3712
|
};
|
|
2641
3713
|
var loadAbsoluteMobileMaterializedBundle = async (root) => {
|
|
2642
3714
|
const resolvedRoot = resolvePath2(root);
|
|
2643
|
-
const serialized = await
|
|
3715
|
+
const serialized = await readFile11(join9(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
|
|
2644
3716
|
const parsed = JSON.parse(serialized);
|
|
2645
3717
|
const index = parseBundleIndex(parsed);
|
|
2646
|
-
const bundleRoot =
|
|
3718
|
+
const bundleRoot = join9(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
2647
3719
|
return {
|
|
2648
3720
|
artifacts: index.releases,
|
|
2649
3721
|
currentReleaseId: index.currentReleaseId,
|
|
2650
3722
|
loadProducer: async (artifact) => {
|
|
2651
|
-
const modulePath =
|
|
3723
|
+
const modulePath = join9(bundleRoot, artifact.releaseId, artifact.producer.module);
|
|
2652
3724
|
await verifyAbsoluteMobileCompatibilityProducer({
|
|
2653
3725
|
artifact,
|
|
2654
3726
|
producer: Bun.file(modulePath)
|
|
@@ -2675,8 +3747,8 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
|
|
|
2675
3747
|
return release;
|
|
2676
3748
|
});
|
|
2677
3749
|
const root = resolvePath2(input.root);
|
|
2678
|
-
const bundlesRoot =
|
|
2679
|
-
await
|
|
3750
|
+
const bundlesRoot = join9(root, BUNDLES_DIRECTORY);
|
|
3751
|
+
await mkdir8(bundlesRoot, { recursive: true });
|
|
2680
3752
|
const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
|
|
2681
3753
|
await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
|
|
2682
3754
|
const index = {
|
|
@@ -2685,22 +3757,22 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
|
|
|
2685
3757
|
format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
|
|
2686
3758
|
releases: artifacts
|
|
2687
3759
|
};
|
|
2688
|
-
const pointerPath =
|
|
2689
|
-
const temporaryPointerPath =
|
|
2690
|
-
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")}
|
|
2691
3763
|
`, { flag: "wx" });
|
|
2692
|
-
await
|
|
3764
|
+
await rename9(temporaryPointerPath, pointerPath);
|
|
2693
3765
|
return index;
|
|
2694
3766
|
};
|
|
2695
3767
|
var readAbsoluteMobileMaterializedReleases = async (root) => {
|
|
2696
3768
|
const resolvedRoot = resolvePath2(root);
|
|
2697
3769
|
try {
|
|
2698
|
-
const serialized = await
|
|
3770
|
+
const serialized = await readFile11(join9(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
|
|
2699
3771
|
const parsed = JSON.parse(serialized);
|
|
2700
3772
|
const index = parseBundleIndex(parsed);
|
|
2701
|
-
const bundleRoot =
|
|
3773
|
+
const bundleRoot = join9(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
2702
3774
|
return Promise.all(index.releases.map(async (artifact) => {
|
|
2703
|
-
const producer = Bun.file(
|
|
3775
|
+
const producer = Bun.file(join9(bundleRoot, artifact.releaseId, artifact.producer.module));
|
|
2704
3776
|
await verifyAbsoluteMobileCompatibilityProducer({
|
|
2705
3777
|
artifact,
|
|
2706
3778
|
producer
|
|
@@ -2749,11 +3821,11 @@ var loadServerApp = async (producerPath) => {
|
|
|
2749
3821
|
return { app, exportName };
|
|
2750
3822
|
};
|
|
2751
3823
|
var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
2752
|
-
const buildDirectory =
|
|
3824
|
+
const buildDirectory = resolve10(options.buildDirectory);
|
|
2753
3825
|
const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
|
|
2754
|
-
const root =
|
|
3826
|
+
const root = join10(buildDirectory, ".absolutejs", "mobile-compatibility");
|
|
2755
3827
|
const [manifestSource, previous] = await Promise.all([
|
|
2756
|
-
|
|
3828
|
+
readFile12(join10(buildDirectory, "manifest.json"), "utf8"),
|
|
2757
3829
|
readAbsoluteMobileMaterializedReleases(root)
|
|
2758
3830
|
]);
|
|
2759
3831
|
const manifest = JSON.parse(manifestSource);
|
|
@@ -2764,7 +3836,7 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
2764
3836
|
process.env.ABSOLUTE_BUILD_DIR = buildDirectory;
|
|
2765
3837
|
let loaded;
|
|
2766
3838
|
try {
|
|
2767
|
-
loaded = await loadServerApp(
|
|
3839
|
+
loaded = await loadServerApp(resolve10(options.producerPath));
|
|
2768
3840
|
} finally {
|
|
2769
3841
|
restoreBuildDirectory(previousBuildDirectory);
|
|
2770
3842
|
}
|
|
@@ -2775,7 +3847,7 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
2775
3847
|
manifest,
|
|
2776
3848
|
previousArtifacts: previous.map(({ artifact }) => artifact),
|
|
2777
3849
|
producerExport: loaded.exportName,
|
|
2778
|
-
producerPath:
|
|
3850
|
+
producerPath: resolve10(options.producerPath),
|
|
2779
3851
|
runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
|
|
2780
3852
|
});
|
|
2781
3853
|
const releasesById = new Map([current, ...previous].map((release) => [
|
|
@@ -2869,20 +3941,20 @@ var createAbsoluteMobileCompatibilityDispatcher = (options) => {
|
|
|
2869
3941
|
}).as("global");
|
|
2870
3942
|
};
|
|
2871
3943
|
// src/mobile/nativeDeepLinks.ts
|
|
2872
|
-
import { readFile as
|
|
2873
|
-
import { join as
|
|
3944
|
+
import { readFile as readFile13, rename as rename10, writeFile as writeFile10 } from "fs/promises";
|
|
3945
|
+
import { join as join11 } from "path";
|
|
2874
3946
|
var START_MARKER = "<!-- absolutejs:deep-links:start -->";
|
|
2875
3947
|
var END_MARKER = "<!-- absolutejs:deep-links:end -->";
|
|
2876
3948
|
var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
|
|
2877
3949
|
var NOT_FOUND = -1;
|
|
2878
3950
|
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
2879
3951
|
var writeChangedFile = async (path, source) => {
|
|
2880
|
-
const current = await
|
|
3952
|
+
const current = await readFile13(path, "utf8");
|
|
2881
3953
|
if (current === source)
|
|
2882
3954
|
return false;
|
|
2883
3955
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
2884
|
-
await
|
|
2885
|
-
await
|
|
3956
|
+
await writeFile10(temporary, source, { flag: "wx" });
|
|
3957
|
+
await rename10(temporary, path);
|
|
2886
3958
|
return true;
|
|
2887
3959
|
};
|
|
2888
3960
|
var replaceManagedRegion = (source, region, insertAt) => {
|
|
@@ -2927,8 +3999,8 @@ ${hosts}
|
|
|
2927
3999
|
`;
|
|
2928
4000
|
};
|
|
2929
4001
|
var configureAndroid = async (config) => {
|
|
2930
|
-
const path =
|
|
2931
|
-
const source = await
|
|
4002
|
+
const path = join11(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
4003
|
+
const source = await readFile13(path, "utf8");
|
|
2932
4004
|
const mainActivity = source.indexOf('android:name=".MainActivity"');
|
|
2933
4005
|
if (mainActivity === NOT_FOUND) {
|
|
2934
4006
|
throw new TypeError("Android MainActivity was not found.");
|
|
@@ -2953,8 +4025,8 @@ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
|
|
|
2953
4025
|
${END_MARKER}
|
|
2954
4026
|
`;
|
|
2955
4027
|
var configureIosInfo = async (config) => {
|
|
2956
|
-
const path =
|
|
2957
|
-
const source = await
|
|
4028
|
+
const path = join11(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
4029
|
+
const source = await readFile13(path, "utf8");
|
|
2958
4030
|
const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
|
|
2959
4031
|
${END_MARKER}
|
|
2960
4032
|
`;
|
|
@@ -2977,10 +4049,10 @@ ${domains}
|
|
|
2977
4049
|
`;
|
|
2978
4050
|
};
|
|
2979
4051
|
var configureIosEntitlements = async (config) => {
|
|
2980
|
-
const path =
|
|
4052
|
+
const path = join11(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
2981
4053
|
let current = "";
|
|
2982
4054
|
try {
|
|
2983
|
-
current = await
|
|
4055
|
+
current = await readFile13(path, "utf8");
|
|
2984
4056
|
} catch (error) {
|
|
2985
4057
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
2986
4058
|
throw error;
|
|
@@ -2990,13 +4062,13 @@ var configureIosEntitlements = async (config) => {
|
|
|
2990
4062
|
if (current === source)
|
|
2991
4063
|
return false;
|
|
2992
4064
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
2993
|
-
await
|
|
2994
|
-
await
|
|
4065
|
+
await writeFile10(temporary, source, { flag: "wx" });
|
|
4066
|
+
await rename10(temporary, path);
|
|
2995
4067
|
return true;
|
|
2996
4068
|
};
|
|
2997
4069
|
var configureIosProject = async (config) => {
|
|
2998
|
-
const path =
|
|
2999
|
-
const source = await
|
|
4070
|
+
const path = join11(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
4071
|
+
const source = await readFile13(path, "utf8");
|
|
3000
4072
|
const declarations = [
|
|
3001
4073
|
...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
|
|
3002
4074
|
].map((match) => match[1]);
|
|
@@ -3034,8 +4106,8 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
|
|
|
3034
4106
|
};
|
|
3035
4107
|
};
|
|
3036
4108
|
// src/mobile/releasePublisher.ts
|
|
3037
|
-
import { access as
|
|
3038
|
-
import { isAbsolute as
|
|
4109
|
+
import { access as access9 } from "fs/promises";
|
|
4110
|
+
import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve11, sep as sep5 } from "path";
|
|
3039
4111
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
3040
4112
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
3041
4113
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -3058,24 +4130,24 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
|
3058
4130
|
}
|
|
3059
4131
|
return versionCode;
|
|
3060
4132
|
};
|
|
3061
|
-
var
|
|
3062
|
-
var isPublisher = (value) =>
|
|
4133
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4134
|
+
var isPublisher = (value) => isRecord8(value) && typeof value.publish === "function";
|
|
3063
4135
|
var publisherModulePath = (projectRoot, requested) => {
|
|
3064
|
-
const root =
|
|
3065
|
-
const path =
|
|
3066
|
-
const projectRelative =
|
|
3067
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
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)) {
|
|
3068
4140
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
3069
4141
|
}
|
|
3070
4142
|
return path;
|
|
3071
4143
|
};
|
|
3072
4144
|
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
3073
4145
|
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
3074
|
-
await
|
|
4146
|
+
await access9(modulePath).catch(() => {
|
|
3075
4147
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
3076
4148
|
});
|
|
3077
4149
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
3078
|
-
const publisher =
|
|
4150
|
+
const publisher = isRecord8(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
3079
4151
|
if (!isPublisher(publisher)) {
|
|
3080
4152
|
throw new TypeError("Native release registry module must default-export a registry with publish(options).");
|
|
3081
4153
|
}
|
|
@@ -3133,13 +4205,13 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
3133
4205
|
};
|
|
3134
4206
|
// src/mobile/routeMetadataTransform.ts
|
|
3135
4207
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
3136
|
-
import { dirname as
|
|
4208
|
+
import { dirname as dirname7, extname as extname2, relative as relative8, resolve as resolve12 } from "path";
|
|
3137
4209
|
import ts from "typescript";
|
|
3138
4210
|
var ROUTE_METHODS = new Set(["get", "head"]);
|
|
3139
4211
|
var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
|
|
3140
4212
|
var PAGE_HANDLER = "handleReactPageRequest";
|
|
3141
4213
|
var posixPath = (value) => value.replace(/\\/g, "/");
|
|
3142
|
-
var findTsconfig = (entry, projectRoot) => ts.findConfigFile(
|
|
4214
|
+
var findTsconfig = (entry, projectRoot) => ts.findConfigFile(dirname7(entry), existsSync3, "tsconfig.json") ?? ts.findConfigFile(projectRoot, existsSync3, "tsconfig.json");
|
|
3143
4215
|
var createProgram = (entry, projectRoot) => {
|
|
3144
4216
|
const configPath = findTsconfig(entry, projectRoot);
|
|
3145
4217
|
if (!configPath) {
|
|
@@ -3151,7 +4223,7 @@ var createProgram = (entry, projectRoot) => {
|
|
|
3151
4223
|
target: ts.ScriptTarget.ESNext
|
|
3152
4224
|
});
|
|
3153
4225
|
}
|
|
3154
|
-
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));
|
|
3155
4227
|
if (!parsed.fileNames.includes(entry))
|
|
3156
4228
|
parsed.fileNames.push(entry);
|
|
3157
4229
|
return ts.createProgram(parsed.fileNames, parsed.options);
|
|
@@ -3257,7 +4329,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
|
|
|
3257
4329
|
const declaration = symbol?.declarations?.[0];
|
|
3258
4330
|
const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
|
|
3259
4331
|
const exportedName = symbol?.name ?? expression.getText(sourceFile);
|
|
3260
|
-
const source = posixPath(
|
|
4332
|
+
const source = posixPath(relative8(projectRoot, file));
|
|
3261
4333
|
return `${source}#${exportedName}`;
|
|
3262
4334
|
};
|
|
3263
4335
|
var resolveAlias = (symbol, checker) => {
|
|
@@ -3364,7 +4436,7 @@ var analyzeProgram = (program, projectRoot) => {
|
|
|
3364
4436
|
const checker = program.getTypeChecker();
|
|
3365
4437
|
const analyzed = new Map;
|
|
3366
4438
|
for (const sourceFile of program.getSourceFiles()) {
|
|
3367
|
-
const resolvedFile =
|
|
4439
|
+
const resolvedFile = resolve12(sourceFile.fileName);
|
|
3368
4440
|
if (!isProjectSource(sourceFile, resolvedFile, projectRoot))
|
|
3369
4441
|
continue;
|
|
3370
4442
|
const analysis = analyzeSourceFile(sourceFile, checker, projectRoot);
|
|
@@ -3447,14 +4519,14 @@ var transformFile = (source, fileName, analysis) => {
|
|
|
3447
4519
|
};
|
|
3448
4520
|
var ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL = ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION;
|
|
3449
4521
|
var createAbsoluteMobileRouteMetadataPlugin = (options) => {
|
|
3450
|
-
const projectRoot =
|
|
3451
|
-
const entry =
|
|
4522
|
+
const projectRoot = resolve12(options.projectRoot ?? process.cwd());
|
|
4523
|
+
const entry = resolve12(options.entry);
|
|
3452
4524
|
const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
|
|
3453
4525
|
return {
|
|
3454
4526
|
name: "absolute-mobile-route-metadata",
|
|
3455
4527
|
setup(build) {
|
|
3456
4528
|
build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
|
|
3457
|
-
const analysis = analyzed.get(
|
|
4529
|
+
const analysis = analyzed.get(resolve12(path));
|
|
3458
4530
|
if (!analysis)
|
|
3459
4531
|
return;
|
|
3460
4532
|
const source = await Bun.file(path).text();
|
|
@@ -3467,32 +4539,42 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
|
|
|
3467
4539
|
};
|
|
3468
4540
|
};
|
|
3469
4541
|
var inspectAbsoluteMobileRouteMetadata = (options) => {
|
|
3470
|
-
const projectRoot =
|
|
3471
|
-
const entry =
|
|
4542
|
+
const projectRoot = resolve12(options.projectRoot ?? process.cwd());
|
|
4543
|
+
const entry = resolve12(options.entry);
|
|
3472
4544
|
const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
|
|
3473
4545
|
return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
|
|
3474
|
-
file: posixPath(
|
|
4546
|
+
file: posixPath(relative8(projectRoot, file)),
|
|
3475
4547
|
metadata
|
|
3476
4548
|
})));
|
|
3477
4549
|
};
|
|
3478
4550
|
export {
|
|
3479
4551
|
writeAbsoluteCapacitorConfig,
|
|
4552
|
+
waitForAbsoluteIosHmrLog,
|
|
3480
4553
|
verifyAbsoluteMobileCompatibilityProducer,
|
|
3481
4554
|
verifyAbsoluteMobileAssociationFiles,
|
|
4555
|
+
startAbsoluteIosDevSession,
|
|
3482
4556
|
runWithAbsoluteMobileProducer,
|
|
3483
4557
|
retainAbsoluteMobileCompatibilityArtifacts,
|
|
3484
4558
|
resolveAbsoluteMobileRoute,
|
|
3485
4559
|
resolveAbsoluteMobileDeepLink,
|
|
3486
4560
|
resolveAbsoluteMobileCompatibilityRelease,
|
|
4561
|
+
repairAbsoluteIosDevSession,
|
|
4562
|
+
redactAbsoluteIosLog,
|
|
3487
4563
|
readAbsoluteMobileMaterializedReleases,
|
|
3488
4564
|
publishAbsoluteIosRelease,
|
|
3489
4565
|
publishAbsoluteAndroidRelease,
|
|
3490
4566
|
prepareAbsoluteIosRelease,
|
|
4567
|
+
prepareAbsoluteIosDevProject,
|
|
3491
4568
|
prepareAbsoluteAndroidRelease,
|
|
4569
|
+
parseIosSimulators,
|
|
4570
|
+
parseIosRuntimes,
|
|
4571
|
+
parseIosDeviceTypes,
|
|
3492
4572
|
parseAbsoluteMobilePageRequest,
|
|
3493
4573
|
parseAbsoluteMobilePageEnvelope,
|
|
3494
4574
|
parseAbsoluteMobileCompatibilityArtifact,
|
|
3495
4575
|
parseAbsoluteMobileBuildPageMetadata,
|
|
4576
|
+
parseAbsoluteIosLogLine,
|
|
4577
|
+
parseAbsoluteIosHmrLog,
|
|
3496
4578
|
normalizeAbsoluteMobileConfig,
|
|
3497
4579
|
navigateAbsoluteMobilePage,
|
|
3498
4580
|
materializeAbsoluteMobileCompatibilityBundle,
|
|
@@ -3501,10 +4583,12 @@ export {
|
|
|
3501
4583
|
matchesAbsoluteMobileRoutePattern,
|
|
3502
4584
|
loadAbsoluteNativeReleasePublisher,
|
|
3503
4585
|
loadAbsoluteMobileMaterializedBundle,
|
|
4586
|
+
isAbsoluteIosNativeRootInput,
|
|
3504
4587
|
inspectAbsoluteMobileRouteMetadata,
|
|
3505
4588
|
hashAbsoluteMobilePropsSchema,
|
|
3506
4589
|
getCurrentAbsoluteMobileProducerContext,
|
|
3507
4590
|
fingerprintAbsoluteIosNativeProject,
|
|
4591
|
+
fingerprintAbsoluteIosDevProject,
|
|
3508
4592
|
finalizeAbsoluteMobilePage,
|
|
3509
4593
|
finalizeAbsoluteMobileCompatibilityBuild,
|
|
3510
4594
|
fetchAbsoluteMobilePage,
|
|
@@ -3519,6 +4603,7 @@ export {
|
|
|
3519
4603
|
createAbsoluteMobileBlobArtifactStore,
|
|
3520
4604
|
createAbsoluteMobileAssociationPlugin,
|
|
3521
4605
|
createAbsoluteMobileAssociationDocuments,
|
|
4606
|
+
createAbsoluteIosNativeWatcher,
|
|
3522
4607
|
carryForwardAbsoluteMobileCompatibilityReleases,
|
|
3523
4608
|
captureAbsoluteMobileRouteGraph,
|
|
3524
4609
|
buildAbsoluteMobileCompatibilityRelease,
|
|
@@ -3539,9 +4624,10 @@ export {
|
|
|
3539
4624
|
ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
|
|
3540
4625
|
ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT,
|
|
3541
4626
|
ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
|
|
4627
|
+
ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
3542
4628
|
ABSOLUTE_IOS_RELEASE_FORMAT,
|
|
3543
4629
|
ABSOLUTE_ANDROID_RELEASE_FORMAT
|
|
3544
4630
|
};
|
|
3545
4631
|
|
|
3546
|
-
//# debugId=
|
|
4632
|
+
//# debugId=048422942BB9C32864756E2164756E21
|
|
3547
4633
|
//# sourceMappingURL=index.js.map
|