@absolutejs/absolute 0.20.0-beta.101 → 0.20.0-beta.103
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/CHANGELOG.md +7 -0
- package/changelog.json +11 -0
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/{compile-9y36k8z1.js → compile-4xbpm6hf.js} +2 -2
- package/dist/cli/{dev-tn9yfehm.js → dev-m4r4zs2k.js} +2 -2
- package/dist/cli/{expoDevController-mxd3h52y.js → expoDevController-zz516pdd.js} +1 -1
- package/dist/cli/{expoProject-ceey4cfg.js → expoProject-780thyfd.js} +1 -1
- package/dist/cli/{index-kr23b0fp.js → index-28nxv57v.js} +2 -0
- package/dist/cli/{index-0bcf7pdw.js → index-6ff78meg.js} +1 -1
- package/dist/cli/{index-qd6mqgsc.js → index-d5fr1xp3.js} +137 -27
- package/dist/cli/{index-fdycyj4v.js → index-rvpd4w9t.js} +1 -1
- package/dist/cli/index.js +5 -5
- package/dist/cli/{mobile-b1mh7ms3.js → mobile-6xe6zysv.js} +787 -221
- package/dist/cli/{start-10dfrjvd.js → start-nmyy64tr.js} +3 -3
- package/dist/mobile/index.js +1168 -755
- package/dist/mobile/index.js.map +8 -7
- package/dist/mobile/remoteMacAgentEntry.js +262 -157
- package/dist/src/mobile/androidReleaseAcceptance.d.ts +65 -0
- package/dist/src/mobile/androidTestReport.d.ts +1 -0
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/nativeTestReport.d.ts +15 -2
- package/package.json +1 -1
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
} from "./index-zh6hhrwy.js";
|
|
8
8
|
import {
|
|
9
9
|
start
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-rvpd4w9t.js";
|
|
11
11
|
import {
|
|
12
12
|
ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
|
|
13
13
|
ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
|
|
14
14
|
resolveAbsoluteMobileRoute
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-6ff78meg.js";
|
|
16
16
|
import"./index-bzs19w3r.js";
|
|
17
17
|
import"./index-s2tazax7.js";
|
|
18
18
|
import {
|
|
@@ -67,10 +67,10 @@ import {
|
|
|
67
67
|
syncAbsoluteExpoWebAssets,
|
|
68
68
|
unsignedAbsoluteMobileUpdate,
|
|
69
69
|
writeAbsoluteExpoProject
|
|
70
|
-
} from "./index-
|
|
70
|
+
} from "./index-d5fr1xp3.js";
|
|
71
71
|
import {
|
|
72
72
|
buildAbsoluteExpoAndroidWslBundle
|
|
73
|
-
} from "./index-
|
|
73
|
+
} from "./index-28nxv57v.js";
|
|
74
74
|
import {
|
|
75
75
|
normalizeAbsoluteIosDeviceIdentifier
|
|
76
76
|
} from "./index-j66p8hn5.js";
|
|
@@ -88,6 +88,7 @@ import {
|
|
|
88
88
|
writeAbsoluteCapacitorConfig
|
|
89
89
|
} from "./index-28pbrd3q.js";
|
|
90
90
|
import {
|
|
91
|
+
ABSOLUTE_ANDROID_AVD_NAME,
|
|
91
92
|
absoluteManagedAndroidSdkRoot,
|
|
92
93
|
detectAbsoluteMobileHost,
|
|
93
94
|
inspectAbsoluteMobileToolchain
|
|
@@ -4411,9 +4412,9 @@ var require_sha256 = __commonJS(function(exports, module) {
|
|
|
4411
4412
|
var forge = require_forge();
|
|
4412
4413
|
require_md();
|
|
4413
4414
|
require_util();
|
|
4414
|
-
var
|
|
4415
|
-
forge.md.sha256 = forge.md.algorithms.sha256 =
|
|
4416
|
-
|
|
4415
|
+
var sha2563 = module.exports = forge.sha256 = forge.sha256 || {};
|
|
4416
|
+
forge.md.sha256 = forge.md.algorithms.sha256 = sha2563;
|
|
4417
|
+
sha2563.create = function() {
|
|
4417
4418
|
if (!_initialized) {
|
|
4418
4419
|
_init();
|
|
4419
4420
|
}
|
|
@@ -16775,15 +16776,15 @@ var require_main = __commonJS(function(exports) {
|
|
|
16775
16776
|
|
|
16776
16777
|
// src/cli/scripts/mobile.ts
|
|
16777
16778
|
import {
|
|
16778
|
-
access as
|
|
16779
|
-
mkdir as
|
|
16780
|
-
mkdtemp as
|
|
16781
|
-
readFile as
|
|
16782
|
-
rm as
|
|
16783
|
-
writeFile as
|
|
16779
|
+
access as access13,
|
|
16780
|
+
mkdir as mkdir13,
|
|
16781
|
+
mkdtemp as mkdtemp4,
|
|
16782
|
+
readFile as readFile17,
|
|
16783
|
+
rm as rm7,
|
|
16784
|
+
writeFile as writeFile15
|
|
16784
16785
|
} from "fs/promises";
|
|
16785
16786
|
import { createPublicKey as createPublicKey2 } from "crypto";
|
|
16786
|
-
import { join as
|
|
16787
|
+
import { join as join14, relative as relative12, resolve as resolve13 } from "path";
|
|
16787
16788
|
import { createInterface } from "readline/promises";
|
|
16788
16789
|
|
|
16789
16790
|
// src/mobile/nativeDeepLinks.ts
|
|
@@ -19838,21 +19839,23 @@ var expoEmbeddedAssetsCheck = async (config) => {
|
|
|
19838
19839
|
cwd: assetsRoot,
|
|
19839
19840
|
onlyFiles: true
|
|
19840
19841
|
}))).sort();
|
|
19841
|
-
if (sourceFiles.length === 0 ||
|
|
19842
|
+
if (sourceFiles.length === 0 || embeddedFiles.length !== 1)
|
|
19842
19843
|
throw new TypeError("Generated Expo asset count does not match the prepared mobile bundle.");
|
|
19843
|
-
const
|
|
19844
|
-
|
|
19845
|
-
|
|
19846
|
-
|
|
19847
|
-
|
|
19848
|
-
|
|
19849
|
-
|
|
19850
|
-
|
|
19851
|
-
|
|
19852
|
-
|
|
19853
|
-
|
|
19844
|
+
const contents = await Promise.all(sourceFiles.map((path) => readFile8(join7(config.bundleDirectory, path))));
|
|
19845
|
+
let offset = 0;
|
|
19846
|
+
const entries = sourceFiles.map((path, index) => {
|
|
19847
|
+
const entry = {
|
|
19848
|
+
length: contents[index]?.byteLength ?? 0,
|
|
19849
|
+
offset,
|
|
19850
|
+
path: path.replaceAll("\\", "/")
|
|
19851
|
+
};
|
|
19852
|
+
offset += entry.length;
|
|
19853
|
+
return entry;
|
|
19854
|
+
});
|
|
19855
|
+
const archive = await readFile8(join7(assetsRoot, embeddedFiles[0] ?? ""));
|
|
19856
|
+
if (!archive.equals(Buffer.concat(contents)) || entries.some((entry) => !source.includes(JSON.stringify(entry))))
|
|
19854
19857
|
throw new TypeError("Generated Expo asset bytes differ from the prepared mobile bundle.");
|
|
19855
|
-
return pass("expo.bundle-projection", `Expo embeds the complete signed mobile bundle as ${
|
|
19858
|
+
return pass("expo.bundle-projection", `Expo embeds the complete signed mobile bundle as one opaque archive containing ${sourceFiles.length} file(s).`, generated);
|
|
19856
19859
|
} catch (error) {
|
|
19857
19860
|
return fail("expo.bundle-projection", error instanceof Error ? error.message : "Generated Expo assets could not be validated.", generated, "Run `absolute mobile build <platform>` to regenerate and verify the production asset projection.");
|
|
19858
19861
|
}
|
|
@@ -20588,6 +20591,343 @@ var buildAbsoluteAndroidRelease = async (options) => {
|
|
|
20588
20591
|
return installRelease(artifactPath, metadata, safeOutputDirectory(projectRoot, options.outputDirectory));
|
|
20589
20592
|
};
|
|
20590
20593
|
|
|
20594
|
+
// src/mobile/androidReleaseAcceptance.ts
|
|
20595
|
+
import { createHash as createHash3 } from "crypto";
|
|
20596
|
+
import {
|
|
20597
|
+
access as access6,
|
|
20598
|
+
mkdir as mkdir7,
|
|
20599
|
+
mkdtemp as mkdtemp2,
|
|
20600
|
+
readFile as readFile10,
|
|
20601
|
+
rename as rename8,
|
|
20602
|
+
rm as rm5,
|
|
20603
|
+
stat as stat3,
|
|
20604
|
+
writeFile as writeFile9
|
|
20605
|
+
} from "fs/promises";
|
|
20606
|
+
import { homedir } from "os";
|
|
20607
|
+
import {
|
|
20608
|
+
basename,
|
|
20609
|
+
dirname as dirname7,
|
|
20610
|
+
isAbsolute as isAbsolute3,
|
|
20611
|
+
join as join9,
|
|
20612
|
+
relative as relative5,
|
|
20613
|
+
resolve as resolve6,
|
|
20614
|
+
sep as sep3
|
|
20615
|
+
} from "path";
|
|
20616
|
+
|
|
20617
|
+
// src/mobile/androidUpgradeConformance.ts
|
|
20618
|
+
var captureCommand2 = async (command) => {
|
|
20619
|
+
const process2 = Bun.spawn(command, { stderr: "pipe", stdout: "pipe" });
|
|
20620
|
+
const [exitCode, stderr, stdout] = await Promise.all([
|
|
20621
|
+
process2.exited,
|
|
20622
|
+
new Response(process2.stderr).text(),
|
|
20623
|
+
new Response(process2.stdout).text()
|
|
20624
|
+
]);
|
|
20625
|
+
return { exitCode, stderr, stdout };
|
|
20626
|
+
};
|
|
20627
|
+
var field = (output, name) => new RegExp(`^\\s*${name}=([^\\r\\n]+)$`, "mu").exec(output)?.[1]?.trim();
|
|
20628
|
+
var inspectAbsoluteAndroidInstalledApp = async (adb, serial, appId, run = captureCommand2) => {
|
|
20629
|
+
const result = await run([
|
|
20630
|
+
adb,
|
|
20631
|
+
"-s",
|
|
20632
|
+
serial,
|
|
20633
|
+
"shell",
|
|
20634
|
+
"dumpsys",
|
|
20635
|
+
"package",
|
|
20636
|
+
appId
|
|
20637
|
+
]);
|
|
20638
|
+
if (result.exitCode !== 0)
|
|
20639
|
+
throw new Error(`Could not inspect installed Android app ${appId}: ${result.stderr.trim() || result.stdout.trim()}`);
|
|
20640
|
+
const installed = parseAbsoluteAndroidInstalledApp(appId, result.stdout);
|
|
20641
|
+
if (!installed)
|
|
20642
|
+
throw new Error(`Android app ${appId} is not installed on ${serial}.`);
|
|
20643
|
+
return installed;
|
|
20644
|
+
};
|
|
20645
|
+
var parseAbsoluteAndroidInstalledApp = (appId, output) => {
|
|
20646
|
+
const packageName = /^\s*Package \[([^\]]+)\]/mu.exec(output)?.[1];
|
|
20647
|
+
if (packageName !== appId && !output.includes(`package:${appId}`))
|
|
20648
|
+
return null;
|
|
20649
|
+
const versionCodeText = /^\s*versionCode=(\d+)/mu.exec(output)?.[1];
|
|
20650
|
+
return {
|
|
20651
|
+
appId,
|
|
20652
|
+
...field(output, "dataDir") ? { dataDirectory: field(output, "dataDir") } : {},
|
|
20653
|
+
...field(output, "firstInstallTime") ? { firstInstallTime: field(output, "firstInstallTime") } : {},
|
|
20654
|
+
...field(output, "lastUpdateTime") ? { lastUpdateTime: field(output, "lastUpdateTime") } : {},
|
|
20655
|
+
...field(output, "userId") || field(output, "appId") ? { uid: field(output, "userId") ?? field(output, "appId") } : {},
|
|
20656
|
+
...versionCodeText ? { versionCode: Number(versionCodeText) } : {},
|
|
20657
|
+
...field(output, "versionName") ? { versionName: field(output, "versionName") } : {}
|
|
20658
|
+
};
|
|
20659
|
+
};
|
|
20660
|
+
|
|
20661
|
+
// src/mobile/androidReleaseAcceptance.ts
|
|
20662
|
+
var ABSOLUTE_BUNDLETOOL_VERSION = "1.18.3";
|
|
20663
|
+
var ABSOLUTE_BUNDLETOOL_SHA256 = "a099cfa1543f55593bc2ed16a70a7c67fe54b1747bb7301f37fdfd6d91028e29";
|
|
20664
|
+
var ABSOLUTE_BUNDLETOOL_URL = `https://github.com/google/bundletool/releases/download/${ABSOLUTE_BUNDLETOOL_VERSION}/bundletool-all-${ABSOLUTE_BUNDLETOOL_VERSION}.jar`;
|
|
20665
|
+
var EMBEDDED_READY_MARKER = "Expo embedded web content ready";
|
|
20666
|
+
var RELEASE_READY_POLL_MS = 250;
|
|
20667
|
+
var RELEASE_READY_TIMEOUT_MS = 30000;
|
|
20668
|
+
var RELEASE_STABILITY_MS = 2000;
|
|
20669
|
+
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20670
|
+
var exists2 = async (path) => {
|
|
20671
|
+
try {
|
|
20672
|
+
await access6(path);
|
|
20673
|
+
return true;
|
|
20674
|
+
} catch {
|
|
20675
|
+
return false;
|
|
20676
|
+
}
|
|
20677
|
+
};
|
|
20678
|
+
var sha256 = async (path) => createHash3("sha256").update(await readFile10(path)).digest("hex");
|
|
20679
|
+
var inside = (root, candidate) => {
|
|
20680
|
+
const path = relative5(resolve6(root), resolve6(candidate));
|
|
20681
|
+
return path === "" || !isAbsolute3(path) && path !== ".." && !path.startsWith(`..${sep3}`);
|
|
20682
|
+
};
|
|
20683
|
+
var requireString2 = (value, name) => {
|
|
20684
|
+
if (typeof value !== "string" || !value)
|
|
20685
|
+
throw new TypeError(`Android release ${name} is invalid.`);
|
|
20686
|
+
return value;
|
|
20687
|
+
};
|
|
20688
|
+
var parseMetadata = (value) => {
|
|
20689
|
+
if (!isRecord5(value))
|
|
20690
|
+
throw new TypeError("Android release metadata must contain an object.");
|
|
20691
|
+
const { bytes } = value;
|
|
20692
|
+
const { versionCode } = value;
|
|
20693
|
+
if (!Number.isSafeInteger(bytes) || Number(bytes) < 1)
|
|
20694
|
+
throw new TypeError("Android release bytes is invalid.");
|
|
20695
|
+
if (versionCode !== undefined && (!Number.isSafeInteger(versionCode) || Number(versionCode) < 1))
|
|
20696
|
+
throw new TypeError("Android release versionCode is invalid.");
|
|
20697
|
+
if (value.format !== 1 || value.platform !== "android" || value.type !== "aab" || value.engine !== "capacitor" && value.engine !== "expo" || typeof value.signed !== "boolean")
|
|
20698
|
+
throw new TypeError("Android release metadata contract is invalid.");
|
|
20699
|
+
const metadata = {
|
|
20700
|
+
appBuild: requireString2(value.appBuild, "appBuild"),
|
|
20701
|
+
appId: requireString2(value.appId, "appId"),
|
|
20702
|
+
artifact: requireString2(value.artifact, "artifact"),
|
|
20703
|
+
bytes: Number(bytes),
|
|
20704
|
+
engine: value.engine,
|
|
20705
|
+
format: 1,
|
|
20706
|
+
platform: "android",
|
|
20707
|
+
releaseId: requireString2(value.releaseId, "releaseId"),
|
|
20708
|
+
runtime: requireString2(value.runtime, "runtime"),
|
|
20709
|
+
sha256: requireString2(value.sha256, "sha256"),
|
|
20710
|
+
signed: value.signed,
|
|
20711
|
+
type: "aab",
|
|
20712
|
+
...versionCode === undefined ? {} : { versionCode: Number(versionCode) }
|
|
20713
|
+
};
|
|
20714
|
+
if (!/^[a-f0-9]{64}$/u.test(metadata.sha256))
|
|
20715
|
+
throw new TypeError("Android release sha256 is invalid.");
|
|
20716
|
+
if (metadata.releaseId !== `amobile_android_${metadata.sha256}`)
|
|
20717
|
+
throw new TypeError("Android release ID does not match its artifact digest.");
|
|
20718
|
+
return metadata;
|
|
20719
|
+
};
|
|
20720
|
+
var absoluteBundletoolPath = (root = process.env.ABSOLUTE_BUNDLETOOL_ROOT ?? join9(homedir(), ".absolutejs", "tools", "bundletool")) => join9(root, ABSOLUTE_BUNDLETOOL_VERSION, "bundletool.jar");
|
|
20721
|
+
var inspectAbsoluteBundletool = async (root) => {
|
|
20722
|
+
const path = absoluteBundletoolPath(root);
|
|
20723
|
+
if (!await exists2(path))
|
|
20724
|
+
return { path, ready: false };
|
|
20725
|
+
if (await sha256(path) !== ABSOLUTE_BUNDLETOOL_SHA256)
|
|
20726
|
+
throw new TypeError(`Managed Bundletool ${ABSOLUTE_BUNDLETOOL_VERSION} failed checksum verification. Remove ${path} and rerun the command.`);
|
|
20727
|
+
return { path, ready: true };
|
|
20728
|
+
};
|
|
20729
|
+
var readAbsoluteAndroidRelease = async (projectRoot, requested) => {
|
|
20730
|
+
const root = resolve6(projectRoot);
|
|
20731
|
+
const selected = resolve6(root, requested);
|
|
20732
|
+
if (!inside(root, selected))
|
|
20733
|
+
throw new TypeError("mobile test android --release must remain inside the project.");
|
|
20734
|
+
const metadataPath = basename(selected) === "release.json" ? selected : join9(selected, "release.json");
|
|
20735
|
+
const releaseRoot = dirname7(metadataPath);
|
|
20736
|
+
const metadata = parseMetadata(JSON.parse(await readFile10(metadataPath, "utf8")));
|
|
20737
|
+
const artifactPath = resolve6(releaseRoot, metadata.artifact);
|
|
20738
|
+
if (!inside(releaseRoot, artifactPath) || basename(artifactPath) !== metadata.artifact)
|
|
20739
|
+
throw new TypeError("Android release artifact path is invalid.");
|
|
20740
|
+
const [artifactStat, artifactSha256] = await Promise.all([
|
|
20741
|
+
stat3(artifactPath),
|
|
20742
|
+
sha256(artifactPath)
|
|
20743
|
+
]);
|
|
20744
|
+
if (artifactStat.size !== metadata.bytes || artifactSha256 !== metadata.sha256)
|
|
20745
|
+
throw new TypeError(`Immutable Android release ${metadata.releaseId} is missing or modified.`);
|
|
20746
|
+
return { artifactPath, metadata, metadataPath, releaseRoot };
|
|
20747
|
+
};
|
|
20748
|
+
var defaultDownload = async (url) => {
|
|
20749
|
+
const response = await fetch(url);
|
|
20750
|
+
if (!response.ok)
|
|
20751
|
+
throw new Error(`Bundletool download failed (${response.status}).`);
|
|
20752
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
20753
|
+
};
|
|
20754
|
+
var ensureAbsoluteBundletool = async (options) => {
|
|
20755
|
+
const inspected = await inspectAbsoluteBundletool(options.root);
|
|
20756
|
+
if (inspected.ready)
|
|
20757
|
+
return inspected.path;
|
|
20758
|
+
if (!options.approved)
|
|
20759
|
+
throw new TypeError(`Bundletool ${ABSOLUTE_BUNDLETOOL_VERSION} is required. Rerun with --yes to download the checksum-pinned official release.`);
|
|
20760
|
+
const bytes = await (options.download ?? defaultDownload)(ABSOLUTE_BUNDLETOOL_URL);
|
|
20761
|
+
const digest = createHash3("sha256").update(bytes).digest("hex");
|
|
20762
|
+
if (digest !== ABSOLUTE_BUNDLETOOL_SHA256)
|
|
20763
|
+
throw new TypeError("Downloaded Bundletool failed checksum verification.");
|
|
20764
|
+
await mkdir7(dirname7(inspected.path), { recursive: true });
|
|
20765
|
+
const staging = await mkdtemp2(join9(dirname7(inspected.path), ".install-"));
|
|
20766
|
+
const candidate = join9(staging, "bundletool.jar");
|
|
20767
|
+
try {
|
|
20768
|
+
await writeFile9(candidate, bytes, { flag: "wx" });
|
|
20769
|
+
await rename8(candidate, inspected.path).catch(async (error) => {
|
|
20770
|
+
if (await exists2(inspected.path))
|
|
20771
|
+
return;
|
|
20772
|
+
throw error;
|
|
20773
|
+
});
|
|
20774
|
+
} finally {
|
|
20775
|
+
await rm5(staging, { force: true, recursive: true });
|
|
20776
|
+
}
|
|
20777
|
+
if (await sha256(inspected.path) !== ABSOLUTE_BUNDLETOOL_SHA256)
|
|
20778
|
+
throw new TypeError("Installed Bundletool failed checksum verification.");
|
|
20779
|
+
return inspected.path;
|
|
20780
|
+
};
|
|
20781
|
+
var defaultRun = async (command) => {
|
|
20782
|
+
const process2 = Bun.spawn(command, {
|
|
20783
|
+
stderr: "pipe",
|
|
20784
|
+
stdin: "ignore",
|
|
20785
|
+
stdout: "pipe"
|
|
20786
|
+
});
|
|
20787
|
+
const [exitCode, stderr, stdout] = await Promise.all([
|
|
20788
|
+
process2.exited,
|
|
20789
|
+
new Response(process2.stderr).text(),
|
|
20790
|
+
new Response(process2.stdout).text()
|
|
20791
|
+
]);
|
|
20792
|
+
return { exitCode, stderr, stdout };
|
|
20793
|
+
};
|
|
20794
|
+
var requireSuccess = async (run, command, label) => {
|
|
20795
|
+
const result = await run(command);
|
|
20796
|
+
if (result.exitCode !== 0)
|
|
20797
|
+
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim()}`);
|
|
20798
|
+
return result;
|
|
20799
|
+
};
|
|
20800
|
+
var adb = (path, serial, ...args) => [
|
|
20801
|
+
path,
|
|
20802
|
+
"-s",
|
|
20803
|
+
serial,
|
|
20804
|
+
...args
|
|
20805
|
+
];
|
|
20806
|
+
var waitForMarker = async (run, adbPath, serial, appId, timeoutMs) => {
|
|
20807
|
+
const startedAt = performance.now();
|
|
20808
|
+
const inspect = async () => {
|
|
20809
|
+
if (performance.now() - startedAt >= timeoutMs)
|
|
20810
|
+
throw new Error("The installed release launched but its embedded web content did not become ready offline.");
|
|
20811
|
+
const [logs, process2] = await Promise.all([
|
|
20812
|
+
run(adb(adbPath, serial, "logcat", "-d", "-s", "AbsoluteJS:I", "*:S")),
|
|
20813
|
+
run(adb(adbPath, serial, "shell", "pidof", appId))
|
|
20814
|
+
]);
|
|
20815
|
+
if (process2.exitCode !== 0 || !process2.stdout.trim())
|
|
20816
|
+
throw new Error("The installed Android release exited before becoming ready.");
|
|
20817
|
+
if (logs.stdout.includes(EMBEDDED_READY_MARKER))
|
|
20818
|
+
return;
|
|
20819
|
+
const failedPhase = /Expo embedded web phase: (assets-(?:root|directory|destination|module|source|download|copy|read|write|finalize|unexpected)|devices)-failed/u.exec(logs.stdout)?.[1];
|
|
20820
|
+
if (failedPhase)
|
|
20821
|
+
throw new Error(`The installed release could not initialize its embedded ${failedPhase} runtime offline.`);
|
|
20822
|
+
await Bun.sleep(RELEASE_READY_POLL_MS);
|
|
20823
|
+
await inspect();
|
|
20824
|
+
};
|
|
20825
|
+
return inspect();
|
|
20826
|
+
};
|
|
20827
|
+
var launch = async (run, adbPath, serial, appId, launchUrl) => {
|
|
20828
|
+
const resolved = await requireSuccess(run, adb(adbPath, serial, "shell", "cmd", "package", "resolve-activity", "--brief", appId), "Android launch activity discovery");
|
|
20829
|
+
const activity = resolved.stdout.split(/\r?\n/u).map((line) => line.trim()).findLast((line) => line.includes("/"));
|
|
20830
|
+
if (!activity)
|
|
20831
|
+
throw new Error(`Android could not resolve a launch activity for ${appId}.`);
|
|
20832
|
+
const startedAt = performance.now();
|
|
20833
|
+
await requireSuccess(run, adb(adbPath, serial, "shell", "am", "start", "-W", "-n", activity, ...launchUrl ? ["-a", "android.intent.action.VIEW", "-d", launchUrl] : []), "Android release launch");
|
|
20834
|
+
return Math.round(performance.now() - startedAt);
|
|
20835
|
+
};
|
|
20836
|
+
var runAbsoluteAndroidReleaseAcceptance = async (options) => {
|
|
20837
|
+
if (options.release.metadata.engine !== "expo")
|
|
20838
|
+
throw new TypeError("Installed AAB release acceptance currently requires the Expo engine.");
|
|
20839
|
+
if (!options.serial.startsWith("emulator-"))
|
|
20840
|
+
throw new TypeError("Installed release acceptance changes network state and therefore requires an Android emulator.");
|
|
20841
|
+
const run = options.run ?? defaultRun;
|
|
20842
|
+
const startedAt = performance.now();
|
|
20843
|
+
await mkdir7(options.artifactDirectory, { recursive: true });
|
|
20844
|
+
const apksPath = join9(options.artifactDirectory, "release.apks");
|
|
20845
|
+
const windowsJava = (options.host ?? detectAbsoluteMobileHost()) === "wsl" && options.java.toLowerCase().endsWith(".exe");
|
|
20846
|
+
const toolPath = async (path) => {
|
|
20847
|
+
if (!windowsJava)
|
|
20848
|
+
return path;
|
|
20849
|
+
const converted = await requireSuccess(run, ["wslpath", "-w", path], "Bundletool Windows path translation");
|
|
20850
|
+
const translated = converted.stdout.trim();
|
|
20851
|
+
if (!translated)
|
|
20852
|
+
throw new Error(`Bundletool could not translate ${basename(path)} for the Windows JDK.`);
|
|
20853
|
+
return translated;
|
|
20854
|
+
};
|
|
20855
|
+
const [bundletoolPath, artifactPath, outputPath, adbPath] = await Promise.all([
|
|
20856
|
+
toolPath(options.bundletool),
|
|
20857
|
+
toolPath(options.release.artifactPath),
|
|
20858
|
+
toolPath(apksPath),
|
|
20859
|
+
toolPath(options.adb)
|
|
20860
|
+
]);
|
|
20861
|
+
await requireSuccess(run, [
|
|
20862
|
+
options.java,
|
|
20863
|
+
"-jar",
|
|
20864
|
+
bundletoolPath,
|
|
20865
|
+
"build-apks",
|
|
20866
|
+
`--bundle=${artifactPath}`,
|
|
20867
|
+
`--output=${outputPath}`,
|
|
20868
|
+
"--mode=universal",
|
|
20869
|
+
"--overwrite"
|
|
20870
|
+
], "Bundletool APK generation");
|
|
20871
|
+
const installStartedAt = performance.now();
|
|
20872
|
+
await requireSuccess(run, [
|
|
20873
|
+
options.java,
|
|
20874
|
+
"-jar",
|
|
20875
|
+
bundletoolPath,
|
|
20876
|
+
"install-apks",
|
|
20877
|
+
`--apks=${outputPath}`,
|
|
20878
|
+
`--adb=${adbPath}`,
|
|
20879
|
+
`--device-id=${options.serial}`
|
|
20880
|
+
], "Bundletool release installation");
|
|
20881
|
+
const installMs = Math.round(performance.now() - installStartedAt);
|
|
20882
|
+
const installed = await inspectAbsoluteAndroidInstalledApp(options.adb, options.serial, options.release.metadata.appId, run);
|
|
20883
|
+
if (options.release.metadata.versionCode !== undefined && installed.versionCode !== options.release.metadata.versionCode)
|
|
20884
|
+
throw new Error(`Installed Android versionCode ${installed.versionCode ?? "unknown"} does not match release ${options.release.metadata.versionCode}.`);
|
|
20885
|
+
await requireSuccess(run, adb(options.adb, options.serial, "logcat", "-c"), "Android release log isolation");
|
|
20886
|
+
const [wifi, mobile] = await Promise.all([
|
|
20887
|
+
requireSuccess(run, adb(options.adb, options.serial, "shell", "settings", "get", "global", "wifi_on"), "Android Wi-Fi state inspection"),
|
|
20888
|
+
requireSuccess(run, adb(options.adb, options.serial, "shell", "settings", "get", "global", "mobile_data"), "Android mobile-data state inspection")
|
|
20889
|
+
]);
|
|
20890
|
+
let launchMs;
|
|
20891
|
+
let relaunchMs;
|
|
20892
|
+
try {
|
|
20893
|
+
await Promise.all([
|
|
20894
|
+
requireSuccess(run, adb(options.adb, options.serial, "shell", "svc", "wifi", "disable"), "Android Wi-Fi disable"),
|
|
20895
|
+
requireSuccess(run, adb(options.adb, options.serial, "shell", "svc", "data", "disable"), "Android mobile-data disable")
|
|
20896
|
+
]);
|
|
20897
|
+
await requireSuccess(run, adb(options.adb, options.serial, "shell", "am", "force-stop", options.release.metadata.appId), "Android release stop");
|
|
20898
|
+
launchMs = await launch(run, options.adb, options.serial, options.release.metadata.appId, options.launchUrl);
|
|
20899
|
+
await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, RELEASE_READY_TIMEOUT_MS);
|
|
20900
|
+
await Bun.sleep(options.stabilityMs ?? RELEASE_STABILITY_MS);
|
|
20901
|
+
await requireSuccess(run, adb(options.adb, options.serial, "shell", "am", "force-stop", options.release.metadata.appId), "Android release relaunch stop");
|
|
20902
|
+
await requireSuccess(run, adb(options.adb, options.serial, "logcat", "-c"), "Android release relaunch log isolation");
|
|
20903
|
+
relaunchMs = await launch(run, options.adb, options.serial, options.release.metadata.appId, options.launchUrl);
|
|
20904
|
+
await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, RELEASE_READY_TIMEOUT_MS);
|
|
20905
|
+
} finally {
|
|
20906
|
+
await Promise.all([
|
|
20907
|
+
requireSuccess(run, adb(options.adb, options.serial, "shell", "svc", "wifi", wifi.stdout.trim() === "1" ? "enable" : "disable"), "Android Wi-Fi restore"),
|
|
20908
|
+
requireSuccess(run, adb(options.adb, options.serial, "shell", "svc", "data", mobile.stdout.trim() === "1" ? "enable" : "disable"), "Android mobile-data restore")
|
|
20909
|
+
]);
|
|
20910
|
+
}
|
|
20911
|
+
return {
|
|
20912
|
+
apksBytes: await stat3(apksPath).then(({ size }) => size),
|
|
20913
|
+
artifactBytes: options.release.metadata.bytes,
|
|
20914
|
+
artifactSha256: options.release.metadata.sha256,
|
|
20915
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
20916
|
+
embeddedOffline: true,
|
|
20917
|
+
engine: options.release.metadata.engine,
|
|
20918
|
+
installed: {
|
|
20919
|
+
...installed.versionCode === undefined ? {} : { versionCode: installed.versionCode },
|
|
20920
|
+
...installed.versionName === undefined ? {} : { versionName: installed.versionName }
|
|
20921
|
+
},
|
|
20922
|
+
installMs,
|
|
20923
|
+
launchMs,
|
|
20924
|
+
relaunchMs,
|
|
20925
|
+
releaseId: options.release.metadata.releaseId,
|
|
20926
|
+
signed: options.release.metadata.signed,
|
|
20927
|
+
status: "pass"
|
|
20928
|
+
};
|
|
20929
|
+
};
|
|
20930
|
+
|
|
20591
20931
|
// src/mobile/iosDeviceAcceptance.ts
|
|
20592
20932
|
var absoluteIosDeviceAcceptanceCommands = (options) => {
|
|
20593
20933
|
const xcrun = options.xcrun ?? "/usr/bin/xcrun";
|
|
@@ -20606,20 +20946,20 @@ var absoluteIosDeviceAcceptanceCommands = (options) => {
|
|
|
20606
20946
|
]
|
|
20607
20947
|
};
|
|
20608
20948
|
};
|
|
20609
|
-
var
|
|
20949
|
+
var requireSuccess2 = (result, message) => {
|
|
20610
20950
|
if (result.exitCode !== 0)
|
|
20611
20951
|
throw new Error(message);
|
|
20612
20952
|
return result;
|
|
20613
20953
|
};
|
|
20614
20954
|
var testAbsoluteIosPhysicalDevice = async (options) => {
|
|
20615
20955
|
const commands = absoluteIosDeviceAcceptanceCommands(options);
|
|
20616
|
-
|
|
20617
|
-
const apps =
|
|
20956
|
+
requireSuccess2(await options.capture(commands.details), "The selected physical iOS device is unavailable. Pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode.");
|
|
20957
|
+
const apps = requireSuccess2(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
|
|
20618
20958
|
if (!apps.stdout.includes(options.appId))
|
|
20619
20959
|
throw new Error("The AbsoluteJS app is not installed on the selected physical iOS device. Start bun dev with the same --ios-device value first.");
|
|
20620
20960
|
const now = options.now ?? performance.now.bind(performance);
|
|
20621
20961
|
const startedAt = now();
|
|
20622
|
-
|
|
20962
|
+
requireSuccess2(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
|
|
20623
20963
|
await options.waitForHmr();
|
|
20624
20964
|
return {
|
|
20625
20965
|
hmrConnected: true,
|
|
@@ -20629,7 +20969,7 @@ var testAbsoluteIosPhysicalDevice = async (options) => {
|
|
|
20629
20969
|
};
|
|
20630
20970
|
|
|
20631
20971
|
// src/mobile/iosConformance.ts
|
|
20632
|
-
import { readFile as
|
|
20972
|
+
import { readFile as readFile11, stat as stat4 } from "fs/promises";
|
|
20633
20973
|
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");
|
|
20634
20974
|
var parseAbsoluteIosHmrLog = (line) => {
|
|
20635
20975
|
const match = HMR_LINE.exec(line);
|
|
@@ -20661,13 +21001,13 @@ var waitForAbsoluteIosHmrLog = async (options) => {
|
|
|
20661
21001
|
const sleep = options.sleep ?? Bun.sleep;
|
|
20662
21002
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
20663
21003
|
const deadline = Date.now() + timeoutMs;
|
|
20664
|
-
let offset = options.startOffset ?? await
|
|
21004
|
+
let offset = options.startOffset ?? await stat4(options.logPath).then(({ size }) => size).catch(() => 0);
|
|
20665
21005
|
let buffered = "";
|
|
20666
21006
|
const poll = async () => {
|
|
20667
21007
|
if (Date.now() > deadline)
|
|
20668
21008
|
throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
|
|
20669
21009
|
options.signal?.throwIfAborted();
|
|
20670
|
-
const contents = await
|
|
21010
|
+
const contents = await readFile11(options.logPath).catch(() => Buffer.alloc(0));
|
|
20671
21011
|
if (contents.byteLength < offset) {
|
|
20672
21012
|
offset = 0;
|
|
20673
21013
|
buffered = "";
|
|
@@ -20688,8 +21028,8 @@ var waitForAbsoluteIosHmrLog = async (options) => {
|
|
|
20688
21028
|
};
|
|
20689
21029
|
|
|
20690
21030
|
// src/mobile/nativeTestReport.ts
|
|
20691
|
-
import { mkdir as
|
|
20692
|
-
import { join as
|
|
21031
|
+
import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
|
|
21032
|
+
import { join as join10 } from "path";
|
|
20693
21033
|
var secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
|
|
20694
21034
|
var bearerPattern = /bearer\s+[^\s,;]+/giu;
|
|
20695
21035
|
var coordinatePattern = /\b(latitude|longitude|lat|lng)(\s*[=:]\s*)-?\d+(?:\.\d+)?/giu;
|
|
@@ -20713,6 +21053,14 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
|
|
|
20713
21053
|
artifactDetails = "Physical-device screen capture was intentionally skipped; use Xcode Device Hub for manual visual evidence.";
|
|
20714
21054
|
artifactResult = "SKIPPED";
|
|
20715
21055
|
}
|
|
21056
|
+
let developmentDetails = run.status === "pass" ? `The app launched and connected to native HMR in ${run.durationMs}ms.${routeDetails}` : `The automated native run failed after ${run.durationMs}ms: ${run.error ?? "No error detail was available."}`;
|
|
21057
|
+
if (run.release)
|
|
21058
|
+
developmentDetails = run.status === "pass" ? `The ${run.release.engine} release was installed from its generated APK set, launched offline in ${run.release.launchMs}ms, and relaunched in ${run.release.relaunchMs}ms.` : `Installed ${run.release.engine} release acceptance failed after ${run.durationMs}ms.`;
|
|
21059
|
+
let hmrDetails = "Correlated edit timing was not requested. Rerun with --wait-for-hmr.";
|
|
21060
|
+
if (run.release)
|
|
21061
|
+
hmrDetails = "HMR is intentionally not part of installed production-release acceptance.";
|
|
21062
|
+
if (run.hmr)
|
|
21063
|
+
hmrDetails = `Observed native HMR ${run.hmr.outcome} in ${run.hmr.durationMs}ms${run.hmr.serverMs === undefined ? "" : ` (server ${run.hmr.serverMs}ms, client ${run.hmr.clientMs}ms)`}.`;
|
|
20716
21064
|
const checks = [
|
|
20717
21065
|
{
|
|
20718
21066
|
details: `Captured host, toolchain, Bun, and AbsoluteJS metadata for ${target}.`,
|
|
@@ -20720,13 +21068,13 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
|
|
|
20720
21068
|
result: "PASS"
|
|
20721
21069
|
},
|
|
20722
21070
|
{
|
|
20723
|
-
details:
|
|
21071
|
+
details: developmentDetails,
|
|
20724
21072
|
...evidence ? { evidence } : {},
|
|
20725
21073
|
id: "AUTO-DEV-01",
|
|
20726
21074
|
result: run.status === "pass" ? "PASS" : "FAIL"
|
|
20727
21075
|
},
|
|
20728
21076
|
{
|
|
20729
|
-
details:
|
|
21077
|
+
details: hmrDetails,
|
|
20730
21078
|
id: "AUTO-HMR-01",
|
|
20731
21079
|
result: hmrResult
|
|
20732
21080
|
},
|
|
@@ -20737,6 +21085,17 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
|
|
|
20737
21085
|
result: artifactResult
|
|
20738
21086
|
}
|
|
20739
21087
|
];
|
|
21088
|
+
if (run.release) {
|
|
21089
|
+
checks.push({
|
|
21090
|
+
details: `Validated immutable release ${run.release.releaseId} (${run.release.artifactBytes} bytes, SHA-256 ${run.release.artifactSha256}); Bundletool produced a ${run.release.apksBytes}-byte APK set and installed it in ${run.release.installMs}ms.`,
|
|
21091
|
+
id: "AUTO-RELEASE-01",
|
|
21092
|
+
result: run.status === "pass" ? "PASS" : "FAIL"
|
|
21093
|
+
}, {
|
|
21094
|
+
details: run.release.embeddedOffline ? "The generated release rendered its embedded web content and survived a process relaunch with Wi-Fi and mobile data disabled." : "The generated release did not prove embedded offline rendering.",
|
|
21095
|
+
id: "AUTO-RELEASE-OFFLINE-01",
|
|
21096
|
+
result: run.release.embeddedOffline ? "PASS" : "FAIL"
|
|
21097
|
+
});
|
|
21098
|
+
}
|
|
20740
21099
|
if (run.upgrade) {
|
|
20741
21100
|
const { upgrade } = run;
|
|
20742
21101
|
checks.push({
|
|
@@ -20799,7 +21158,7 @@ var createAbsoluteNativeTestReport = (options) => ({
|
|
|
20799
21158
|
run: options.run
|
|
20800
21159
|
});
|
|
20801
21160
|
var readPackageVersionForNativeReport = async (packageJsonPath) => {
|
|
20802
|
-
const manifest = JSON.parse(await
|
|
21161
|
+
const manifest = JSON.parse(await readFile12(packageJsonPath, "utf8"));
|
|
20803
21162
|
if (typeof manifest !== "object" || manifest === null)
|
|
20804
21163
|
return "unknown";
|
|
20805
21164
|
const version = Reflect.get(manifest, "version");
|
|
@@ -20837,13 +21196,13 @@ ${table(report.manualChecks)}
|
|
|
20837
21196
|
`;
|
|
20838
21197
|
};
|
|
20839
21198
|
var writeAbsoluteNativeTestReport = async (directory, report) => {
|
|
20840
|
-
await
|
|
20841
|
-
const jsonPath =
|
|
20842
|
-
const markdownPath =
|
|
21199
|
+
await mkdir8(directory, { recursive: true });
|
|
21200
|
+
const jsonPath = join10(directory, "report.json");
|
|
21201
|
+
const markdownPath = join10(directory, "report.md");
|
|
20843
21202
|
await Promise.all([
|
|
20844
|
-
|
|
21203
|
+
writeFile10(jsonPath, `${JSON.stringify(report, null, 2)}
|
|
20845
21204
|
`),
|
|
20846
|
-
|
|
21205
|
+
writeFile10(markdownPath, renderAbsoluteNativeTestReport(report))
|
|
20847
21206
|
]);
|
|
20848
21207
|
return { directory, jsonPath, markdownPath };
|
|
20849
21208
|
};
|
|
@@ -21071,6 +21430,8 @@ var MANUAL_CHECKS2 = [
|
|
|
21071
21430
|
"Review this directory for sensitive content and complete every row."
|
|
21072
21431
|
]
|
|
21073
21432
|
];
|
|
21433
|
+
var portableAdbVersion = (version) => version.split(/\r?\n/u).filter((line) => !/^\s*(?:Installed as|Running on)\b/iu.test(line)).join(`
|
|
21434
|
+
`).trim();
|
|
21074
21435
|
var createAbsoluteAndroidTestReport = (options) => {
|
|
21075
21436
|
const { serial, ...run } = options.run;
|
|
21076
21437
|
return createAbsoluteNativeTestReport({
|
|
@@ -21078,10 +21439,10 @@ var createAbsoluteAndroidTestReport = (options) => {
|
|
|
21078
21439
|
manualChecks: MANUAL_CHECKS2,
|
|
21079
21440
|
metadata: {
|
|
21080
21441
|
absolutejsVersion: options.absolutejsVersion,
|
|
21081
|
-
adbVersion: options.adbVersion,
|
|
21442
|
+
adbVersion: portableAdbVersion(options.adbVersion),
|
|
21082
21443
|
bunVersion: options.bunVersion,
|
|
21083
21444
|
host: options.host,
|
|
21084
|
-
provider: "capacitor"
|
|
21445
|
+
provider: options.provider ?? "capacitor"
|
|
21085
21446
|
},
|
|
21086
21447
|
run: {
|
|
21087
21448
|
...run,
|
|
@@ -21093,8 +21454,8 @@ var createAbsoluteAndroidTestReport = (options) => {
|
|
|
21093
21454
|
};
|
|
21094
21455
|
|
|
21095
21456
|
// src/mobile/releasePublisher.ts
|
|
21096
|
-
import { access as
|
|
21097
|
-
import { isAbsolute as
|
|
21457
|
+
import { access as access7 } from "fs/promises";
|
|
21458
|
+
import { isAbsolute as isAbsolute4, relative as relative6, resolve as resolve7, sep as sep4 } from "path";
|
|
21098
21459
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
21099
21460
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
21100
21461
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -21117,24 +21478,24 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
|
21117
21478
|
}
|
|
21118
21479
|
return versionCode;
|
|
21119
21480
|
};
|
|
21120
|
-
var
|
|
21121
|
-
var isPublisher = (value) =>
|
|
21481
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21482
|
+
var isPublisher = (value) => isRecord6(value) && typeof value.publish === "function";
|
|
21122
21483
|
var publisherModulePath = (projectRoot, requested) => {
|
|
21123
|
-
const root =
|
|
21124
|
-
const path =
|
|
21125
|
-
const projectRelative =
|
|
21126
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
21484
|
+
const root = resolve7(projectRoot);
|
|
21485
|
+
const path = resolve7(root, requested);
|
|
21486
|
+
const projectRelative = relative6(root, path);
|
|
21487
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep4}`) || isAbsolute4(projectRelative)) {
|
|
21127
21488
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
21128
21489
|
}
|
|
21129
21490
|
return path;
|
|
21130
21491
|
};
|
|
21131
21492
|
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
21132
21493
|
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
21133
|
-
await
|
|
21494
|
+
await access7(modulePath).catch(() => {
|
|
21134
21495
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
21135
21496
|
});
|
|
21136
21497
|
const loaded = await import(pathToFileURL2(modulePath).href);
|
|
21137
|
-
const publisher =
|
|
21498
|
+
const publisher = isRecord6(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
21138
21499
|
if (!isPublisher(publisher)) {
|
|
21139
21500
|
throw new TypeError("Native release registry module must default-export a registry with publish(options).");
|
|
21140
21501
|
}
|
|
@@ -21192,8 +21553,8 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
21192
21553
|
};
|
|
21193
21554
|
|
|
21194
21555
|
// src/mobile/mobileInspect.ts
|
|
21195
|
-
import { access as
|
|
21196
|
-
import { join as
|
|
21556
|
+
import { access as access8, readFile as readFile13 } from "fs/promises";
|
|
21557
|
+
import { join as join11, relative as relative7, resolve as resolve8 } from "path";
|
|
21197
21558
|
var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
|
|
21198
21559
|
var MOBILE_PACKAGE_NAMES = new Set([
|
|
21199
21560
|
"@absolutejs/absolute",
|
|
@@ -21208,19 +21569,19 @@ var MOBILE_PACKAGE_NAMES = new Set([
|
|
|
21208
21569
|
]);
|
|
21209
21570
|
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21210
21571
|
var portablePath2 = (projectRoot, path) => {
|
|
21211
|
-
const value =
|
|
21572
|
+
const value = relative7(resolve8(projectRoot), resolve8(path)).replaceAll("\\", "/");
|
|
21212
21573
|
return value || ".";
|
|
21213
21574
|
};
|
|
21214
21575
|
var pathExists4 = async (path) => {
|
|
21215
21576
|
try {
|
|
21216
|
-
await
|
|
21577
|
+
await access8(path);
|
|
21217
21578
|
return true;
|
|
21218
21579
|
} catch {
|
|
21219
21580
|
return false;
|
|
21220
21581
|
}
|
|
21221
21582
|
};
|
|
21222
21583
|
var readObject2 = async (path) => {
|
|
21223
|
-
const value = JSON.parse(await
|
|
21584
|
+
const value = JSON.parse(await readFile13(path, "utf8"));
|
|
21224
21585
|
if (!isObject2(value))
|
|
21225
21586
|
throw new TypeError("JSON root must be an object.");
|
|
21226
21587
|
return value;
|
|
@@ -21232,13 +21593,13 @@ var addPackageDeclarations = (declarations, value) => {
|
|
|
21232
21593
|
declarations.set(name, declared);
|
|
21233
21594
|
};
|
|
21234
21595
|
var packageInspections = async (projectRoot, additionalNames) => {
|
|
21235
|
-
const project = await readObject2(
|
|
21596
|
+
const project = await readObject2(join11(projectRoot, "package.json"));
|
|
21236
21597
|
const declarations = new Map;
|
|
21237
|
-
for (const
|
|
21238
|
-
addPackageDeclarations(declarations, project[
|
|
21598
|
+
for (const field2 of ["dependencies", "devDependencies"])
|
|
21599
|
+
addPackageDeclarations(declarations, project[field2]);
|
|
21239
21600
|
const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
|
|
21240
21601
|
return Promise.all(names.map(async (name) => {
|
|
21241
|
-
const installedManifest = await readObject2(
|
|
21602
|
+
const installedManifest = await readObject2(join11(projectRoot, "node_modules", name, "package.json")).catch(() => {
|
|
21242
21603
|
return;
|
|
21243
21604
|
});
|
|
21244
21605
|
const installed = installedManifest?.version;
|
|
@@ -21297,7 +21658,7 @@ var inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) =>
|
|
|
21297
21658
|
},
|
|
21298
21659
|
format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
|
|
21299
21660
|
nativeProjects: await Promise.all(config.platforms.map(async (platform) => {
|
|
21300
|
-
const path =
|
|
21661
|
+
const path = join11(config.nativeProjectDirectory, platform);
|
|
21301
21662
|
return {
|
|
21302
21663
|
initialized: await pathExists4(path),
|
|
21303
21664
|
path: portablePath2(projectRoot, path),
|
|
@@ -21354,8 +21715,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
|
|
|
21354
21715
|
|
|
21355
21716
|
// src/mobile/ciWorkflow.ts
|
|
21356
21717
|
import { existsSync } from "fs";
|
|
21357
|
-
import { access as
|
|
21358
|
-
import { dirname as
|
|
21718
|
+
import { access as access9, mkdir as mkdir9, readFile as readFile14, writeFile as writeFile11 } from "fs/promises";
|
|
21719
|
+
import { dirname as dirname8, extname as extname2, relative as relative8, resolve as resolve9, sep as sep5 } from "path";
|
|
21359
21720
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
|
|
21360
21721
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
21361
21722
|
var CI_ENV_INDENTATION = 6;
|
|
@@ -21374,34 +21735,34 @@ var RESERVED_SECRET_NAMES = new Set([
|
|
|
21374
21735
|
"APP_STORE_CONNECT_KEY_ID",
|
|
21375
21736
|
"APP_STORE_CONNECT_PRIVATE_KEY_BASE64"
|
|
21376
21737
|
]);
|
|
21377
|
-
var
|
|
21738
|
+
var exists3 = async (path) => {
|
|
21378
21739
|
try {
|
|
21379
|
-
await
|
|
21740
|
+
await access9(path);
|
|
21380
21741
|
return true;
|
|
21381
21742
|
} catch {
|
|
21382
21743
|
return false;
|
|
21383
21744
|
}
|
|
21384
21745
|
};
|
|
21385
21746
|
var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
|
|
21386
|
-
var projectPath2 = (projectRoot, value,
|
|
21387
|
-
const root =
|
|
21388
|
-
const path =
|
|
21389
|
-
const portable =
|
|
21390
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
21391
|
-
throw new TypeError(`${
|
|
21747
|
+
var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
21748
|
+
const root = resolve9(projectRoot);
|
|
21749
|
+
const path = resolve9(root, value);
|
|
21750
|
+
const portable = relative8(root, path).replaceAll("\\", "/");
|
|
21751
|
+
if (portable === ".." || portable.startsWith(`..${sep5}`) || portable.startsWith("../") || portable === "") {
|
|
21752
|
+
throw new TypeError(`${field2} must remain inside the project root.`);
|
|
21392
21753
|
}
|
|
21393
21754
|
if (/\r|\n/u.test(portable) || portable.startsWith("-"))
|
|
21394
|
-
throw new TypeError(`${
|
|
21755
|
+
throw new TypeError(`${field2} contains an unsafe path.`);
|
|
21395
21756
|
if (!options.allowMissing && !existsSync(path))
|
|
21396
|
-
throw new TypeError(`${
|
|
21757
|
+
throw new TypeError(`${field2} does not exist inside the project.`);
|
|
21397
21758
|
return portable;
|
|
21398
21759
|
};
|
|
21399
21760
|
var workflowOutputPath = (projectRoot, value) => {
|
|
21400
|
-
const root =
|
|
21401
|
-
const workflows =
|
|
21402
|
-
const path =
|
|
21403
|
-
const portable =
|
|
21404
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
21761
|
+
const root = resolve9(projectRoot);
|
|
21762
|
+
const workflows = resolve9(root, ".github/workflows");
|
|
21763
|
+
const path = resolve9(root, value ?? ".github/workflows/absolute-mobile.yml");
|
|
21764
|
+
const portable = relative8(workflows, path);
|
|
21765
|
+
if (portable === ".." || portable.startsWith(`..${sep5}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
|
|
21405
21766
|
throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
|
|
21406
21767
|
}
|
|
21407
21768
|
return path;
|
|
@@ -21830,12 +22191,12 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
21830
22191
|
var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
21831
22192
|
const path = workflowOutputPath(options.projectRoot, options.outputPath);
|
|
21832
22193
|
const generated = createAbsoluteMobileGithubWorkflow(options);
|
|
21833
|
-
const previous = await
|
|
22194
|
+
const previous = await exists3(path) ? await readFile14(path, "utf8") : undefined;
|
|
21834
22195
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
21835
|
-
throw new TypeError(`${
|
|
22196
|
+
throw new TypeError(`${relative8(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
21836
22197
|
if (previous !== generated.workflow) {
|
|
21837
|
-
await
|
|
21838
|
-
await
|
|
22198
|
+
await mkdir9(dirname8(path), { recursive: true });
|
|
22199
|
+
await writeFile11(path, generated.workflow);
|
|
21839
22200
|
}
|
|
21840
22201
|
return {
|
|
21841
22202
|
changed: previous !== generated.workflow,
|
|
@@ -21848,46 +22209,46 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
|
21848
22209
|
};
|
|
21849
22210
|
|
|
21850
22211
|
// src/mobile/updateSigning.ts
|
|
21851
|
-
import { createHash as
|
|
22212
|
+
import { createHash as createHash4, sign, verify } from "crypto";
|
|
21852
22213
|
import {
|
|
21853
22214
|
cp,
|
|
21854
|
-
mkdir as
|
|
21855
|
-
mkdtemp as
|
|
22215
|
+
mkdir as mkdir10,
|
|
22216
|
+
mkdtemp as mkdtemp3,
|
|
21856
22217
|
readdir as readdir4,
|
|
21857
|
-
readFile as
|
|
21858
|
-
rename as
|
|
21859
|
-
rm as
|
|
21860
|
-
stat as
|
|
21861
|
-
writeFile as
|
|
22218
|
+
readFile as readFile15,
|
|
22219
|
+
rename as rename9,
|
|
22220
|
+
rm as rm6,
|
|
22221
|
+
stat as stat5,
|
|
22222
|
+
writeFile as writeFile12
|
|
21862
22223
|
} from "fs/promises";
|
|
21863
|
-
import { dirname as
|
|
22224
|
+
import { dirname as dirname9, join as join12, relative as relative9, resolve as resolve10 } from "path";
|
|
21864
22225
|
var UPDATE_MANIFEST_FILE = "update.json";
|
|
21865
22226
|
var UPDATE_FILES_DIRECTORY = "files";
|
|
21866
|
-
var
|
|
22227
|
+
var sha2562 = (value) => createHash4("sha256").update(value).digest("hex");
|
|
21867
22228
|
var listFiles = async (root, directory = root) => {
|
|
21868
22229
|
const entries = await readdir4(directory, { withFileTypes: true });
|
|
21869
22230
|
const paths = await Promise.all(entries.map(async (entry) => {
|
|
21870
|
-
const path =
|
|
22231
|
+
const path = join12(directory, entry.name);
|
|
21871
22232
|
if (entry.isDirectory())
|
|
21872
22233
|
return listFiles(root, path);
|
|
21873
22234
|
if (!entry.isFile())
|
|
21874
22235
|
throw new TypeError("Mobile updates cannot contain links or special files.");
|
|
21875
|
-
return [
|
|
22236
|
+
return [relative9(root, path).replaceAll("\\", "/")];
|
|
21876
22237
|
}));
|
|
21877
22238
|
return paths.flat().sort((left, right) => left.localeCompare(right));
|
|
21878
22239
|
};
|
|
21879
22240
|
var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
|
|
21880
|
-
const source =
|
|
22241
|
+
const source = join12(root, path);
|
|
21881
22242
|
const [metadata, contents] = await Promise.all([
|
|
21882
|
-
|
|
21883
|
-
|
|
22243
|
+
stat5(source),
|
|
22244
|
+
readFile15(source)
|
|
21884
22245
|
]);
|
|
21885
|
-
return { bytes: metadata.size, path, sha256:
|
|
22246
|
+
return { bytes: metadata.size, path, sha256: sha2562(contents) };
|
|
21886
22247
|
}));
|
|
21887
|
-
var releaseIdFor = (value) => `amu_${
|
|
22248
|
+
var releaseIdFor = (value) => `amu_${sha2562(canonicalizeAbsoluteMobileUpdate(value))}`;
|
|
21888
22249
|
var buildAbsoluteMobileUpdate = async (options) => {
|
|
21889
|
-
const bundleDirectory =
|
|
21890
|
-
const outputRoot =
|
|
22250
|
+
const bundleDirectory = resolve10(options.bundleDirectory);
|
|
22251
|
+
const outputRoot = resolve10(options.outputDirectory);
|
|
21891
22252
|
if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
|
|
21892
22253
|
throw new TypeError("Mobile update output must be outside the embedded bundle.");
|
|
21893
22254
|
const paths = await listFiles(bundleDirectory);
|
|
@@ -21915,30 +22276,30 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
21915
22276
|
value: signature.toString("base64")
|
|
21916
22277
|
}
|
|
21917
22278
|
});
|
|
21918
|
-
await
|
|
21919
|
-
const outputDirectory =
|
|
21920
|
-
const staging = await
|
|
22279
|
+
await mkdir10(outputRoot, { recursive: true });
|
|
22280
|
+
const outputDirectory = join12(outputRoot, manifest.releaseId);
|
|
22281
|
+
const staging = await mkdtemp3(join12(outputRoot, ".stage-"));
|
|
21921
22282
|
try {
|
|
21922
|
-
await cp(bundleDirectory,
|
|
22283
|
+
await cp(bundleDirectory, join12(staging, UPDATE_FILES_DIRECTORY), {
|
|
21923
22284
|
force: true,
|
|
21924
22285
|
recursive: true
|
|
21925
22286
|
});
|
|
21926
|
-
await
|
|
22287
|
+
await writeFile12(join12(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
21927
22288
|
`);
|
|
21928
|
-
await
|
|
22289
|
+
await rename9(staging, outputDirectory);
|
|
21929
22290
|
} catch (error) {
|
|
21930
|
-
await
|
|
22291
|
+
await rm6(staging, { force: true, recursive: true });
|
|
21931
22292
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
|
|
21932
22293
|
throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
|
|
21933
22294
|
throw error;
|
|
21934
22295
|
}
|
|
21935
22296
|
return {
|
|
21936
22297
|
manifest,
|
|
21937
|
-
manifestPath:
|
|
22298
|
+
manifestPath: join12(outputDirectory, UPDATE_MANIFEST_FILE),
|
|
21938
22299
|
outputDirectory
|
|
21939
22300
|
};
|
|
21940
22301
|
};
|
|
21941
|
-
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await
|
|
22302
|
+
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile15(join12(resolve10(directory), UPDATE_MANIFEST_FILE), "utf8")));
|
|
21942
22303
|
var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
21943
22304
|
const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
|
|
21944
22305
|
const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
|
|
@@ -21948,17 +22309,17 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
21948
22309
|
};
|
|
21949
22310
|
|
|
21950
22311
|
// src/mobile/expoUpdate.ts
|
|
21951
|
-
import { access as
|
|
21952
|
-
import { dirname as
|
|
22312
|
+
import { access as access10, mkdir as mkdir11, readFile as readFile16, writeFile as writeFile13 } from "fs/promises";
|
|
22313
|
+
import { dirname as dirname10, extname as extname3, join as join13 } from "path";
|
|
21953
22314
|
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
|
|
21954
22315
|
var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
|
|
21955
22316
|
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21956
|
-
var safePath = (value,
|
|
22317
|
+
var safePath = (value, field2) => {
|
|
21957
22318
|
if (typeof value !== "string" || value.length === 0)
|
|
21958
|
-
throw new TypeError(`Expo update ${
|
|
22319
|
+
throw new TypeError(`Expo update ${field2} is invalid.`);
|
|
21959
22320
|
const path = value.replaceAll("\\", "/");
|
|
21960
22321
|
if (path.startsWith("/") || path.split("/").some((segment) => !segment || segment === "." || segment === ".."))
|
|
21961
|
-
throw new TypeError(`Expo update ${
|
|
22322
|
+
throw new TypeError(`Expo update ${field2} must be a relative path.`);
|
|
21962
22323
|
return path;
|
|
21963
22324
|
};
|
|
21964
22325
|
var extension = (value, path) => {
|
|
@@ -22015,7 +22376,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
|
22015
22376
|
};
|
|
22016
22377
|
};
|
|
22017
22378
|
var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
22018
|
-
const metadata = JSON.parse(await
|
|
22379
|
+
const metadata = JSON.parse(await readFile16(join13(options.exportDirectory, "metadata.json"), "utf8"));
|
|
22019
22380
|
if (!object2(metadata) || !object2(metadata.fileMetadata))
|
|
22020
22381
|
throw new TypeError("Expo export metadata.json is invalid.");
|
|
22021
22382
|
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
@@ -22026,33 +22387,33 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
22026
22387
|
runtimeVersion: options.runtimeVersion
|
|
22027
22388
|
});
|
|
22028
22389
|
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
22029
|
-
await Promise.all(referenced.map(({ path }) =>
|
|
22390
|
+
await Promise.all(referenced.map(({ path }) => access10(join13(options.exportDirectory, path)).catch(() => {
|
|
22030
22391
|
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
22031
22392
|
})));
|
|
22032
|
-
const destination =
|
|
22033
|
-
await
|
|
22034
|
-
await
|
|
22393
|
+
const destination = join13(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
22394
|
+
await mkdir11(dirname10(destination), { recursive: true });
|
|
22395
|
+
await writeFile13(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
22035
22396
|
`);
|
|
22036
22397
|
return { descriptor, path: destination };
|
|
22037
22398
|
};
|
|
22038
22399
|
|
|
22039
22400
|
// src/mobile/expoCodeSigning.ts
|
|
22040
22401
|
var import_code_signing_certificates = __toESM(require_main(), 1);
|
|
22041
|
-
import { access as
|
|
22042
|
-
import { dirname as
|
|
22043
|
-
var
|
|
22044
|
-
var
|
|
22045
|
-
const location =
|
|
22046
|
-
return location === "" || location !== ".." && !location.startsWith(`..${
|
|
22402
|
+
import { access as access11, mkdir as mkdir12, writeFile as writeFile14 } from "fs/promises";
|
|
22403
|
+
import { dirname as dirname11, isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep6 } from "path";
|
|
22404
|
+
var exists4 = async (path) => access11(path).then(() => true).catch(() => false);
|
|
22405
|
+
var inside2 = (root, path) => {
|
|
22406
|
+
const location = relative10(root, path);
|
|
22407
|
+
return location === "" || location !== ".." && !location.startsWith(`..${sep6}`) && !isAbsolute5(location);
|
|
22047
22408
|
};
|
|
22048
22409
|
var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
22049
|
-
const root =
|
|
22050
|
-
const certificatePath =
|
|
22051
|
-
const privateKeyPath =
|
|
22052
|
-
const publicKeyPath =
|
|
22053
|
-
if (!
|
|
22410
|
+
const root = resolve11(options.projectRoot);
|
|
22411
|
+
const certificatePath = resolve11(root, options.certificatePath);
|
|
22412
|
+
const privateKeyPath = resolve11(root, options.privateKeyPath);
|
|
22413
|
+
const publicKeyPath = resolve11(root, options.publicKeyPath ?? resolve11(dirname11(privateKeyPath), "public-key.pem"));
|
|
22414
|
+
if (!inside2(root, certificatePath))
|
|
22054
22415
|
throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
|
|
22055
|
-
if (
|
|
22416
|
+
if (inside2(root, privateKeyPath) || inside2(root, publicKeyPath))
|
|
22056
22417
|
throw new TypeError("Expo code-signing private/public key output must remain outside the project and source control.");
|
|
22057
22418
|
const commonName = options.commonName.trim();
|
|
22058
22419
|
if (!commonName || commonName.length > 64)
|
|
@@ -22061,7 +22422,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22061
22422
|
if (!Number.isSafeInteger(validityYears) || validityYears < 1 || validityYears > 20)
|
|
22062
22423
|
throw new TypeError("Expo code-signing validity must be an integer from 1 through 20 years.");
|
|
22063
22424
|
const collisions = (await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map(async (path) => ({
|
|
22064
|
-
exists: await
|
|
22425
|
+
exists: await exists4(path),
|
|
22065
22426
|
path
|
|
22066
22427
|
})))).filter((entry) => entry.exists);
|
|
22067
22428
|
if (collisions.length > 0)
|
|
@@ -22079,11 +22440,11 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22079
22440
|
import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
|
|
22080
22441
|
const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
|
|
22081
22442
|
const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
|
|
22082
|
-
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) =>
|
|
22443
|
+
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir12(dirname11(path), { recursive: true })));
|
|
22083
22444
|
await Promise.all([
|
|
22084
|
-
|
|
22085
|
-
|
|
22086
|
-
|
|
22445
|
+
writeFile14(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
|
|
22446
|
+
writeFile14(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
|
|
22447
|
+
writeFile14(publicKeyPath, publicKeyPEM, { flag: "wx", mode: 420 })
|
|
22087
22448
|
]);
|
|
22088
22449
|
return {
|
|
22089
22450
|
certificatePath,
|
|
@@ -22094,8 +22455,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22094
22455
|
};
|
|
22095
22456
|
|
|
22096
22457
|
// src/mobile/updatePublisher.ts
|
|
22097
|
-
import { access as
|
|
22098
|
-
import { isAbsolute as
|
|
22458
|
+
import { access as access12 } from "fs/promises";
|
|
22459
|
+
import { isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep7 } from "path";
|
|
22099
22460
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
22100
22461
|
var lifecycleMethod = (publisher, name) => {
|
|
22101
22462
|
const method = publisher[name];
|
|
@@ -22204,16 +22565,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
|
|
|
22204
22565
|
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22205
22566
|
var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
22206
22567
|
var projectPath3 = (projectRoot, requested, label) => {
|
|
22207
|
-
const root =
|
|
22208
|
-
const path =
|
|
22209
|
-
const projectRelative =
|
|
22210
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
22568
|
+
const root = resolve12(projectRoot);
|
|
22569
|
+
const path = resolve12(root, requested);
|
|
22570
|
+
const projectRelative = relative11(root, path);
|
|
22571
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep7}`) || isAbsolute6(projectRelative))
|
|
22211
22572
|
throw new TypeError(`${label} must remain inside the project.`);
|
|
22212
22573
|
return path;
|
|
22213
22574
|
};
|
|
22214
22575
|
var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
|
|
22215
22576
|
const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
|
|
22216
|
-
await
|
|
22577
|
+
await access12(modulePath).catch(() => {
|
|
22217
22578
|
throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
|
|
22218
22579
|
});
|
|
22219
22580
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
@@ -22266,10 +22627,12 @@ var rollbackAbsoluteMobileUpdate = async (options) => {
|
|
|
22266
22627
|
|
|
22267
22628
|
// src/cli/scripts/mobile.ts
|
|
22268
22629
|
var NOT_FOUND4 = -1;
|
|
22269
|
-
var
|
|
22630
|
+
var ANDROID_EMULATOR_BOOT_POLL_MS = 500;
|
|
22631
|
+
var ANDROID_EMULATOR_BOOT_TIMEOUT_MS = 180000;
|
|
22632
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22270
22633
|
var expoPublicConfig = (config) => {
|
|
22271
22634
|
const result = { ...config };
|
|
22272
|
-
if (!
|
|
22635
|
+
if (!isRecord7(result.updates))
|
|
22273
22636
|
return result;
|
|
22274
22637
|
const {
|
|
22275
22638
|
codeSigningCertificate: _codeSigningCertificate,
|
|
@@ -22309,13 +22672,13 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
|
22309
22672
|
];
|
|
22310
22673
|
var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
|
|
22311
22674
|
var directProjectPackages = async (projectRoot) => {
|
|
22312
|
-
const manifest = JSON.parse(await
|
|
22313
|
-
if (!
|
|
22675
|
+
const manifest = JSON.parse(await readFile17(join14(projectRoot, "package.json"), "utf8"));
|
|
22676
|
+
if (!isRecord7(manifest))
|
|
22314
22677
|
throw new TypeError("Application package.json must contain an object.");
|
|
22315
22678
|
const names = new Set;
|
|
22316
|
-
for (const
|
|
22317
|
-
const dependencies = Reflect.get(manifest,
|
|
22318
|
-
if (
|
|
22679
|
+
for (const field2 of ["dependencies", "devDependencies"]) {
|
|
22680
|
+
const dependencies = Reflect.get(manifest, field2);
|
|
22681
|
+
if (isRecord7(dependencies))
|
|
22319
22682
|
for (const name of Object.keys(dependencies))
|
|
22320
22683
|
names.add(name);
|
|
22321
22684
|
}
|
|
@@ -22323,8 +22686,8 @@ var directProjectPackages = async (projectRoot) => {
|
|
|
22323
22686
|
};
|
|
22324
22687
|
var resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
22325
22688
|
try {
|
|
22326
|
-
const manifest = JSON.parse(await
|
|
22327
|
-
return
|
|
22689
|
+
const manifest = JSON.parse(await readFile17(join14(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
22690
|
+
return isRecord7(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
22328
22691
|
} catch {
|
|
22329
22692
|
return;
|
|
22330
22693
|
}
|
|
@@ -22374,9 +22737,9 @@ var requireMobileConfig = (value) => {
|
|
|
22374
22737
|
return value;
|
|
22375
22738
|
};
|
|
22376
22739
|
var capacitorExecutable = async (projectRoot) => {
|
|
22377
|
-
const executable =
|
|
22740
|
+
const executable = join14(projectRoot, "node_modules", ".bin", "cap");
|
|
22378
22741
|
try {
|
|
22379
|
-
await
|
|
22742
|
+
await access13(executable);
|
|
22380
22743
|
return executable;
|
|
22381
22744
|
} catch {
|
|
22382
22745
|
throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
|
|
@@ -22397,9 +22760,9 @@ var runCapacitor = async (projectRoot, args) => {
|
|
|
22397
22760
|
};
|
|
22398
22761
|
var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
|
|
22399
22762
|
var expoExecutable = async (project) => {
|
|
22400
|
-
const executable =
|
|
22763
|
+
const executable = join14(project, "node_modules", ".bin", "expo");
|
|
22401
22764
|
try {
|
|
22402
|
-
await
|
|
22765
|
+
await access13(executable);
|
|
22403
22766
|
return executable;
|
|
22404
22767
|
} catch {
|
|
22405
22768
|
throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
|
|
@@ -22431,14 +22794,14 @@ var runExpo = async (project, args, options = {}) => {
|
|
|
22431
22794
|
};
|
|
22432
22795
|
var ensureExpoPackages = async (project, args) => {
|
|
22433
22796
|
try {
|
|
22434
|
-
const manifest = JSON.parse(await
|
|
22797
|
+
const manifest = JSON.parse(await readFile17(join14(project, "package.json"), "utf8"));
|
|
22435
22798
|
const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
|
|
22436
22799
|
if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
|
|
22437
22800
|
throw new TypeError("Generated Expo dependencies are invalid.");
|
|
22438
22801
|
await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
|
|
22439
22802
|
if (typeof expected !== "string")
|
|
22440
22803
|
throw new TypeError("Generated Expo dependency version is invalid.");
|
|
22441
|
-
const installed = JSON.parse(await
|
|
22804
|
+
const installed = JSON.parse(await readFile17(join14(project, "node_modules", name, "package.json"), "utf8"));
|
|
22442
22805
|
const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
|
|
22443
22806
|
if (typeof actual !== "string")
|
|
22444
22807
|
throw new TypeError("Installed Expo dependency version is invalid.");
|
|
@@ -22565,7 +22928,7 @@ var unpairRemoteMac = async (args) => {
|
|
|
22565
22928
|
var initialize = async (args) => {
|
|
22566
22929
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
22567
22930
|
if (mobile.engine === "expo") {
|
|
22568
|
-
console.warn("Experimental: Expo Android
|
|
22931
|
+
console.warn("Experimental: Expo Android and iOS release automation is available; real macOS and physical-device acceptance remains pending.");
|
|
22569
22932
|
const generated2 = await writeAbsoluteExpoProject(mobile, {
|
|
22570
22933
|
force: args.includes("--force"),
|
|
22571
22934
|
projectRoot
|
|
@@ -22634,7 +22997,7 @@ var sync = async (args) => {
|
|
|
22634
22997
|
};
|
|
22635
22998
|
var associations = async (args) => {
|
|
22636
22999
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
22637
|
-
const outputDirectory =
|
|
23000
|
+
const outputDirectory = resolve13(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
|
|
22638
23001
|
if (args.includes("--verify")) {
|
|
22639
23002
|
const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
|
|
22640
23003
|
console.log(`Verified ${result2.results.length} hosted association files`);
|
|
@@ -22686,7 +23049,7 @@ var generateGithubCi = async (args) => {
|
|
|
22686
23049
|
const publicResult = {
|
|
22687
23050
|
changed: result.changed,
|
|
22688
23051
|
format: result.format,
|
|
22689
|
-
path:
|
|
23052
|
+
path: relative12(projectRoot, result.path).replaceAll("\\", "/"),
|
|
22690
23053
|
platforms: result.platforms,
|
|
22691
23054
|
publishing: result.publishing,
|
|
22692
23055
|
requiredSecrets: result.requiredSecrets
|
|
@@ -22797,9 +23160,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
22797
23160
|
temporaryDirectory: undefined
|
|
22798
23161
|
};
|
|
22799
23162
|
await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
|
|
22800
|
-
const temporaryParent =
|
|
22801
|
-
await
|
|
22802
|
-
const temporaryDirectory = await
|
|
23163
|
+
const temporaryParent = join14(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
|
|
23164
|
+
await mkdir13(temporaryParent, { recursive: true });
|
|
23165
|
+
const temporaryDirectory = await mkdtemp4(join14(temporaryParent, ".stage-"));
|
|
22803
23166
|
try {
|
|
22804
23167
|
await runExpo(mobile.nativeProjectDirectory, [
|
|
22805
23168
|
"export",
|
|
@@ -22811,9 +23174,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
22811
23174
|
temporaryDirectory,
|
|
22812
23175
|
"--clear"
|
|
22813
23176
|
], { production: true });
|
|
22814
|
-
const generatedApp = JSON.parse(await
|
|
22815
|
-
const expoConfig =
|
|
22816
|
-
if (!
|
|
23177
|
+
const generatedApp = JSON.parse(await readFile17(join14(mobile.nativeProjectDirectory, "app.json"), "utf8"));
|
|
23178
|
+
const expoConfig = isRecord7(generatedApp) ? generatedApp.expo : undefined;
|
|
23179
|
+
if (!isRecord7(expoConfig))
|
|
22817
23180
|
throw new TypeError("Generated Expo app configuration is invalid.");
|
|
22818
23181
|
const publicExpoConfig = expoPublicConfig(expoConfig);
|
|
22819
23182
|
const computedRuntime = resolveAbsoluteMobileUpdateRuntime(mobile, options.projectRoot).fingerprint;
|
|
@@ -22829,7 +23192,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
22829
23192
|
temporaryDirectory
|
|
22830
23193
|
};
|
|
22831
23194
|
} catch (error) {
|
|
22832
|
-
await
|
|
23195
|
+
await rm7(temporaryDirectory, { force: true, recursive: true });
|
|
22833
23196
|
throw error;
|
|
22834
23197
|
}
|
|
22835
23198
|
};
|
|
@@ -22854,11 +23217,11 @@ var buildMobileUpdate = async (args) => {
|
|
|
22854
23217
|
let expoExportDirectory;
|
|
22855
23218
|
try {
|
|
22856
23219
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
|
|
22857
|
-
const embedded = JSON.parse(await
|
|
22858
|
-
const runtimeFingerprint =
|
|
23220
|
+
const embedded = JSON.parse(await readFile17(join14(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
23221
|
+
const runtimeFingerprint = isRecord7(embedded) ? embedded.nativeRuntime : undefined;
|
|
22859
23222
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
22860
23223
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
22861
|
-
const privateKey = await
|
|
23224
|
+
const privateKey = await readFile17(resolve13(projectRoot, signingKeyPath));
|
|
22862
23225
|
const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
|
|
22863
23226
|
const derivedPublicKey = createPublicKey2(privateKey).export({
|
|
22864
23227
|
format: "der",
|
|
@@ -22879,7 +23242,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
22879
23242
|
channel: mobile.updates.channel,
|
|
22880
23243
|
classification,
|
|
22881
23244
|
keyId,
|
|
22882
|
-
outputDirectory: valueAfter(args, "--outdir") ??
|
|
23245
|
+
outputDirectory: valueAfter(args, "--outdir") ?? join14(projectRoot, ".absolutejs", "mobile", "updates"),
|
|
22883
23246
|
privateKey,
|
|
22884
23247
|
runtimeFingerprint
|
|
22885
23248
|
});
|
|
@@ -22891,7 +23254,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
22891
23254
|
return result;
|
|
22892
23255
|
} finally {
|
|
22893
23256
|
if (expoExportDirectory)
|
|
22894
|
-
await
|
|
23257
|
+
await rm7(expoExportDirectory, { force: true, recursive: true });
|
|
22895
23258
|
sendTelemetryEvent("mobile:update-build", {
|
|
22896
23259
|
classification: classification ?? "invalid",
|
|
22897
23260
|
durationMs: Math.round(performance.now() - startedAt),
|
|
@@ -22921,7 +23284,7 @@ var generateExpoUpdateSigning = async (args) => {
|
|
|
22921
23284
|
...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
|
|
22922
23285
|
...validityYears === undefined ? {} : { validityYears }
|
|
22923
23286
|
});
|
|
22924
|
-
const certificatePath =
|
|
23287
|
+
const certificatePath = relative12(projectRoot, result.certificatePath).replaceAll("\\", "/");
|
|
22925
23288
|
sendTelemetryEvent("mobile:update-code-signing-generated", {
|
|
22926
23289
|
engine: "expo",
|
|
22927
23290
|
validityYears: validityYears ?? 10
|
|
@@ -22983,7 +23346,7 @@ var provisionMobileUpdate = async (args) => {
|
|
|
22983
23346
|
publicKeys: mobile.updates.publicKeys,
|
|
22984
23347
|
storage: requestedStorage
|
|
22985
23348
|
});
|
|
22986
|
-
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${
|
|
23349
|
+
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative12(projectRoot, path).replaceAll("\\", "/")}.`);
|
|
22987
23350
|
if (requestedStorage === "local")
|
|
22988
23351
|
console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
|
|
22989
23352
|
else
|
|
@@ -23458,7 +23821,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
|
|
|
23458
23821
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
23459
23822
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
23460
23823
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
23461
|
-
console.log(`Metadata: ${
|
|
23824
|
+
console.log(`Metadata: ${join14(release.releaseRoot, "release.json")}`);
|
|
23462
23825
|
return release;
|
|
23463
23826
|
} finally {
|
|
23464
23827
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -23614,7 +23977,7 @@ var buildIos = async (args, prepareBuildNumber) => {
|
|
|
23614
23977
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
23615
23978
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
|
|
23616
23979
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
23617
|
-
console.log(`Metadata: ${
|
|
23980
|
+
console.log(`Metadata: ${join14(release.releaseRoot, "release.json")}`);
|
|
23618
23981
|
return release;
|
|
23619
23982
|
} finally {
|
|
23620
23983
|
stopListeningForCancellation();
|
|
@@ -23726,7 +24089,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
23726
24089
|
checks.push({
|
|
23727
24090
|
id: "sync.storage-schema",
|
|
23728
24091
|
label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
|
|
23729
|
-
path:
|
|
24092
|
+
path: join14(projectRoot, "package.json"),
|
|
23730
24093
|
platform: "host",
|
|
23731
24094
|
status: "pass"
|
|
23732
24095
|
});
|
|
@@ -23734,7 +24097,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
23734
24097
|
checks.push({
|
|
23735
24098
|
id: "sync.storage-schema",
|
|
23736
24099
|
label: "Offline schema metadata is invalid",
|
|
23737
|
-
path:
|
|
24100
|
+
path: join14(projectRoot, "package.json"),
|
|
23738
24101
|
platform: "host",
|
|
23739
24102
|
remediation: error instanceof Error ? error.message : String(error),
|
|
23740
24103
|
status: "fail"
|
|
@@ -23801,7 +24164,7 @@ AbsoluteJS can configure ${installPlatform} emulation:`);
|
|
|
23801
24164
|
Emulator setup verification:`);
|
|
23802
24165
|
printDoctorChecks(result.checks.filter((check) => check.platform === "host" || check.platform === installPlatform));
|
|
23803
24166
|
};
|
|
23804
|
-
var
|
|
24167
|
+
var captureCommand3 = (command) => {
|
|
23805
24168
|
const result = Bun.spawnSync(command, {
|
|
23806
24169
|
stderr: "pipe",
|
|
23807
24170
|
stdin: "ignore",
|
|
@@ -23822,7 +24185,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
|
|
|
23822
24185
|
}
|
|
23823
24186
|
return { https: args.includes("--https"), port };
|
|
23824
24187
|
}
|
|
23825
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
24188
|
+
const instances = listLiveInstances().filter((instance2) => resolve13(instance2.cwd) === resolve13(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
23826
24189
|
if (instances.length !== 1) {
|
|
23827
24190
|
throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for Android to report ready, then run `absolute mobile test android`." : "Multiple dev servers are running for this project. Select one with mobile test android --port <port>.");
|
|
23828
24191
|
}
|
|
@@ -23844,14 +24207,14 @@ var androidTestTimeout = (args) => {
|
|
|
23844
24207
|
};
|
|
23845
24208
|
var requireAndroidAdb = async () => {
|
|
23846
24209
|
const checks = await inspectAbsoluteMobileToolchain();
|
|
23847
|
-
const
|
|
23848
|
-
if (!
|
|
24210
|
+
const adb2 = checks.find((check) => check.id === "android.adb" && check.status === "pass")?.path;
|
|
24211
|
+
if (!adb2) {
|
|
23849
24212
|
throw new TypeError("Android Debug Bridge is unavailable. Run `absolute mobile doctor android --fix`.");
|
|
23850
24213
|
}
|
|
23851
|
-
return
|
|
24214
|
+
return adb2;
|
|
23852
24215
|
};
|
|
23853
|
-
var selectAndroidSerial = (
|
|
23854
|
-
const devices =
|
|
24216
|
+
var selectAndroidSerial = (adb2, explicitSerial) => {
|
|
24217
|
+
const devices = captureCommand3([adb2, "devices"]);
|
|
23855
24218
|
if (devices.exitCode !== 0) {
|
|
23856
24219
|
throw new Error(`Could not list Android targets: ${devices.stderr.trim() || devices.stdout.trim()}`);
|
|
23857
24220
|
}
|
|
@@ -23868,9 +24231,56 @@ var selectAndroidSerial = (adb, explicitSerial) => {
|
|
|
23868
24231
|
}
|
|
23869
24232
|
return selected;
|
|
23870
24233
|
};
|
|
24234
|
+
var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial, json) => {
|
|
24235
|
+
if (explicitSerial)
|
|
24236
|
+
return selectAndroidSerial(adb2, explicitSerial);
|
|
24237
|
+
const current = captureCommand3([adb2, "devices"]);
|
|
24238
|
+
const ready = current.exitCode === 0 ? parseAdbDevices(current.stdout).find((serial) => serial.startsWith("emulator-")) : undefined;
|
|
24239
|
+
if (ready)
|
|
24240
|
+
return ready;
|
|
24241
|
+
if (!emulator)
|
|
24242
|
+
throw new TypeError("Android Emulator is unavailable. Run `absolute mobile doctor android --fix`.");
|
|
24243
|
+
(json ? console.error : console.log)(`Starting managed Android emulator ${ABSOLUTE_ANDROID_AVD_NAME}\u2026`);
|
|
24244
|
+
const launched = Bun.spawn([
|
|
24245
|
+
emulator,
|
|
24246
|
+
"-avd",
|
|
24247
|
+
ABSOLUTE_ANDROID_AVD_NAME,
|
|
24248
|
+
"-netdelay",
|
|
24249
|
+
"none",
|
|
24250
|
+
"-netspeed",
|
|
24251
|
+
"full"
|
|
24252
|
+
], { stderr: "ignore", stdin: "ignore", stdout: "ignore" });
|
|
24253
|
+
launched.unref();
|
|
24254
|
+
const startedAt = performance.now();
|
|
24255
|
+
const bootedSerial = () => {
|
|
24256
|
+
const devices = captureCommand3([adb2, "devices"]);
|
|
24257
|
+
const serial = devices.exitCode === 0 ? parseAdbDevices(devices.stdout).find((candidate) => candidate.startsWith("emulator-")) : undefined;
|
|
24258
|
+
if (!serial)
|
|
24259
|
+
return;
|
|
24260
|
+
const booted = captureCommand3([
|
|
24261
|
+
adb2,
|
|
24262
|
+
"-s",
|
|
24263
|
+
serial,
|
|
24264
|
+
"shell",
|
|
24265
|
+
"getprop",
|
|
24266
|
+
"sys.boot_completed"
|
|
24267
|
+
]);
|
|
24268
|
+
return booted.exitCode === 0 && booted.stdout.trim() === "1" ? serial : undefined;
|
|
24269
|
+
};
|
|
24270
|
+
const waitForBoot = async () => {
|
|
24271
|
+
if (performance.now() - startedAt >= ANDROID_EMULATOR_BOOT_TIMEOUT_MS)
|
|
24272
|
+
throw new Error(`Managed Android emulator ${ABSOLUTE_ANDROID_AVD_NAME} did not finish booting within 180 seconds.`);
|
|
24273
|
+
const serial = bootedSerial();
|
|
24274
|
+
if (serial)
|
|
24275
|
+
return serial;
|
|
24276
|
+
await Bun.sleep(ANDROID_EMULATOR_BOOT_POLL_MS);
|
|
24277
|
+
return waitForBoot();
|
|
24278
|
+
};
|
|
24279
|
+
return waitForBoot();
|
|
24280
|
+
};
|
|
23871
24281
|
var safeArtifactRoot = (projectRoot, value) => {
|
|
23872
|
-
const root =
|
|
23873
|
-
if (root !== projectRoot && !root.startsWith(`${
|
|
24282
|
+
const root = resolve13(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
|
|
24283
|
+
if (root !== projectRoot && !root.startsWith(`${resolve13(projectRoot)}/`)) {
|
|
23874
24284
|
throw new TypeError("mobile test --artifacts must remain inside the project.");
|
|
23875
24285
|
}
|
|
23876
24286
|
return root;
|
|
@@ -23898,12 +24308,12 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
|
|
|
23898
24308
|
});
|
|
23899
24309
|
};
|
|
23900
24310
|
var writeAndroidFailureArtifacts = async (options) => {
|
|
23901
|
-
await
|
|
23902
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
24311
|
+
await mkdir13(options.artifactRoot, { recursive: true });
|
|
24312
|
+
const screenshot = options.session ? await options.session.screenshot(join14(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
23903
24313
|
return;
|
|
23904
24314
|
}) : undefined;
|
|
23905
|
-
const diagnosticPath =
|
|
23906
|
-
await
|
|
24315
|
+
const diagnosticPath = join14(options.artifactRoot, "android-failure.json");
|
|
24316
|
+
await writeFile15(diagnosticPath, `${JSON.stringify({
|
|
23907
24317
|
diagnostics: options.session?.diagnostics ?? [],
|
|
23908
24318
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
23909
24319
|
platform: "android",
|
|
@@ -23915,16 +24325,171 @@ var writeAndroidFailureArtifacts = async (options) => {
|
|
|
23915
24325
|
`);
|
|
23916
24326
|
return { diagnosticPath, screenshot };
|
|
23917
24327
|
};
|
|
24328
|
+
var captureAndroidReleaseScreenshot = async (adb2, serial, destination) => {
|
|
24329
|
+
const process2 = Bun.spawn([adb2, "-s", serial, "exec-out", "screencap", "-p"], { stderr: "pipe", stdin: "ignore", stdout: "pipe" });
|
|
24330
|
+
const [exitCode, bytes, stderr] = await Promise.all([
|
|
24331
|
+
process2.exited,
|
|
24332
|
+
new Response(process2.stdout).bytes(),
|
|
24333
|
+
new Response(process2.stderr).text()
|
|
24334
|
+
]);
|
|
24335
|
+
if (exitCode !== 0)
|
|
24336
|
+
throw new Error(`Android release screenshot failed: ${stderr.trim() || `status ${exitCode}`}`);
|
|
24337
|
+
await writeFile15(destination, bytes);
|
|
24338
|
+
return destination;
|
|
24339
|
+
};
|
|
24340
|
+
var printAndroidReleaseAcceptance = (result, json) => {
|
|
24341
|
+
if (json) {
|
|
24342
|
+
console.log(JSON.stringify(result, null, 2));
|
|
24343
|
+
return;
|
|
24344
|
+
}
|
|
24345
|
+
console.log(`\u2713 Installed immutable ${result.engine} release ${result.releaseId} with Bundletool in ${getDurationString(result.installMs)}.`);
|
|
24346
|
+
console.log(`\u2713 Embedded web content booted offline in ${getDurationString(result.launchMs)} and relaunched in ${getDurationString(result.relaunchMs)}.`);
|
|
24347
|
+
};
|
|
24348
|
+
var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
24349
|
+
const requested = valueAfter(args, "--release");
|
|
24350
|
+
if (!requested || requested.startsWith("--"))
|
|
24351
|
+
throw new TypeError("mobile test android --release requires a release directory or release.json path.");
|
|
24352
|
+
if (!mobile.platforms.includes("android"))
|
|
24353
|
+
throw new TypeError("mobile test android requires android in mobile.platforms.");
|
|
24354
|
+
if (mobile.engine !== "expo")
|
|
24355
|
+
throw new TypeError("mobile test android --release currently requires mobile.engine: expo; use the ordinary test command for Capacitor.");
|
|
24356
|
+
const release = await readAbsoluteAndroidRelease(projectRoot, requested);
|
|
24357
|
+
if (release.metadata.appId !== mobile.appId)
|
|
24358
|
+
throw new TypeError(`Android release app ID ${release.metadata.appId} does not match mobile.appId ${mobile.appId}.`);
|
|
24359
|
+
if (release.metadata.engine !== mobile.engine)
|
|
24360
|
+
throw new TypeError(`Android release engine ${release.metadata.engine} does not match configured engine ${mobile.engine}.`);
|
|
24361
|
+
if (mobile.engine === "expo" && mobile.expoNativeRoutes[mobile.entry])
|
|
24362
|
+
throw new TypeError("Offline Expo release acceptance requires mobile.entry to be an embedded web route; the configured entry is native and requires trusted server page data.");
|
|
24363
|
+
const checks = await inspectAbsoluteMobileToolchain();
|
|
24364
|
+
const adb2 = checks.find((check) => check.id === "android.adb" && check.status === "pass")?.path;
|
|
24365
|
+
const java = checks.find((check) => check.id === "android.java" && check.status === "pass")?.path;
|
|
24366
|
+
const emulator = checks.find((check) => check.id === "android.emulator" && check.status === "pass")?.path;
|
|
24367
|
+
if (!adb2)
|
|
24368
|
+
throw new TypeError("Android Debug Bridge is unavailable. Run `absolute mobile doctor android --fix`.");
|
|
24369
|
+
if (!java)
|
|
24370
|
+
throw new TypeError("Java is unavailable. Install the JDK required by the Android toolchain and rerun `absolute mobile doctor android`.");
|
|
24371
|
+
const serial = await selectOrStartAndroidReleaseEmulator(adb2, emulator, valueAfter(args, "--serial"), args.includes("--json"));
|
|
24372
|
+
const inspectedBundletool = await inspectAbsoluteBundletool();
|
|
24373
|
+
const approved = inspectedBundletool.ready || args.includes("--yes") || await confirmInstall(`Bundletool ${ABSOLUTE_BUNDLETOOL_VERSION} is required to install the exact App Bundle. Download and checksum-verify it now?`);
|
|
24374
|
+
const bundletool = await ensureAbsoluteBundletool({
|
|
24375
|
+
approved: approved === true
|
|
24376
|
+
});
|
|
24377
|
+
const reportRoot = nativeReportRoot(args, projectRoot, "android");
|
|
24378
|
+
const artifactRoot = reportRoot ?? safeArtifactRoot(projectRoot, valueAfter(args, "--artifacts") ?? `.absolutejs/mobile/test-artifacts/${release.metadata.releaseId}`);
|
|
24379
|
+
const startedAt = performance.now();
|
|
24380
|
+
try {
|
|
24381
|
+
const result = await runAbsoluteAndroidReleaseAcceptance({
|
|
24382
|
+
adb: adb2,
|
|
24383
|
+
artifactDirectory: artifactRoot,
|
|
24384
|
+
bundletool,
|
|
24385
|
+
host: detectAbsoluteMobileHost(),
|
|
24386
|
+
java,
|
|
24387
|
+
...mobile.deepLinkScheme ? {
|
|
24388
|
+
launchUrl: `${mobile.deepLinkScheme}://${mobile.entry}`
|
|
24389
|
+
} : {},
|
|
24390
|
+
release,
|
|
24391
|
+
serial
|
|
24392
|
+
});
|
|
24393
|
+
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join14(reportRoot, "android-release.png")) : undefined;
|
|
24394
|
+
sendTelemetryEvent("mobile:android-release-conformance", {
|
|
24395
|
+
durationMs: result.durationMs,
|
|
24396
|
+
engine: result.engine,
|
|
24397
|
+
installMs: result.installMs,
|
|
24398
|
+
launchMs: result.launchMs,
|
|
24399
|
+
platform: "android",
|
|
24400
|
+
relaunchMs: result.relaunchMs,
|
|
24401
|
+
success: true
|
|
24402
|
+
});
|
|
24403
|
+
await writeRequestedAndroidReport({
|
|
24404
|
+
adb: adb2,
|
|
24405
|
+
args,
|
|
24406
|
+
projectRoot,
|
|
24407
|
+
provider: mobile.engine,
|
|
24408
|
+
reportRoot,
|
|
24409
|
+
run: {
|
|
24410
|
+
appId: mobile.appId,
|
|
24411
|
+
durationMs: result.durationMs,
|
|
24412
|
+
hmrConnected: false,
|
|
24413
|
+
release: {
|
|
24414
|
+
apksBytes: result.apksBytes,
|
|
24415
|
+
artifactBytes: result.artifactBytes,
|
|
24416
|
+
artifactSha256: result.artifactSha256,
|
|
24417
|
+
embeddedOffline: result.embeddedOffline,
|
|
24418
|
+
engine: result.engine,
|
|
24419
|
+
installMs: result.installMs,
|
|
24420
|
+
launchMs: result.launchMs,
|
|
24421
|
+
relaunchMs: result.relaunchMs,
|
|
24422
|
+
releaseId: result.releaseId,
|
|
24423
|
+
signed: result.signed
|
|
24424
|
+
},
|
|
24425
|
+
...screenshot ? { screenshot } : {},
|
|
24426
|
+
serial,
|
|
24427
|
+
status: "pass"
|
|
24428
|
+
}
|
|
24429
|
+
});
|
|
24430
|
+
printAndroidReleaseAcceptance(result, args.includes("--json"));
|
|
24431
|
+
return result;
|
|
24432
|
+
} catch (error) {
|
|
24433
|
+
const durationMs = Math.round(performance.now() - startedAt);
|
|
24434
|
+
sendTelemetryEvent("mobile:android-release-conformance", {
|
|
24435
|
+
durationMs,
|
|
24436
|
+
engine: mobile.engine,
|
|
24437
|
+
platform: "android",
|
|
24438
|
+
success: false
|
|
24439
|
+
});
|
|
24440
|
+
await mkdir13(artifactRoot, { recursive: true });
|
|
24441
|
+
const diagnosticPath = join14(artifactRoot, "android-release-failure.json");
|
|
24442
|
+
await writeFile15(diagnosticPath, `${JSON.stringify({
|
|
24443
|
+
error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
|
|
24444
|
+
platform: "android",
|
|
24445
|
+
provider: mobile.engine,
|
|
24446
|
+
releaseId: release.metadata.releaseId,
|
|
24447
|
+
status: "fail"
|
|
24448
|
+
}, null, 2)}
|
|
24449
|
+
`);
|
|
24450
|
+
await writeRequestedAndroidReport({
|
|
24451
|
+
adb: adb2,
|
|
24452
|
+
args,
|
|
24453
|
+
projectRoot,
|
|
24454
|
+
provider: mobile.engine,
|
|
24455
|
+
reportRoot,
|
|
24456
|
+
run: {
|
|
24457
|
+
appId: mobile.appId,
|
|
24458
|
+
durationMs,
|
|
24459
|
+
error: "Installed release acceptance failed; inspect the local diagnostic artifact before sharing evidence.",
|
|
24460
|
+
hmrConnected: false,
|
|
24461
|
+
release: {
|
|
24462
|
+
apksBytes: 0,
|
|
24463
|
+
artifactBytes: release.metadata.bytes,
|
|
24464
|
+
artifactSha256: release.metadata.sha256,
|
|
24465
|
+
embeddedOffline: false,
|
|
24466
|
+
engine: release.metadata.engine,
|
|
24467
|
+
installMs: 0,
|
|
24468
|
+
launchMs: 0,
|
|
24469
|
+
relaunchMs: 0,
|
|
24470
|
+
releaseId: release.metadata.releaseId,
|
|
24471
|
+
signed: release.metadata.signed
|
|
24472
|
+
},
|
|
24473
|
+
serial,
|
|
24474
|
+
status: "fail"
|
|
24475
|
+
}
|
|
24476
|
+
});
|
|
24477
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}`, { cause: error });
|
|
24478
|
+
}
|
|
24479
|
+
};
|
|
23918
24480
|
var testAndroid = async (args) => {
|
|
23919
24481
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
24482
|
+
if (args.includes("--release")) {
|
|
24483
|
+
return testAndroidRelease(args, mobile, projectRoot);
|
|
24484
|
+
}
|
|
23920
24485
|
requireCapacitorEngine(mobile, "mobile test android");
|
|
23921
24486
|
if (!mobile.platforms.includes("android")) {
|
|
23922
24487
|
throw new TypeError("mobile test android requires android in mobile.platforms.");
|
|
23923
24488
|
}
|
|
23924
24489
|
const { https, port } = requireAndroidTestPort(args, projectRoot);
|
|
23925
24490
|
const timeoutMs = androidTestTimeout(args);
|
|
23926
|
-
const
|
|
23927
|
-
const serial = selectAndroidSerial(
|
|
24491
|
+
const adb2 = await requireAndroidAdb();
|
|
24492
|
+
const serial = selectAndroidSerial(adb2, valueAfter(args, "--serial"));
|
|
23928
24493
|
const routes = valuesAfter(args, "--route");
|
|
23929
24494
|
if (routes.length === 0)
|
|
23930
24495
|
routes.push(mobile.entry);
|
|
@@ -23934,7 +24499,7 @@ var testAndroid = async (args) => {
|
|
|
23934
24499
|
let session;
|
|
23935
24500
|
try {
|
|
23936
24501
|
session = await attachAbsoluteAndroidWebView({
|
|
23937
|
-
adb,
|
|
24502
|
+
adb: adb2,
|
|
23938
24503
|
appId: mobile.appId,
|
|
23939
24504
|
serial,
|
|
23940
24505
|
timeoutMs
|
|
@@ -23968,9 +24533,9 @@ var testAndroid = async (args) => {
|
|
|
23968
24533
|
console.log(JSON.stringify(report, null, 2));
|
|
23969
24534
|
else
|
|
23970
24535
|
printAndroidTestReport(report);
|
|
23971
|
-
const screenshot = reportRoot ? await session.screenshot(
|
|
24536
|
+
const screenshot = reportRoot ? await session.screenshot(join14(artifactRoot, "android-emulator.png")) : undefined;
|
|
23972
24537
|
await writeRequestedAndroidReport({
|
|
23973
|
-
adb,
|
|
24538
|
+
adb: adb2,
|
|
23974
24539
|
args,
|
|
23975
24540
|
projectRoot,
|
|
23976
24541
|
run: {
|
|
@@ -24011,7 +24576,7 @@ var testAndroid = async (args) => {
|
|
|
24011
24576
|
session
|
|
24012
24577
|
});
|
|
24013
24578
|
await writeRequestedAndroidReport({
|
|
24014
|
-
adb,
|
|
24579
|
+
adb: adb2,
|
|
24015
24580
|
args,
|
|
24016
24581
|
projectRoot,
|
|
24017
24582
|
run: {
|
|
@@ -24037,14 +24602,14 @@ var requireIosTestContext = (args, projectRoot) => {
|
|
|
24037
24602
|
const port = Number(explicit);
|
|
24038
24603
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
24039
24604
|
throw new TypeError("mobile test --port must be a valid TCP port.");
|
|
24040
|
-
const instance2 = listLiveInstances().find((candidate) =>
|
|
24605
|
+
const instance2 = listLiveInstances().find((candidate) => resolve13(candidate.cwd) === resolve13(projectRoot) && candidate.source === "dev" && candidate.port === port);
|
|
24041
24606
|
return {
|
|
24042
24607
|
https: instance2?.https ?? args.includes("--https"),
|
|
24043
24608
|
instance: instance2,
|
|
24044
24609
|
port
|
|
24045
24610
|
};
|
|
24046
24611
|
}
|
|
24047
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
24612
|
+
const instances = listLiveInstances().filter((instance2) => resolve13(instance2.cwd) === resolve13(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
24048
24613
|
if (instances.length !== 1)
|
|
24049
24614
|
throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for iOS to report ready, then run `absolute mobile test ios`." : "Multiple dev servers are running for this project. Select one with mobile test ios --port <port>.");
|
|
24050
24615
|
const [instance] = instances;
|
|
@@ -24062,7 +24627,7 @@ var waitForIosHmrClient = async (options) => {
|
|
|
24062
24627
|
return;
|
|
24063
24628
|
});
|
|
24064
24629
|
const status = response?.ok ? await response.json().catch(() => null) : null;
|
|
24065
|
-
const targets =
|
|
24630
|
+
const targets = isRecord7(status) && isRecord7(status.connectedTargets) ? status.connectedTargets : undefined;
|
|
24066
24631
|
if (targets && typeof targets["capacitor-ios"] === "number" && targets["capacitor-ios"] > 0)
|
|
24067
24632
|
return;
|
|
24068
24633
|
await Bun.sleep(100);
|
|
@@ -24134,11 +24699,11 @@ var physicalIosCapture = async (options) => {
|
|
|
24134
24699
|
remote: false,
|
|
24135
24700
|
xcodeVersion: requireCapturedCommand(["/usr/bin/xcodebuild", "-version"], "Xcode version inspection").stdout.trim(),
|
|
24136
24701
|
xcrun: "/usr/bin/xcrun",
|
|
24137
|
-
capture: async (command) =>
|
|
24702
|
+
capture: async (command) => captureCommand3(command)
|
|
24138
24703
|
};
|
|
24139
24704
|
};
|
|
24140
24705
|
var selectIosSimulator = (xcrun, explicitUdid) => {
|
|
24141
|
-
const result =
|
|
24706
|
+
const result = captureCommand3([
|
|
24142
24707
|
xcrun,
|
|
24143
24708
|
"simctl",
|
|
24144
24709
|
"list",
|
|
@@ -24161,15 +24726,15 @@ var selectIosSimulator = (xcrun, explicitUdid) => {
|
|
|
24161
24726
|
return selected;
|
|
24162
24727
|
};
|
|
24163
24728
|
var requireCapturedCommand = (command, label) => {
|
|
24164
|
-
const result =
|
|
24729
|
+
const result = captureCommand3(command);
|
|
24165
24730
|
if (result.exitCode !== 0)
|
|
24166
24731
|
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
24167
24732
|
return result;
|
|
24168
24733
|
};
|
|
24169
24734
|
var writeIosFailureArtifacts = async (options) => {
|
|
24170
|
-
await
|
|
24171
|
-
const screenshot =
|
|
24172
|
-
const screenshotResult =
|
|
24735
|
+
await mkdir13(options.artifactRoot, { recursive: true });
|
|
24736
|
+
const screenshot = join14(options.artifactRoot, "ios-failure.png");
|
|
24737
|
+
const screenshotResult = captureCommand3([
|
|
24173
24738
|
options.xcrun,
|
|
24174
24739
|
"simctl",
|
|
24175
24740
|
"io",
|
|
@@ -24177,8 +24742,8 @@ var writeIosFailureArtifacts = async (options) => {
|
|
|
24177
24742
|
"screenshot",
|
|
24178
24743
|
screenshot
|
|
24179
24744
|
]);
|
|
24180
|
-
const diagnosticPath =
|
|
24181
|
-
await
|
|
24745
|
+
const diagnosticPath = join14(options.artifactRoot, "ios-failure.json");
|
|
24746
|
+
await writeFile15(diagnosticPath, `${JSON.stringify({
|
|
24182
24747
|
appId: options.appId,
|
|
24183
24748
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
24184
24749
|
platform: "ios",
|
|
@@ -24205,8 +24770,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
|
|
|
24205
24770
|
var absolutejsVersionForReport = async () => {
|
|
24206
24771
|
let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
|
|
24207
24772
|
const versions = await Promise.all([
|
|
24208
|
-
|
|
24209
|
-
|
|
24773
|
+
resolve13(import.meta.dir, "..", "..", "package.json"),
|
|
24774
|
+
resolve13(import.meta.dir, "..", "..", "..", "package.json")
|
|
24210
24775
|
].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
|
|
24211
24776
|
for (const version of versions) {
|
|
24212
24777
|
if (version === "unknown")
|
|
@@ -24217,7 +24782,7 @@ var absolutejsVersionForReport = async () => {
|
|
|
24217
24782
|
return absolutejsVersion;
|
|
24218
24783
|
};
|
|
24219
24784
|
var writeRequestedAndroidReport = async (options) => {
|
|
24220
|
-
const reportRoot = nativeReportRoot(options.args, options.projectRoot, "android");
|
|
24785
|
+
const reportRoot = options.reportRoot ?? nativeReportRoot(options.args, options.projectRoot, "android");
|
|
24221
24786
|
if (!reportRoot)
|
|
24222
24787
|
return;
|
|
24223
24788
|
const adbVersion = requireCapturedCommand([options.adb, "version"], "ADB version inspection").stdout.trim();
|
|
@@ -24226,6 +24791,7 @@ var writeRequestedAndroidReport = async (options) => {
|
|
|
24226
24791
|
adbVersion,
|
|
24227
24792
|
bunVersion: Bun.version,
|
|
24228
24793
|
host: `${process.platform}-${process.arch}`,
|
|
24794
|
+
...options.provider ? { provider: options.provider } : {},
|
|
24229
24795
|
run: options.run
|
|
24230
24796
|
});
|
|
24231
24797
|
const paths = await writeAbsoluteNativeTestReport(reportRoot, report);
|
|
@@ -24430,8 +24996,8 @@ var testIos = async (args) => {
|
|
|
24430
24996
|
mobile.appId
|
|
24431
24997
|
], "iOS app launch");
|
|
24432
24998
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
24433
|
-
await
|
|
24434
|
-
const screenshot =
|
|
24999
|
+
await mkdir13(artifactRoot, { recursive: true });
|
|
25000
|
+
const screenshot = join14(artifactRoot, "ios-simulator.png");
|
|
24435
25001
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
24436
25002
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
24437
25003
|
const report = {
|
|
@@ -24617,7 +25183,7 @@ var runMobile = async (args) => {
|
|
|
24617
25183
|
await publishIos(args.slice(2));
|
|
24618
25184
|
return;
|
|
24619
25185
|
}
|
|
24620
|
-
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path
|
|
25186
|
+
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path]");
|
|
24621
25187
|
};
|
|
24622
25188
|
export {
|
|
24623
25189
|
runMobile
|