@absolutejs/absolute 0.20.0-beta.60 → 0.20.0-beta.61
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/cli/index.js +547 -94
- package/dist/index.js +11 -3
- package/dist/index.js.map +3 -3
- package/dist/mobile/index.js +495 -50
- package/dist/mobile/index.js.map +7 -7
- package/dist/mobile/remoteMacAgentEntry.js +195 -195
- package/dist/mobile/shellUpdate.js +82 -6
- package/dist/src/mobile/capacitorBundle.d.ts +1 -0
- package/dist/src/mobile/config.d.ts +1 -0
- package/dist/src/mobile/transport.d.ts +1 -0
- package/dist/src/mobile/updateClient.d.ts +4 -0
- package/dist/src/mobile/updateRuntime.d.ts +1 -1
- package/dist/types/build.d.ts +2 -0
- package/package.json +7 -7
package/dist/cli/index.js
CHANGED
|
@@ -719,7 +719,7 @@ var init_portScan = () => {};
|
|
|
719
719
|
// src/mobile/config.ts
|
|
720
720
|
import { resolve as resolve2 } from "path";
|
|
721
721
|
import { createPublicKey } from "crypto";
|
|
722
|
-
var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, UPDATE_NAME_PATTERN, UPDATE_PUBLIC_KEY_PATTERN, HOSTNAME_PATTERN, EXPO_RESERVED_ROUTE_PREFIXES, resolveProjectPath = (projectRoot, value, field) => {
|
|
722
|
+
var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, UPDATE_NAME_PATTERN, UPDATE_PUBLIC_KEY_PATTERN, DEFAULT_UPDATE_BOOT_TIMEOUT_MS = 20000, MINIMUM_UPDATE_BOOT_TIMEOUT_MS = 5000, MAXIMUM_UPDATE_BOOT_TIMEOUT_MS = 120000, HOSTNAME_PATTERN, EXPO_RESERVED_ROUTE_PREFIXES, resolveProjectPath = (projectRoot, value, field) => {
|
|
723
723
|
const root = resolve2(projectRoot);
|
|
724
724
|
const path = resolve2(root, value);
|
|
725
725
|
if (path !== root && !path.startsWith(`${root}/`)) {
|
|
@@ -795,6 +795,9 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
795
795
|
].sort(), normalizeUpdates = (config, productionOrigin) => {
|
|
796
796
|
if (!config.updates)
|
|
797
797
|
return;
|
|
798
|
+
const bootTimeoutMs = config.updates.bootTimeoutMs ?? DEFAULT_UPDATE_BOOT_TIMEOUT_MS;
|
|
799
|
+
if (!Number.isSafeInteger(bootTimeoutMs) || bootTimeoutMs < MINIMUM_UPDATE_BOOT_TIMEOUT_MS || bootTimeoutMs > MAXIMUM_UPDATE_BOOT_TIMEOUT_MS)
|
|
800
|
+
throw new TypeError("mobile.updates.bootTimeoutMs must be an integer from 5000 through 120000.");
|
|
798
801
|
const channel = requireText(config.updates.channel ?? "production", "mobile.updates.channel");
|
|
799
802
|
if (!UPDATE_NAME_PATTERN.test(channel))
|
|
800
803
|
throw new TypeError("mobile.updates.channel contains unsupported characters.");
|
|
@@ -836,7 +839,12 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
836
839
|
throw new TypeError(`mobile.updates.publicKeys.${keyId} is not an ECDSA P-256 SPKI public key.`);
|
|
837
840
|
return [keyId, normalized];
|
|
838
841
|
}));
|
|
839
|
-
return {
|
|
842
|
+
return {
|
|
843
|
+
bootTimeoutMs,
|
|
844
|
+
channel,
|
|
845
|
+
manifestUrl: manifestUrl.href,
|
|
846
|
+
publicKeys
|
|
847
|
+
};
|
|
840
848
|
}, validateExpoNativeRouteSegment = (path, segment, index, count, parameters) => {
|
|
841
849
|
if (segment === "*" && (index !== count - 1 || count === 1)) {
|
|
842
850
|
throw new TypeError(`mobile.routes.native route ${path} must use * once, as the final segment after a static or parameterized prefix.`);
|
|
@@ -9660,7 +9668,7 @@ var init_updateProtocol = __esm(() => {
|
|
|
9660
9668
|
|
|
9661
9669
|
// src/mobile/updateRuntime.ts
|
|
9662
9670
|
import { createHash as createHash12 } from "crypto";
|
|
9663
|
-
var ABSOLUTE_MOBILE_SHELL_ABI =
|
|
9671
|
+
var ABSOLUTE_MOBILE_SHELL_ABI = 2, ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT = 1, createAbsoluteMobileUpdateRuntimeDescriptor = (options) => ({
|
|
9664
9672
|
appId: options.config.appId,
|
|
9665
9673
|
auth: options.auth ?? null,
|
|
9666
9674
|
deepLinks: {
|
|
@@ -19664,70 +19672,490 @@ var init_nativeBackgroundSync = __esm(() => {
|
|
|
19664
19672
|
});
|
|
19665
19673
|
|
|
19666
19674
|
// src/mobile/nativeUpdates.ts
|
|
19667
|
-
import {
|
|
19668
|
-
|
|
19669
|
-
|
|
19670
|
-
|
|
19675
|
+
import {
|
|
19676
|
+
mkdir as mkdir11,
|
|
19677
|
+
readFile as readFile16,
|
|
19678
|
+
readdir as readdir5,
|
|
19679
|
+
rename as rename12,
|
|
19680
|
+
rm as rm9,
|
|
19681
|
+
writeFile as writeFile13
|
|
19682
|
+
} from "fs/promises";
|
|
19683
|
+
import { dirname as dirname29, join as join51 } from "path";
|
|
19684
|
+
var START = "// absolutejs:mobile-updates:start", END = "// absolutejs:mobile-updates:end", PLUGIN_START = "// absolutejs:mobile-update-plugin:start", PLUGIN_END = "// absolutejs:mobile-update-plugin:end", ANDROID_PLUGIN = "AbsoluteMobileUpdateWatchdogPlugin.java", optionalSource2 = async (path) => {
|
|
19685
|
+
try {
|
|
19686
|
+
return await readFile16(path, "utf8");
|
|
19687
|
+
} catch (error) {
|
|
19688
|
+
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
19689
|
+
return null;
|
|
19690
|
+
throw error;
|
|
19691
|
+
}
|
|
19692
|
+
}, isRecord14 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), writeChanged2 = async (path, source) => {
|
|
19693
|
+
const current = await optionalSource2(path);
|
|
19671
19694
|
if (current === source)
|
|
19672
19695
|
return false;
|
|
19696
|
+
await mkdir11(dirname29(path), { recursive: true });
|
|
19697
|
+
if (current === null) {
|
|
19698
|
+
await writeFile13(path, source, { flag: "wx" });
|
|
19699
|
+
return true;
|
|
19700
|
+
}
|
|
19673
19701
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
19674
19702
|
await writeFile13(temporary, source, { flag: "wx" });
|
|
19675
19703
|
await rename12(temporary, path);
|
|
19676
19704
|
return true;
|
|
19677
|
-
},
|
|
19678
|
-
|
|
19679
|
-
|
|
19680
|
-
|
|
19705
|
+
}, removeOwnedFile = async (path) => {
|
|
19706
|
+
if (await optionalSource2(path) === null)
|
|
19707
|
+
return false;
|
|
19708
|
+
await rm9(path);
|
|
19709
|
+
return true;
|
|
19710
|
+
}, replaceMarkedRegion = (source, start2, end, region, insertion, error) => {
|
|
19711
|
+
const existingStart = source.indexOf(start2);
|
|
19712
|
+
const existingEnd = source.indexOf(end);
|
|
19713
|
+
if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
|
|
19681
19714
|
throw new TypeError("AbsoluteJS mobile update markers are malformed.");
|
|
19682
|
-
if (
|
|
19715
|
+
if (existingStart >= 0) {
|
|
19683
19716
|
const from = source.lastIndexOf(`
|
|
19684
|
-
`,
|
|
19717
|
+
`, existingStart) + 1;
|
|
19685
19718
|
const newline = source.indexOf(`
|
|
19686
|
-
`,
|
|
19719
|
+
`, existingEnd + end.length);
|
|
19687
19720
|
return `${source.slice(0, from)}${region}${source.slice(newline < 0 ? source.length : newline + 1)}`;
|
|
19688
19721
|
}
|
|
19689
19722
|
if (!region)
|
|
19690
19723
|
return source;
|
|
19724
|
+
if (insertion < 0)
|
|
19725
|
+
throw new TypeError(error);
|
|
19726
|
+
return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
|
|
19727
|
+
}, iosPluginRegion = (timeoutMs) => `${PLUGIN_START}
|
|
19728
|
+
@objc(AbsoluteMobileUpdateWatchdogPlugin)
|
|
19729
|
+
public final class AbsoluteMobileUpdateWatchdogPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
19730
|
+
public let identifier = "AbsoluteMobileUpdateWatchdogPlugin"
|
|
19731
|
+
public let jsName = "AbsoluteMobileUpdateWatchdog"
|
|
19732
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
19733
|
+
CAPPluginMethod(name: "arm", returnType: CAPPluginReturnPromise),
|
|
19734
|
+
CAPPluginMethod(name: "confirm", returnType: CAPPluginReturnPromise)
|
|
19735
|
+
]
|
|
19736
|
+
|
|
19737
|
+
private static let stateKey = "CapacitorStorage.absolute.mobile.update.state.v1"
|
|
19738
|
+
private static let releasePattern = try! NSRegularExpression(pattern: "^amu_[a-f0-9]{64}$")
|
|
19739
|
+
private var deadline: DispatchWorkItem?
|
|
19740
|
+
|
|
19741
|
+
private static func state() -> [String: Any] {
|
|
19742
|
+
guard let encoded = UserDefaults.standard.string(forKey: stateKey),
|
|
19743
|
+
let data = encoded.data(using: .utf8),
|
|
19744
|
+
let object = try? JSONSerialization.jsonObject(with: data),
|
|
19745
|
+
let value = object as? [String: Any] else { return [:] }
|
|
19746
|
+
return value
|
|
19747
|
+
}
|
|
19748
|
+
|
|
19749
|
+
private static func write(_ state: [String: Any]) {
|
|
19750
|
+
guard let data = try? JSONSerialization.data(withJSONObject: state),
|
|
19751
|
+
let encoded = String(data: data, encoding: .utf8) else { return }
|
|
19752
|
+
UserDefaults.standard.set(encoded, forKey: stateKey)
|
|
19753
|
+
}
|
|
19754
|
+
|
|
19755
|
+
private static func validRelease(_ value: String) -> Bool {
|
|
19756
|
+
releasePattern.firstMatch(in: value, range: NSRange(value.startIndex..., in: value)) != nil
|
|
19757
|
+
}
|
|
19758
|
+
|
|
19759
|
+
private static func snapshotRoot() -> URL? {
|
|
19760
|
+
FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first?
|
|
19761
|
+
.appendingPathComponent("NoCloud/ionic_built_snapshots", isDirectory: true)
|
|
19762
|
+
}
|
|
19763
|
+
|
|
19764
|
+
@discardableResult
|
|
19765
|
+
private static func recover(_ reason: String) -> (String, String?, Bool)? {
|
|
19766
|
+
var value = state()
|
|
19767
|
+
guard let release = value["pendingRelease"] as? String, validRelease(release) else { return nil }
|
|
19768
|
+
let previous = value["previousPath"] as? String
|
|
19769
|
+
let active = value["activeRelease"] as? String
|
|
19770
|
+
let hasActive = active.map(validRelease) ?? false
|
|
19771
|
+
let started = value["pendingStartedAt"] as? Double ?? Date().timeIntervalSince1970 * 1000
|
|
19772
|
+
let duration = max(0, Date().timeIntervalSince1970 * 1000 - started)
|
|
19773
|
+
value.removeValue(forKey: "pendingRelease")
|
|
19774
|
+
value.removeValue(forKey: "pendingStartedAt")
|
|
19775
|
+
value.removeValue(forKey: "previousPath")
|
|
19776
|
+
value.removeValue(forKey: "readyRelease")
|
|
19777
|
+
var quarantined = value["quarantinedReleases"] as? [String] ?? []
|
|
19778
|
+
quarantined.removeAll(where: { $0 == release })
|
|
19779
|
+
quarantined.append(release)
|
|
19780
|
+
value["quarantinedReleases"] = Array(quarantined.suffix(8))
|
|
19781
|
+
value["recovery"] = ["durationMs": duration, "reason": reason, "releaseId": release]
|
|
19782
|
+
write(value)
|
|
19783
|
+
|
|
19784
|
+
if hasActive, let previous, let root = snapshotRoot(),
|
|
19785
|
+
URL(fileURLWithPath: previous).standardizedFileURL.path.hasPrefix(root.standardizedFileURL.path + "/") {
|
|
19786
|
+
UserDefaults.standard.set(previous, forKey: "serverBasePath")
|
|
19787
|
+
} else {
|
|
19788
|
+
UserDefaults.standard.removeObject(forKey: "serverBasePath")
|
|
19789
|
+
}
|
|
19790
|
+
if let root = snapshotRoot() {
|
|
19791
|
+
try? FileManager.default.removeItem(at: root.appendingPathComponent(release, isDirectory: true))
|
|
19792
|
+
}
|
|
19793
|
+
|
|
19794
|
+
return (release, previous, hasActive)
|
|
19795
|
+
}
|
|
19796
|
+
|
|
19797
|
+
public static func recoverInterruptedBoot() {
|
|
19798
|
+
if recover("boot-interrupted") != nil { return }
|
|
19799
|
+
guard let persisted = UserDefaults.standard.string(forKey: "serverBasePath"),
|
|
19800
|
+
let root = snapshotRoot() else { return }
|
|
19801
|
+
let path = URL(fileURLWithPath: persisted).standardizedFileURL.path
|
|
19802
|
+
if path.hasPrefix(root.standardizedFileURL.path + "/") &&
|
|
19803
|
+
!FileManager.default.fileExists(atPath: path) {
|
|
19804
|
+
UserDefaults.standard.removeObject(forKey: "serverBasePath")
|
|
19805
|
+
}
|
|
19806
|
+
}
|
|
19807
|
+
|
|
19808
|
+
@objc public func arm(_ call: CAPPluginCall) {
|
|
19809
|
+
guard let requested = call.getString("releaseId"),
|
|
19810
|
+
Self.validRelease(requested),
|
|
19811
|
+
Self.state()["pendingRelease"] as? String == requested else {
|
|
19812
|
+
call.reject("Mobile update watchdog cannot arm an unknown release.")
|
|
19813
|
+
return
|
|
19814
|
+
}
|
|
19815
|
+
deadline?.cancel()
|
|
19816
|
+
let work = DispatchWorkItem { [weak self] in
|
|
19817
|
+
guard let self, let recovery = Self.recover("boot-timeout") else { return }
|
|
19818
|
+
if recovery.2, let previous = recovery.1,
|
|
19819
|
+
let root = Self.snapshotRoot(),
|
|
19820
|
+
URL(fileURLWithPath: previous).standardizedFileURL.path.hasPrefix(root.standardizedFileURL.path + "/") {
|
|
19821
|
+
(self.bridge?.viewController as? CAPBridgeViewController)?.setServerBasePath(path: previous)
|
|
19822
|
+
} else if let embedded = Bundle.main.url(forResource: "public", withExtension: nil)?.path {
|
|
19823
|
+
(self.bridge?.viewController as? CAPBridgeViewController)?.setServerBasePath(path: embedded)
|
|
19824
|
+
}
|
|
19825
|
+
}
|
|
19826
|
+
deadline = work
|
|
19827
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(${timeoutMs}), execute: work)
|
|
19828
|
+
call.resolve()
|
|
19829
|
+
}
|
|
19830
|
+
|
|
19831
|
+
@objc public func confirm(_ call: CAPPluginCall) {
|
|
19832
|
+
deadline?.cancel()
|
|
19833
|
+
deadline = nil
|
|
19834
|
+
call.resolve()
|
|
19835
|
+
}
|
|
19836
|
+
}
|
|
19837
|
+
${PLUGIN_END}
|
|
19838
|
+
`, configureIos3 = async (config, enabled) => {
|
|
19839
|
+
const path = join51(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
19840
|
+
const capacitorConfigPath = join51(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
|
|
19841
|
+
let source = await readFile16(path, "utf8");
|
|
19691
19842
|
const launch = source.indexOf("didFinishLaunchingWithOptions");
|
|
19692
19843
|
const brace = launch < 0 ? -1 : source.indexOf("{", launch);
|
|
19693
|
-
const
|
|
19694
|
-
|
|
19695
|
-
|
|
19696
|
-
|
|
19697
|
-
|
|
19698
|
-
|
|
19699
|
-
|
|
19700
|
-
|
|
19701
|
-
const
|
|
19702
|
-
|
|
19703
|
-
|
|
19704
|
-
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19708
|
-
|
|
19709
|
-
|
|
19710
|
-
|
|
19711
|
-
|
|
19712
|
-
|
|
19713
|
-
|
|
19714
|
-
|
|
19715
|
-
|
|
19716
|
-
|
|
19717
|
-
|
|
19844
|
+
const launchRegion = enabled ? ` ${START}
|
|
19845
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot()
|
|
19846
|
+
${END}
|
|
19847
|
+
` : "";
|
|
19848
|
+
source = replaceMarkedRegion(source, START, END, launchRegion, brace < 0 ? -1 : source.indexOf(`
|
|
19849
|
+
`, brace) + 1, "Could not find a safe iOS location for mobile update recovery.");
|
|
19850
|
+
const plugin = enabled ? iosPluginRegion(config.updates?.bootTimeoutMs ?? 20000) : "";
|
|
19851
|
+
source = replaceMarkedRegion(source, PLUGIN_START, PLUGIN_END, plugin, source.length, "Could not find a safe iOS location for the mobile update watchdog.");
|
|
19852
|
+
const capacitorConfigSource2 = await readFile16(capacitorConfigPath, "utf8");
|
|
19853
|
+
let capacitorConfig;
|
|
19854
|
+
try {
|
|
19855
|
+
const value = JSON.parse(capacitorConfigSource2);
|
|
19856
|
+
if (!isRecord14(value))
|
|
19857
|
+
throw new TypeError;
|
|
19858
|
+
capacitorConfig = value;
|
|
19859
|
+
} catch {
|
|
19860
|
+
throw new TypeError("iOS capacitor.config.json is invalid; run Capacitor sync before projecting mobile updates.");
|
|
19861
|
+
}
|
|
19862
|
+
const packageClassList = Reflect.get(capacitorConfig, "packageClassList");
|
|
19863
|
+
if (!Array.isArray(packageClassList))
|
|
19864
|
+
throw new TypeError("iOS capacitor.config.json has no packageClassList; run Capacitor sync before projecting mobile updates.");
|
|
19865
|
+
const classes = packageClassList.filter((value) => typeof value === "string");
|
|
19866
|
+
const nextClasses = enabled ? [...new Set([...classes, "AbsoluteMobileUpdateWatchdogPlugin"])] : classes.filter((value) => value !== "AbsoluteMobileUpdateWatchdogPlugin");
|
|
19867
|
+
const nextCapacitorConfig = `${JSON.stringify({ ...capacitorConfig, packageClassList: nextClasses }, null, "\t")}
|
|
19868
|
+
`;
|
|
19869
|
+
const changed = await Promise.all([
|
|
19870
|
+
writeChanged2(path, source),
|
|
19871
|
+
writeChanged2(capacitorConfigPath, nextCapacitorConfig)
|
|
19872
|
+
]);
|
|
19873
|
+
return changed.some(Boolean);
|
|
19874
|
+
}, androidPluginSource = (packageName, timeoutMs) => `package ${packageName};
|
|
19875
|
+
|
|
19876
|
+
import android.app.Activity;
|
|
19877
|
+
import android.content.Context;
|
|
19878
|
+
import android.content.SharedPreferences;
|
|
19879
|
+
import android.os.Handler;
|
|
19880
|
+
import android.os.Looper;
|
|
19881
|
+
import com.getcapacitor.JSObject;
|
|
19882
|
+
import com.getcapacitor.Plugin;
|
|
19883
|
+
import com.getcapacitor.PluginCall;
|
|
19884
|
+
import com.getcapacitor.PluginMethod;
|
|
19885
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
19886
|
+
import java.io.File;
|
|
19887
|
+
import org.json.JSONArray;
|
|
19888
|
+
import org.json.JSONObject;
|
|
19889
|
+
|
|
19890
|
+
@CapacitorPlugin(name = "AbsoluteMobileUpdateWatchdog")
|
|
19891
|
+
public final class AbsoluteMobileUpdateWatchdogPlugin extends Plugin {
|
|
19892
|
+
private static final String STATE_KEY = "absolute.mobile.update.state.v1";
|
|
19893
|
+
private static final String RELEASE_PATTERN = "^amu_[a-f0-9]{64}$";
|
|
19894
|
+
private final Handler handler = new Handler(Looper.getMainLooper());
|
|
19895
|
+
private Runnable deadline;
|
|
19896
|
+
|
|
19897
|
+
private static SharedPreferences statePreferences(Context context) {
|
|
19898
|
+
return context.getSharedPreferences("CapacitorStorage", Activity.MODE_PRIVATE);
|
|
19899
|
+
}
|
|
19900
|
+
|
|
19901
|
+
private static JSONObject state(Context context) {
|
|
19902
|
+
try {
|
|
19903
|
+
return new JSONObject(statePreferences(context).getString(STATE_KEY, "{}"));
|
|
19904
|
+
} catch (Exception ignored) {
|
|
19905
|
+
return new JSONObject();
|
|
19906
|
+
}
|
|
19907
|
+
}
|
|
19908
|
+
|
|
19909
|
+
private static void write(Context context, JSONObject state) {
|
|
19910
|
+
statePreferences(context).edit().putString(STATE_KEY, state.toString()).commit();
|
|
19911
|
+
}
|
|
19912
|
+
|
|
19913
|
+
private static File snapshotRoot(Context context) {
|
|
19914
|
+
return new File(context.getFilesDir(), "NoCloud/ionic_built_snapshots");
|
|
19915
|
+
}
|
|
19916
|
+
|
|
19917
|
+
private static boolean validRelease(String release) {
|
|
19918
|
+
return release != null && release.matches(RELEASE_PATTERN);
|
|
19919
|
+
}
|
|
19920
|
+
|
|
19921
|
+
private static boolean inside(File root, String path) {
|
|
19922
|
+
try {
|
|
19923
|
+
String base = root.getCanonicalPath() + File.separator;
|
|
19924
|
+
return new File(path).getCanonicalPath().startsWith(base);
|
|
19925
|
+
} catch (Exception ignored) {
|
|
19926
|
+
return false;
|
|
19927
|
+
}
|
|
19928
|
+
}
|
|
19929
|
+
|
|
19930
|
+
private static void deleteTree(File file) {
|
|
19931
|
+
File[] children = file.listFiles();
|
|
19932
|
+
if (children != null) for (File child : children) deleteTree(child);
|
|
19933
|
+
file.delete();
|
|
19934
|
+
}
|
|
19935
|
+
|
|
19936
|
+
private static Recovery recover(Context context, String reason) {
|
|
19937
|
+
JSONObject value = state(context);
|
|
19938
|
+
String release = value.optString("pendingRelease", "");
|
|
19939
|
+
if (!validRelease(release)) return null;
|
|
19940
|
+
String previous = value.optString("previousPath", "");
|
|
19941
|
+
boolean hasActive = validRelease(value.optString("activeRelease", ""));
|
|
19942
|
+
long started = value.optLong("pendingStartedAt", System.currentTimeMillis());
|
|
19943
|
+
long duration = Math.max(0, System.currentTimeMillis() - started);
|
|
19944
|
+
value.remove("pendingRelease");
|
|
19945
|
+
value.remove("pendingStartedAt");
|
|
19946
|
+
value.remove("previousPath");
|
|
19947
|
+
value.remove("readyRelease");
|
|
19948
|
+
try {
|
|
19949
|
+
JSONArray prior = value.optJSONArray("quarantinedReleases");
|
|
19950
|
+
JSONArray quarantined = new JSONArray();
|
|
19951
|
+
if (prior != null) {
|
|
19952
|
+
int start = Math.max(0, prior.length() - 7);
|
|
19953
|
+
for (int index = start; index < prior.length(); index++) {
|
|
19954
|
+
String candidate = prior.optString(index, "");
|
|
19955
|
+
if (validRelease(candidate) && !release.equals(candidate)) quarantined.put(candidate);
|
|
19956
|
+
}
|
|
19718
19957
|
}
|
|
19958
|
+
quarantined.put(release);
|
|
19959
|
+
value.put("quarantinedReleases", quarantined);
|
|
19960
|
+
value.put("recovery", new JSONObject()
|
|
19961
|
+
.put("durationMs", duration)
|
|
19962
|
+
.put("reason", reason)
|
|
19963
|
+
.put("releaseId", release));
|
|
19964
|
+
} catch (Exception ignored) {}
|
|
19965
|
+
write(context, value);
|
|
19966
|
+
|
|
19967
|
+
SharedPreferences webView = context.getSharedPreferences("CapWebViewSettings", Activity.MODE_PRIVATE);
|
|
19968
|
+
if (hasActive && inside(snapshotRoot(context), previous)) {
|
|
19969
|
+
webView.edit().putString("serverBasePath", previous).commit();
|
|
19970
|
+
} else {
|
|
19971
|
+
webView.edit().remove("serverBasePath").commit();
|
|
19972
|
+
}
|
|
19973
|
+
deleteTree(new File(snapshotRoot(context), release));
|
|
19974
|
+
return new Recovery(previous, hasActive);
|
|
19975
|
+
}
|
|
19976
|
+
|
|
19977
|
+
public static void recoverInterruptedBoot(Context context) {
|
|
19978
|
+
recover(context, "boot-interrupted");
|
|
19979
|
+
}
|
|
19980
|
+
|
|
19981
|
+
@PluginMethod
|
|
19982
|
+
public void arm(PluginCall call) {
|
|
19983
|
+
String requested = call.getString("releaseId");
|
|
19984
|
+
if (!validRelease(requested) || !requested.equals(state(getContext()).optString("pendingRelease", ""))) {
|
|
19985
|
+
call.reject("Mobile update watchdog cannot arm an unknown release.");
|
|
19986
|
+
return;
|
|
19719
19987
|
}
|
|
19988
|
+
if (deadline != null) handler.removeCallbacks(deadline);
|
|
19989
|
+
deadline = () -> {
|
|
19990
|
+
Recovery recovery = recover(getContext(), "boot-timeout");
|
|
19991
|
+
if (recovery == null || bridge == null) return;
|
|
19992
|
+
if (recovery.hasActive && inside(snapshotRoot(getContext()), recovery.previousPath)) {
|
|
19993
|
+
bridge.setServerBasePath(recovery.previousPath);
|
|
19994
|
+
} else {
|
|
19995
|
+
bridge.setServerAssetPath("public");
|
|
19996
|
+
}
|
|
19997
|
+
};
|
|
19998
|
+
handler.postDelayed(deadline, ${timeoutMs}L);
|
|
19999
|
+
call.resolve(new JSObject());
|
|
20000
|
+
}
|
|
20001
|
+
|
|
20002
|
+
@PluginMethod
|
|
20003
|
+
public void confirm(PluginCall call) {
|
|
20004
|
+
if (deadline != null) handler.removeCallbacks(deadline);
|
|
20005
|
+
deadline = null;
|
|
20006
|
+
call.resolve(new JSObject());
|
|
20007
|
+
}
|
|
20008
|
+
|
|
20009
|
+
private static final class Recovery {
|
|
20010
|
+
final String previousPath;
|
|
20011
|
+
final boolean hasActive;
|
|
20012
|
+
Recovery(String previousPath, boolean hasActive) {
|
|
20013
|
+
this.previousPath = previousPath;
|
|
20014
|
+
this.hasActive = hasActive;
|
|
20015
|
+
}
|
|
20016
|
+
}
|
|
20017
|
+
}
|
|
20018
|
+
`, androidActivityInRoot = async (root) => {
|
|
20019
|
+
let entries;
|
|
20020
|
+
try {
|
|
20021
|
+
entries = await readdir5(root, { recursive: true });
|
|
20022
|
+
} catch (error) {
|
|
20023
|
+
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
20024
|
+
return;
|
|
20025
|
+
throw error;
|
|
20026
|
+
}
|
|
20027
|
+
const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
|
|
20028
|
+
return candidate ? join51(root, candidate) : undefined;
|
|
20029
|
+
}, androidActivity = async (config) => {
|
|
20030
|
+
const roots = [
|
|
20031
|
+
join51(config.nativeProjectDirectory, "android/app/src/main/java"),
|
|
20032
|
+
join51(config.nativeProjectDirectory, "android/app/src/main/kotlin")
|
|
20033
|
+
];
|
|
20034
|
+
const candidates = await Promise.all(roots.map(androidActivityInRoot));
|
|
20035
|
+
const candidate = candidates.find((value) => value !== undefined);
|
|
20036
|
+
if (candidate)
|
|
20037
|
+
return candidate;
|
|
20038
|
+
throw new TypeError("Android MainActivity.java or MainActivity.kt was not found.");
|
|
20039
|
+
}, androidManagedRegion = (kotlin, generatedMethod) => {
|
|
20040
|
+
if (generatedMethod)
|
|
20041
|
+
return kotlin ? ` ${START}
|
|
20042
|
+
override fun onCreate(savedInstanceState: android.os.Bundle?) {
|
|
20043
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot(this)
|
|
20044
|
+
registerPlugin(AbsoluteMobileUpdateWatchdogPlugin::class.java)
|
|
20045
|
+
super.onCreate(savedInstanceState)
|
|
20046
|
+
}
|
|
20047
|
+
${END}
|
|
20048
|
+
` : ` ${START}
|
|
20049
|
+
@Override
|
|
20050
|
+
public void onCreate(android.os.Bundle savedInstanceState) {
|
|
20051
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot(this);
|
|
20052
|
+
registerPlugin(AbsoluteMobileUpdateWatchdogPlugin.class);
|
|
20053
|
+
super.onCreate(savedInstanceState);
|
|
20054
|
+
}
|
|
20055
|
+
${END}
|
|
20056
|
+
`;
|
|
20057
|
+
return kotlin ? ` ${START}
|
|
20058
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot(this)
|
|
20059
|
+
registerPlugin(AbsoluteMobileUpdateWatchdogPlugin::class.java)
|
|
20060
|
+
${END}
|
|
20061
|
+
` : ` ${START}
|
|
20062
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot(this);
|
|
20063
|
+
registerPlugin(AbsoluteMobileUpdateWatchdogPlugin.class);
|
|
19720
20064
|
${END}
|
|
19721
20065
|
`;
|
|
19722
|
-
})
|
|
20066
|
+
}, injectAndroidActivity = (source, enabled) => {
|
|
20067
|
+
const kotlin = /\bfun\s+onCreate\s*\(/u.test(source) || source.includes("BridgeActivity()");
|
|
20068
|
+
const existingStart = source.indexOf(START);
|
|
20069
|
+
const existingEnd = source.indexOf(END);
|
|
20070
|
+
if (existingStart < 0 !== existingEnd < 0)
|
|
20071
|
+
throw new TypeError("AbsoluteJS mobile update markers are malformed.");
|
|
20072
|
+
if (existingStart >= 0) {
|
|
20073
|
+
const from = source.lastIndexOf(`
|
|
20074
|
+
`, existingStart) + 1;
|
|
20075
|
+
const newline = source.indexOf(`
|
|
20076
|
+
`, existingEnd + END.length);
|
|
20077
|
+
const through = newline < 0 ? source.length : newline + 1;
|
|
20078
|
+
const owned = source.slice(from, through);
|
|
20079
|
+
const generatedMethod = owned.includes("onCreate(");
|
|
20080
|
+
if (!enabled)
|
|
20081
|
+
return `${source.slice(0, from)}${source.slice(through)}`;
|
|
20082
|
+
const region2 = androidManagedRegion(kotlin, generatedMethod);
|
|
20083
|
+
return `${source.slice(0, from)}${region2}${source.slice(through)}`;
|
|
20084
|
+
}
|
|
20085
|
+
if (!enabled)
|
|
20086
|
+
return source;
|
|
20087
|
+
const onCreate = kotlin ? source.search(/\boverride\s+fun\s+onCreate\s*\([^)]*\)\s*\{/u) : source.search(/\b(?:public|protected)\s+void\s+onCreate\s*\([^)]*\)\s*\{/u);
|
|
20088
|
+
if (onCreate >= 0) {
|
|
20089
|
+
const brace = source.indexOf("{", onCreate);
|
|
20090
|
+
const insert = source.indexOf(`
|
|
20091
|
+
`, brace) + 1;
|
|
20092
|
+
const region2 = kotlin ? ` ${START}
|
|
20093
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot(this)
|
|
20094
|
+
registerPlugin(AbsoluteMobileUpdateWatchdogPlugin::class.java)
|
|
20095
|
+
${END}
|
|
20096
|
+
` : ` ${START}
|
|
20097
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot(this);
|
|
20098
|
+
registerPlugin(AbsoluteMobileUpdateWatchdogPlugin.class);
|
|
20099
|
+
${END}
|
|
20100
|
+
`;
|
|
20101
|
+
return `${source.slice(0, insert)}${region2}${source.slice(insert)}`;
|
|
20102
|
+
}
|
|
20103
|
+
const close = source.lastIndexOf("}");
|
|
20104
|
+
if (close < 0)
|
|
20105
|
+
throw new TypeError("Could not find the Android MainActivity class body.");
|
|
20106
|
+
const region = kotlin ? ` ${START}
|
|
20107
|
+
override fun onCreate(savedInstanceState: android.os.Bundle?) {
|
|
20108
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot(this)
|
|
20109
|
+
registerPlugin(AbsoluteMobileUpdateWatchdogPlugin::class.java)
|
|
20110
|
+
super.onCreate(savedInstanceState)
|
|
20111
|
+
}
|
|
20112
|
+
${END}
|
|
20113
|
+
` : ` ${START}
|
|
20114
|
+
@Override
|
|
20115
|
+
public void onCreate(android.os.Bundle savedInstanceState) {
|
|
20116
|
+
AbsoluteMobileUpdateWatchdogPlugin.recoverInterruptedBoot(this);
|
|
20117
|
+
registerPlugin(AbsoluteMobileUpdateWatchdogPlugin.class);
|
|
20118
|
+
super.onCreate(savedInstanceState);
|
|
20119
|
+
}
|
|
20120
|
+
${END}
|
|
20121
|
+
`;
|
|
20122
|
+
const separator = source[close - 1] === `
|
|
20123
|
+
` ? "" : `
|
|
20124
|
+
`;
|
|
20125
|
+
return `${source.slice(0, close)}${separator}${region}${source.slice(close)}`;
|
|
20126
|
+
}, configureAndroid3 = async (config, enabled) => {
|
|
20127
|
+
const activityPath = await androidActivity(config);
|
|
20128
|
+
const activity = await readFile16(activityPath, "utf8");
|
|
20129
|
+
const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
|
|
20130
|
+
if (!packageName)
|
|
20131
|
+
throw new TypeError("Android MainActivity package declaration was not found.");
|
|
20132
|
+
const packagePath = packageName.replaceAll(".", "/");
|
|
20133
|
+
const pluginPath = join51(config.nativeProjectDirectory, "android/app/src/main/java", packagePath, ANDROID_PLUGIN);
|
|
20134
|
+
const changed = await Promise.all([
|
|
20135
|
+
writeChanged2(activityPath, injectAndroidActivity(activity, enabled)),
|
|
20136
|
+
enabled ? writeChanged2(pluginPath, androidPluginSource(packageName, config.updates?.bootTimeoutMs ?? 20000)) : removeOwnedFile(pluginPath)
|
|
20137
|
+
]);
|
|
20138
|
+
return changed.some(Boolean);
|
|
20139
|
+
}, applyAbsoluteNativeUpdates = async (config, platforms = config.platforms) => {
|
|
20140
|
+
if (config.engine !== "capacitor")
|
|
20141
|
+
return { changed: false };
|
|
20142
|
+
const enabled = config.updates !== undefined;
|
|
20143
|
+
const changed = [];
|
|
20144
|
+
if (platforms.includes("ios"))
|
|
20145
|
+
changed.push(await configureIos3(config, enabled));
|
|
20146
|
+
if (platforms.includes("android"))
|
|
20147
|
+
changed.push(await configureAndroid3(config, enabled));
|
|
20148
|
+
return { changed: changed.some(Boolean) };
|
|
20149
|
+
};
|
|
20150
|
+
var init_nativeUpdates = () => {};
|
|
19723
20151
|
|
|
19724
20152
|
// src/mobile/associationFiles.ts
|
|
19725
20153
|
import {
|
|
19726
20154
|
access as access9,
|
|
19727
|
-
mkdir as
|
|
20155
|
+
mkdir as mkdir12,
|
|
19728
20156
|
readFile as readFile17,
|
|
19729
20157
|
rename as rename13,
|
|
19730
|
-
rm as
|
|
20158
|
+
rm as rm10,
|
|
19731
20159
|
writeFile as writeFile14
|
|
19732
20160
|
} from "fs/promises";
|
|
19733
20161
|
import { resolve as resolve37 } from "path";
|
|
@@ -19828,10 +20256,10 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
|
|
|
19828
20256
|
throw error;
|
|
19829
20257
|
}
|
|
19830
20258
|
if (hasCurrent)
|
|
19831
|
-
await
|
|
20259
|
+
await rm10(backup, { force: true, recursive: true });
|
|
19832
20260
|
}, materializeHost = async (root, host2, files) => {
|
|
19833
20261
|
const directory = resolve37(root, host2, ".well-known");
|
|
19834
|
-
await
|
|
20262
|
+
await mkdir12(directory, { recursive: true });
|
|
19835
20263
|
return Promise.all(files.map(async ([name, document]) => {
|
|
19836
20264
|
const path = resolve37(directory, name);
|
|
19837
20265
|
await writeAtomic(path, `${JSON.stringify(document, null, 2)}
|
|
@@ -19867,7 +20295,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
|
|
|
19867
20295
|
if (documents.apple) {
|
|
19868
20296
|
files.push(["apple-app-site-association", documents.apple]);
|
|
19869
20297
|
}
|
|
19870
|
-
await
|
|
20298
|
+
await mkdir12(temporary, { recursive: true });
|
|
19871
20299
|
try {
|
|
19872
20300
|
const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host2) => materializeHost(temporary, host2, files)))).flat();
|
|
19873
20301
|
await writeAtomic(resolve37(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
|
|
@@ -19876,7 +20304,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
|
|
|
19876
20304
|
const written = temporaryPaths.map((path) => resolve37(root, path.slice(temporary.length + 1)));
|
|
19877
20305
|
return { root, written };
|
|
19878
20306
|
} catch (error) {
|
|
19879
|
-
await
|
|
20307
|
+
await rm10(temporary, { force: true, recursive: true });
|
|
19880
20308
|
throw error;
|
|
19881
20309
|
}
|
|
19882
20310
|
}, verifyAbsoluteMobileAssociationFiles = async (config, request = globalThis.fetch) => {
|
|
@@ -19914,8 +20342,8 @@ var init_associationFiles = __esm(() => {
|
|
|
19914
20342
|
});
|
|
19915
20343
|
|
|
19916
20344
|
// src/mobile/androidWebView.ts
|
|
19917
|
-
import { mkdir as
|
|
19918
|
-
import { dirname as
|
|
20345
|
+
import { mkdir as mkdir13, writeFile as writeFile15 } from "fs/promises";
|
|
20346
|
+
import { dirname as dirname30, resolve as resolve38 } from "path";
|
|
19919
20347
|
|
|
19920
20348
|
class CdpConnection {
|
|
19921
20349
|
#closed;
|
|
@@ -20189,7 +20617,7 @@ var CDP_COMMAND_TIMEOUT_MS = 1e4, WEBVIEW_ATTACH_TIMEOUT_MS = 30000, WEBVIEW_POL
|
|
|
20189
20617
|
throw new Error("Android WebView screenshot returned no image data.");
|
|
20190
20618
|
}
|
|
20191
20619
|
const absolutePath = resolve38(path);
|
|
20192
|
-
await
|
|
20620
|
+
await mkdir13(dirname30(absolutePath), { recursive: true });
|
|
20193
20621
|
await writeFile15(absolutePath, Buffer.from(data, "base64"));
|
|
20194
20622
|
return absolutePath;
|
|
20195
20623
|
},
|
|
@@ -20473,8 +20901,8 @@ var init_mobileBundleInspection = __esm(() => {
|
|
|
20473
20901
|
});
|
|
20474
20902
|
|
|
20475
20903
|
// src/mobile/releaseDoctor.ts
|
|
20476
|
-
import { access as access11, readFile as readFile19, readdir as
|
|
20477
|
-
import { dirname as
|
|
20904
|
+
import { access as access11, readFile as readFile19, readdir as readdir6 } from "fs/promises";
|
|
20905
|
+
import { dirname as dirname31, extname as extname8, join as join53, relative as relative26 } from "path";
|
|
20478
20906
|
var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, EXACT_VERSION_PATTERN, NOT_FOUND3 = -1, LOCK_FILES, MANUAL_REVIEW, pathExists6 = async (path) => {
|
|
20479
20907
|
try {
|
|
20480
20908
|
await access11(path);
|
|
@@ -20492,7 +20920,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
20492
20920
|
}, findHmrAsset = async (root) => {
|
|
20493
20921
|
if (!await pathExists6(root))
|
|
20494
20922
|
return;
|
|
20495
|
-
const entries = await
|
|
20923
|
+
const entries = await readdir6(root, { withFileTypes: true });
|
|
20496
20924
|
const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(join53(root, entry.name), entry.isDirectory(), entry.isFile())));
|
|
20497
20925
|
return matches.find((match) => match !== undefined);
|
|
20498
20926
|
}, pass = (id, detail, path) => ({ detail, id, path, status: "pass" }), fail5 = (id, detail, path, remediation) => ({
|
|
@@ -20507,7 +20935,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
20507
20935
|
path,
|
|
20508
20936
|
remediation,
|
|
20509
20937
|
status: "warn"
|
|
20510
|
-
}),
|
|
20938
|
+
}), isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJsonObject = async (path) => {
|
|
20511
20939
|
const value = JSON.parse(await readFile19(path, "utf8"));
|
|
20512
20940
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
20513
20941
|
throw new TypeError("JSON root must be an object.");
|
|
@@ -20638,7 +21066,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
20638
21066
|
const source = await readFile19(manifestPath, "utf8");
|
|
20639
21067
|
const cleartext = /android:usesCleartextTraffic=["']true["']/u.test(source);
|
|
20640
21068
|
const networkConfigName = source.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
|
|
20641
|
-
const networkConfigPath = networkConfigName ? join53(
|
|
21069
|
+
const networkConfigPath = networkConfigName ? join53(dirname31(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
|
|
20642
21070
|
const developmentTrustReference = /android:networkSecurityConfig=["']@xml\/absolutejs_dev_network_security["']/u.test(source);
|
|
20643
21071
|
const developmentTrustContents = networkConfigPath ? await readFile19(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
|
|
20644
21072
|
const developmentTrust = developmentTrustReference || developmentTrustContents;
|
|
@@ -20675,13 +21103,13 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
20675
21103
|
const path = join53(config.nativeProjectDirectory, "app.json");
|
|
20676
21104
|
try {
|
|
20677
21105
|
const root = await readJsonObject(path);
|
|
20678
|
-
if (!
|
|
21106
|
+
if (!isRecord15(root.expo))
|
|
20679
21107
|
throw new TypeError("Generated Expo application config is invalid.");
|
|
20680
21108
|
const { expo } = root;
|
|
20681
|
-
if (expo.name !== config.appName || config.platforms.includes("android") && (!
|
|
21109
|
+
if (expo.name !== config.appName || config.platforms.includes("android") && (!isRecord15(expo.android) || expo.android.package !== config.appId) || config.platforms.includes("ios") && (!isRecord15(expo.ios) || expo.ios.bundleIdentifier !== config.appId)) {
|
|
20682
21110
|
throw new TypeError("Generated Expo application identity does not match mobile config.");
|
|
20683
21111
|
}
|
|
20684
|
-
if (!
|
|
21112
|
+
if (!isRecord15(expo.runtimeVersion) || expo.runtimeVersion.policy !== "appVersion") {
|
|
20685
21113
|
throw new TypeError("Generated Expo runtimeVersion must follow the native app version.");
|
|
20686
21114
|
}
|
|
20687
21115
|
if (Array.isArray(expo.plugins) && expo.plugins.some((plugin) => typeof plugin === "string" && plugin.includes("withAbsoluteDevelopmentCa"))) {
|
|
@@ -20767,6 +21195,26 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
20767
21195
|
} catch (error) {
|
|
20768
21196
|
return fail5("android.deep-links", error instanceof Error ? error.message : "Android deep-link projection could not be validated.", manifestPath, "Run `absolute mobile sync android` and review the AbsoluteJS-owned deep-link region.");
|
|
20769
21197
|
}
|
|
21198
|
+
}, nativeUpdateWatchdogCheck = async (config, platform6) => {
|
|
21199
|
+
if (!config.updates)
|
|
21200
|
+
return;
|
|
21201
|
+
const timeout = String(config.updates.bootTimeoutMs);
|
|
21202
|
+
if (platform6 === "ios") {
|
|
21203
|
+
const path = join53(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
21204
|
+
const configPath2 = join53(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
|
|
21205
|
+
const [source, nativeConfig] = await Promise.all([
|
|
21206
|
+
readFile19(path, "utf8").catch(() => ""),
|
|
21207
|
+
readFile19(configPath2, "utf8").catch(() => "")
|
|
21208
|
+
]);
|
|
21209
|
+
return source.includes("AbsoluteMobileUpdateWatchdogPlugin") && source.includes("recoverInterruptedBoot()") && source.includes(`.milliseconds(${timeout})`) && source.includes("quarantinedReleases") && nativeConfig.includes("AbsoluteMobileUpdateWatchdogPlugin") ? pass("ios.update-watchdog", `The native update boot watchdog is projected with a ${timeout}ms deadline.`, path) : fail5("ios.update-watchdog", "The iOS update boot watchdog does not match mobile config.", path, "Run `absolute mobile sync ios` before building the release.");
|
|
21210
|
+
}
|
|
21211
|
+
const sourceRoot = join53(config.nativeProjectDirectory, "android/app/src/main");
|
|
21212
|
+
const sources = await sourceFiles(sourceRoot, new Set([".java", ".kt"]));
|
|
21213
|
+
const [activity, plugin] = await Promise.all([
|
|
21214
|
+
containsPattern(sources, /recoverInterruptedBoot\s*\(\s*this\s*\)/u),
|
|
21215
|
+
containsPattern(sources, new RegExp(`handler\\.postDelayed\\(deadline,\\s*${timeout}L\\)`, "u"))
|
|
21216
|
+
]);
|
|
21217
|
+
return activity && plugin ? pass("android.update-watchdog", `The native update boot watchdog is projected with a ${timeout}ms deadline.`, plugin) : fail5("android.update-watchdog", "The Android update boot watchdog does not match mobile config.", plugin ?? sourceRoot, "Run `absolute mobile sync android` before building the release.");
|
|
20770
21218
|
}, iosNativeSecurityCheck = async (iosRoot) => {
|
|
20771
21219
|
const applicationFiles = async (extensions) => (await sourceFiles(iosRoot, extensions)).filter((path) => !relative26(iosRoot, path).split(/[\\/]/u).some((part) => ["Pods", "DerivedData", "build"].includes(part)));
|
|
20772
21220
|
const entitlementPaths = await applicationFiles(new Set([".entitlements"]));
|
|
@@ -20944,9 +21392,10 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
20944
21392
|
contentSecurityPolicyCheck(config, "android", publicRoot),
|
|
20945
21393
|
androidNativeSecurityCheck(androidRoot),
|
|
20946
21394
|
androidExportedComponentsCheck(manifestPath),
|
|
20947
|
-
androidDeepLinkProjectionCheck(config, manifestPath)
|
|
21395
|
+
androidDeepLinkProjectionCheck(config, manifestPath),
|
|
21396
|
+
nativeUpdateWatchdogCheck(config, "android")
|
|
20948
21397
|
]);
|
|
20949
|
-
return checks.map((check2) => ({
|
|
21398
|
+
return checks.filter((check2) => check2 !== undefined).map((check2) => ({
|
|
20950
21399
|
...check2,
|
|
20951
21400
|
path: check2.path ? relative26(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
|
|
20952
21401
|
}));
|
|
@@ -21031,6 +21480,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
21031
21480
|
const checks = [
|
|
21032
21481
|
await journalReleaseCheck(journalPath, "ios")
|
|
21033
21482
|
];
|
|
21483
|
+
const updateWatchdog = config.updates ? await nativeUpdateWatchdogCheck(config, "ios") : undefined;
|
|
21034
21484
|
if (!config.iosVersion) {
|
|
21035
21485
|
checks.push(fail5("ios.marketing-version", "iOS has no explicit App Store marketing version.", projectRoot, "Add mobile.ios.version to absolutejs.config.ts, for example 1.0.0."));
|
|
21036
21486
|
} else {
|
|
@@ -21053,6 +21503,8 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
21053
21503
|
const hmrAsset = await findHmrAsset(publicRoot);
|
|
21054
21504
|
checks.push(hmrAsset ? fail5("ios.hmr-assets", "A packaged iOS asset contains the development HMR client.", hmrAsset, "Rebuild the production mobile bundle and run Capacitor sync again.") : pass("ios.hmr-assets", "Packaged iOS assets contain no development HMR markers.", publicRoot));
|
|
21055
21505
|
checks.push(await embeddedBundleReleaseCheck(config, projectRoot, "ios", publicRoot), await contentSecurityPolicyCheck(config, "ios", publicRoot), await iosNativeSecurityCheck(join53(config.nativeProjectDirectory, "ios")), await iosDeepLinkProjectionCheck(config, join53(config.nativeProjectDirectory, "ios")));
|
|
21506
|
+
if (updateWatchdog)
|
|
21507
|
+
checks.push(updateWatchdog);
|
|
21056
21508
|
return checks.map((check2) => ({
|
|
21057
21509
|
...check2,
|
|
21058
21510
|
path: check2.path ? relative26(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
|
|
@@ -21144,18 +21596,18 @@ import { createHash as createHash15 } from "crypto";
|
|
|
21144
21596
|
import {
|
|
21145
21597
|
access as access12,
|
|
21146
21598
|
copyFile as copyFile5,
|
|
21147
|
-
mkdir as
|
|
21599
|
+
mkdir as mkdir14,
|
|
21148
21600
|
mkdtemp as mkdtemp7,
|
|
21149
21601
|
readFile as readFile20,
|
|
21150
21602
|
realpath as realpath2,
|
|
21151
21603
|
rename as rename14,
|
|
21152
|
-
rm as
|
|
21604
|
+
rm as rm11,
|
|
21153
21605
|
stat as stat3,
|
|
21154
21606
|
writeFile as writeFile16
|
|
21155
21607
|
} from "fs/promises";
|
|
21156
|
-
import { dirname as
|
|
21157
|
-
var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1,
|
|
21158
|
-
if (!
|
|
21608
|
+
import { dirname as dirname32, isAbsolute as isAbsolute7, join as join54, relative as relative27, resolve as resolve40, sep as sep7 } from "path";
|
|
21609
|
+
var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest2 = (value) => {
|
|
21610
|
+
if (!isRecord16(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
|
|
21159
21611
|
throw new TypeError("Invalid embedded AbsoluteJS mobile manifest.");
|
|
21160
21612
|
}
|
|
21161
21613
|
return {
|
|
@@ -21251,8 +21703,8 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord15 = (value) => typeof value ==
|
|
|
21251
21703
|
}
|
|
21252
21704
|
return { artifactPath: destination, metadata: existing, releaseRoot };
|
|
21253
21705
|
}
|
|
21254
|
-
await
|
|
21255
|
-
const staging = await mkdtemp7(join54(
|
|
21706
|
+
await mkdir14(dirname32(releaseRoot), { recursive: true });
|
|
21707
|
+
const staging = await mkdtemp7(join54(dirname32(releaseRoot), ".android-stage-"));
|
|
21256
21708
|
try {
|
|
21257
21709
|
await copyFile5(artifactPath, join54(staging, artifactName));
|
|
21258
21710
|
const complete = {
|
|
@@ -21264,12 +21716,12 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord15 = (value) => typeof value ==
|
|
|
21264
21716
|
await rename14(staging, releaseRoot);
|
|
21265
21717
|
return { artifactPath: destination, metadata: complete, releaseRoot };
|
|
21266
21718
|
} finally {
|
|
21267
|
-
await
|
|
21719
|
+
await rm11(staging, { force: true, recursive: true }).catch(() => {
|
|
21268
21720
|
return;
|
|
21269
21721
|
});
|
|
21270
21722
|
}
|
|
21271
21723
|
}, requireManifestIdentity = (value, expected) => {
|
|
21272
|
-
if (!
|
|
21724
|
+
if (!isRecord16(value)) {
|
|
21273
21725
|
throw new TypeError("Existing Android release metadata is invalid.");
|
|
21274
21726
|
}
|
|
21275
21727
|
const { artifact } = value;
|
|
@@ -21464,7 +21916,7 @@ var init_iosConformance = __esm(() => {
|
|
|
21464
21916
|
});
|
|
21465
21917
|
|
|
21466
21918
|
// src/mobile/nativeTestReport.ts
|
|
21467
|
-
import { mkdir as
|
|
21919
|
+
import { mkdir as mkdir15, readFile as readFile22, writeFile as writeFile17 } from "fs/promises";
|
|
21468
21920
|
import { join as join55 } from "path";
|
|
21469
21921
|
var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sanitizeNativeReportText = (value) => value.replace(nativeCredentialPattern, "[REDACTED]").replace(bearerPattern, "Bearer [REDACTED]").replace(secretPattern, "$1$2[REDACTED]").replace(coordinatePattern, "$1$2[REDACTED]").replace(/(https?:\/\/[^\s?#]+)[?#][^\s]*/giu, "$1?[REDACTED]"), markdownCell = (value) => sanitizeNativeReportText(value).replaceAll("|", "\\|").replaceAll(`
|
|
21470
21922
|
`, "<br>"), createAbsoluteNativeAutomatedChecks = (run) => {
|
|
@@ -21603,7 +22055,7 @@ Replace each \`NOT_RUN\` with \`PASS\`, \`FAIL\`, or \`SKIPPED\` after completin
|
|
|
21603
22055
|
${table(report.manualChecks)}
|
|
21604
22056
|
`;
|
|
21605
22057
|
}, writeAbsoluteNativeTestReport = async (directory, report) => {
|
|
21606
|
-
await
|
|
22058
|
+
await mkdir15(directory, { recursive: true });
|
|
21607
22059
|
const jsonPath = join55(directory, "report.json");
|
|
21608
22060
|
const markdownPath = join55(directory, "report.md");
|
|
21609
22061
|
await Promise.all([
|
|
@@ -21893,7 +22345,7 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
|
21893
22345
|
throw new TypeError("Google Play publisher returned an invalid Android versionCode.");
|
|
21894
22346
|
}
|
|
21895
22347
|
return versionCode;
|
|
21896
|
-
},
|
|
22348
|
+
}, isRecord17 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPublisher = (value) => isRecord17(value) && typeof value.publish === "function", publisherModulePath = (projectRoot, requested) => {
|
|
21897
22349
|
const root = resolve41(projectRoot);
|
|
21898
22350
|
const path = resolve41(root, requested);
|
|
21899
22351
|
const projectRelative = relative28(root, path);
|
|
@@ -21907,7 +22359,7 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
|
21907
22359
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
21908
22360
|
});
|
|
21909
22361
|
const loaded = await import(pathToFileURL2(modulePath).href);
|
|
21910
|
-
const publisher =
|
|
22362
|
+
const publisher = isRecord17(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
21911
22363
|
if (!isPublisher(publisher)) {
|
|
21912
22364
|
throw new TypeError("Native release registry module must default-export a registry with publish(options).");
|
|
21913
22365
|
}
|
|
@@ -22113,8 +22565,8 @@ var init_mobileInspect = __esm(() => {
|
|
|
22113
22565
|
|
|
22114
22566
|
// src/mobile/ciWorkflow.ts
|
|
22115
22567
|
import { existsSync as existsSync43 } from "fs";
|
|
22116
|
-
import { access as access15, mkdir as
|
|
22117
|
-
import { dirname as
|
|
22568
|
+
import { access as access15, mkdir as mkdir16, readFile as readFile24, writeFile as writeFile18 } from "fs/promises";
|
|
22569
|
+
import { dirname as dirname33, extname as extname9, relative as relative30, resolve as resolve43, sep as sep9 } from "path";
|
|
22118
22570
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1, SECRET_NAME_PATTERN, CI_ENV_INDENTATION = 6, RESERVED_SECRET_NAMES, exists4 = async (path) => {
|
|
22119
22571
|
try {
|
|
22120
22572
|
await access15(path);
|
|
@@ -22531,7 +22983,7 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
22531
22983
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
22532
22984
|
throw new TypeError(`${relative30(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
22533
22985
|
if (previous !== generated.workflow) {
|
|
22534
|
-
await
|
|
22986
|
+
await mkdir16(dirname33(path), { recursive: true });
|
|
22535
22987
|
await writeFile18(path, generated.workflow);
|
|
22536
22988
|
}
|
|
22537
22989
|
return {
|
|
@@ -22593,18 +23045,18 @@ var init_ciWorkflow = __esm(() => {
|
|
|
22593
23045
|
import { createHash as createHash16, sign as sign2, verify as verify2 } from "crypto";
|
|
22594
23046
|
import {
|
|
22595
23047
|
cp as cp4,
|
|
22596
|
-
mkdir as
|
|
23048
|
+
mkdir as mkdir17,
|
|
22597
23049
|
mkdtemp as mkdtemp8,
|
|
22598
|
-
readdir as
|
|
23050
|
+
readdir as readdir7,
|
|
22599
23051
|
readFile as readFile25,
|
|
22600
23052
|
rename as rename15,
|
|
22601
|
-
rm as
|
|
23053
|
+
rm as rm12,
|
|
22602
23054
|
stat as stat5,
|
|
22603
23055
|
writeFile as writeFile19
|
|
22604
23056
|
} from "fs/promises";
|
|
22605
|
-
import { dirname as
|
|
23057
|
+
import { dirname as dirname34, join as join57, relative as relative31, resolve as resolve44 } from "path";
|
|
22606
23058
|
var UPDATE_MANIFEST_FILE = "update.json", UPDATE_FILES_DIRECTORY = "files", sha2562 = (value) => createHash16("sha256").update(value).digest("hex"), listFiles = async (root, directory = root) => {
|
|
22607
|
-
const entries = await
|
|
23059
|
+
const entries = await readdir7(directory, { withFileTypes: true });
|
|
22608
23060
|
const paths = await Promise.all(entries.map(async (entry) => {
|
|
22609
23061
|
const path = join57(directory, entry.name);
|
|
22610
23062
|
if (entry.isDirectory())
|
|
@@ -22651,7 +23103,7 @@ var UPDATE_MANIFEST_FILE = "update.json", UPDATE_FILES_DIRECTORY = "files", sha2
|
|
|
22651
23103
|
value: signature.toString("base64")
|
|
22652
23104
|
}
|
|
22653
23105
|
});
|
|
22654
|
-
await
|
|
23106
|
+
await mkdir17(outputRoot, { recursive: true });
|
|
22655
23107
|
const outputDirectory = join57(outputRoot, manifest.releaseId);
|
|
22656
23108
|
const staging = await mkdtemp8(join57(outputRoot, ".stage-"));
|
|
22657
23109
|
try {
|
|
@@ -22663,7 +23115,7 @@ var UPDATE_MANIFEST_FILE = "update.json", UPDATE_FILES_DIRECTORY = "files", sha2
|
|
|
22663
23115
|
`);
|
|
22664
23116
|
await rename15(staging, outputDirectory);
|
|
22665
23117
|
} catch (error) {
|
|
22666
|
-
await
|
|
23118
|
+
await rm12(staging, { force: true, recursive: true });
|
|
22667
23119
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
|
|
22668
23120
|
throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
|
|
22669
23121
|
throw error;
|
|
@@ -22748,18 +23200,18 @@ var exports_mobile = {};
|
|
|
22748
23200
|
__export(exports_mobile, {
|
|
22749
23201
|
runMobile: () => runMobile
|
|
22750
23202
|
});
|
|
22751
|
-
import { access as access17, mkdir as
|
|
23203
|
+
import { access as access17, mkdir as mkdir18, readFile as readFile26, writeFile as writeFile20 } from "fs/promises";
|
|
22752
23204
|
import { createPublicKey as createPublicKey3 } from "crypto";
|
|
22753
23205
|
import { join as join58, relative as relative33, resolve as resolve46 } from "path";
|
|
22754
23206
|
import { createInterface } from "readline/promises";
|
|
22755
|
-
var NOT_FOUND4 = -1,
|
|
23207
|
+
var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
|
|
22756
23208
|
const manifest = JSON.parse(await readFile26(join58(projectRoot, "package.json"), "utf8"));
|
|
22757
|
-
if (!
|
|
23209
|
+
if (!isRecord18(manifest))
|
|
22758
23210
|
throw new TypeError("Application package.json must contain an object.");
|
|
22759
23211
|
const names = new Set;
|
|
22760
23212
|
for (const field of ["dependencies", "devDependencies"]) {
|
|
22761
23213
|
const dependencies = Reflect.get(manifest, field);
|
|
22762
|
-
if (
|
|
23214
|
+
if (isRecord18(dependencies))
|
|
22763
23215
|
for (const name of Object.keys(dependencies))
|
|
22764
23216
|
names.add(name);
|
|
22765
23217
|
}
|
|
@@ -22767,7 +23219,7 @@ var NOT_FOUND4 = -1, isRecord17 = (value) => typeof value === "object" && value
|
|
|
22767
23219
|
}, resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
22768
23220
|
try {
|
|
22769
23221
|
const manifest = JSON.parse(await readFile26(join58(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
22770
|
-
return
|
|
23222
|
+
return isRecord18(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
22771
23223
|
} catch {
|
|
22772
23224
|
return;
|
|
22773
23225
|
}
|
|
@@ -23216,7 +23668,7 @@ Mobile release security and compliance checks failed.`);
|
|
|
23216
23668
|
try {
|
|
23217
23669
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath2, { prepareOnly: true });
|
|
23218
23670
|
const embedded = JSON.parse(await readFile26(join58(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
23219
|
-
const runtimeFingerprint =
|
|
23671
|
+
const runtimeFingerprint = isRecord18(embedded) ? embedded.nativeRuntime : undefined;
|
|
23220
23672
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
23221
23673
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
23222
23674
|
const privateKey = await readFile26(resolve46(projectRoot, signingKeyPath));
|
|
@@ -23482,6 +23934,7 @@ Mobile release security and compliance checks failed.`);
|
|
|
23482
23934
|
"android"
|
|
23483
23935
|
]);
|
|
23484
23936
|
await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["android"]);
|
|
23937
|
+
await applyAbsoluteNativeUpdates(mobile, ["android"]);
|
|
23485
23938
|
}, prepareAndroidReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoAndroidReleaseProject(mobile, projectRoot, args) : prepareCapacitorAndroidReleaseProject(mobile, projectRoot), buildAndroid = async (args, prepareVersionCode) => {
|
|
23486
23939
|
const configPath2 = valueAfter(args, "--config");
|
|
23487
23940
|
const { mobile, projectRoot } = await loadMobile(configPath2);
|
|
@@ -23927,7 +24380,7 @@ Emulator setup verification:`);
|
|
|
23927
24380
|
timeoutMs
|
|
23928
24381
|
});
|
|
23929
24382
|
}, writeAndroidFailureArtifacts = async (options) => {
|
|
23930
|
-
await
|
|
24383
|
+
await mkdir18(options.artifactRoot, { recursive: true });
|
|
23931
24384
|
const screenshot = options.session ? await options.session.screenshot(join58(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
23932
24385
|
return;
|
|
23933
24386
|
}) : undefined;
|
|
@@ -24088,7 +24541,7 @@ Emulator setup verification:`);
|
|
|
24088
24541
|
return;
|
|
24089
24542
|
});
|
|
24090
24543
|
const status2 = response?.ok ? await response.json().catch(() => null) : null;
|
|
24091
|
-
const targets =
|
|
24544
|
+
const targets = isRecord18(status2) && isRecord18(status2.connectedTargets) ? status2.connectedTargets : undefined;
|
|
24092
24545
|
if (targets && typeof targets["capacitor-ios"] === "number" && targets["capacitor-ios"] > 0)
|
|
24093
24546
|
return;
|
|
24094
24547
|
await Bun.sleep(100);
|
|
@@ -24185,7 +24638,7 @@ Emulator setup verification:`);
|
|
|
24185
24638
|
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
24186
24639
|
return result;
|
|
24187
24640
|
}, writeIosFailureArtifacts = async (options) => {
|
|
24188
|
-
await
|
|
24641
|
+
await mkdir18(options.artifactRoot, { recursive: true });
|
|
24189
24642
|
const screenshot = join58(options.artifactRoot, "ios-failure.png");
|
|
24190
24643
|
const screenshotResult = captureCommand4([
|
|
24191
24644
|
options.xcrun,
|
|
@@ -24441,7 +24894,7 @@ Emulator setup verification:`);
|
|
|
24441
24894
|
mobile.appId
|
|
24442
24895
|
], "iOS app launch");
|
|
24443
24896
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
24444
|
-
await
|
|
24897
|
+
await mkdir18(artifactRoot, { recursive: true });
|
|
24445
24898
|
const screenshot = join58(artifactRoot, "ios-simulator.png");
|
|
24446
24899
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
24447
24900
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
@@ -24680,7 +25133,7 @@ __export(exports_typecheck, {
|
|
|
24680
25133
|
});
|
|
24681
25134
|
import { resolve as resolve47, join as join59 } from "path";
|
|
24682
25135
|
import { existsSync as existsSync44, readFileSync as readFileSync40 } from "fs";
|
|
24683
|
-
import { mkdir as
|
|
25136
|
+
import { mkdir as mkdir19, writeFile as writeFile21 } from "fs/promises";
|
|
24684
25137
|
var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve47(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
|
|
24685
25138
|
if (!existsSync44(resolveConfigPath(configPath2))) {
|
|
24686
25139
|
const defaultService = {};
|
|
@@ -24892,7 +25345,7 @@ Found ${errorCount} error${suffix}.`;
|
|
|
24892
25345
|
...new Set(targets.map((config) => config.angularDirectory).filter((dir) => typeof dir === "string" && dir.length > 0))
|
|
24893
25346
|
];
|
|
24894
25347
|
const cacheDir = ".absolutejs";
|
|
24895
|
-
await
|
|
25348
|
+
await mkdir19(cacheDir, { recursive: true });
|
|
24896
25349
|
const checks = [];
|
|
24897
25350
|
checks.push(hasVue ? buildVueTscCheck(cacheDir) : buildTscCheck(cacheDir));
|
|
24898
25351
|
for (const svelteDir of hasSvelte ? svelteDirs : []) {
|