@absolutejs/absolute 0.20.0-beta.1 → 0.20.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/index.js +763 -58
- package/dist/index.js +11 -1
- package/dist/index.js.map +3 -3
- package/dist/mobile/index.js +640 -151
- package/dist/mobile/index.js.map +8 -6
- package/dist/src/mobile/androidEmulatorController.d.ts +2 -1
- package/dist/src/mobile/androidRelease.d.ts +4 -0
- package/dist/src/mobile/config.d.ts +1 -0
- package/dist/src/mobile/index.d.ts +2 -0
- package/dist/src/mobile/iosRelease.d.ts +61 -0
- package/dist/src/mobile/releasePublisher.d.ts +130 -0
- package/dist/types/build.d.ts +4 -0
- package/package.json +7 -7
package/dist/cli/index.js
CHANGED
|
@@ -774,6 +774,14 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
774
774
|
throw new TypeError("mobile.deepLinks.apple.appIdPrefix must contain ten letters or digits.");
|
|
775
775
|
}
|
|
776
776
|
return normalized;
|
|
777
|
+
}, normalizeIosVersion = (value) => {
|
|
778
|
+
if (value === undefined)
|
|
779
|
+
return;
|
|
780
|
+
const normalized = requireText(value, "mobile.ios.version");
|
|
781
|
+
if (!/^\d+(?:\.\d+){0,2}$/u.test(normalized)) {
|
|
782
|
+
throw new TypeError("mobile.ios.version must contain one to three dot-separated integer components, for example 1.4.0.");
|
|
783
|
+
}
|
|
784
|
+
return normalized;
|
|
777
785
|
}, normalizeCertificateFingerprints = (values) => [
|
|
778
786
|
...new Set((values ?? []).map((value) => requireText(value, "mobile.deepLinks.android.sha256CertificateFingerprints").replaceAll(":", "").toUpperCase()).map((value) => {
|
|
779
787
|
if (!CERTIFICATE_FINGERPRINT_PATTERN.test(value)) {
|
|
@@ -801,6 +809,7 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
801
809
|
deepLinkScheme,
|
|
802
810
|
engine: "capacitor",
|
|
803
811
|
entry: normalizeEntry(config.entry),
|
|
812
|
+
iosVersion: normalizeIosVersion(config.ios?.version),
|
|
804
813
|
nativeProjectDirectory: resolveProjectPath(projectRoot, config.nativeProject?.directory ?? "mobile", "mobile.nativeProject.directory"),
|
|
805
814
|
platforms: normalizePlatforms(config.platforms),
|
|
806
815
|
productionOrigin
|
|
@@ -1643,12 +1652,13 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
|
|
|
1643
1652
|
throw new Error("Capacitor Android settings did not declare any native dependencies.");
|
|
1644
1653
|
}
|
|
1645
1654
|
return { dependencies, rewrittenSettings };
|
|
1646
|
-
}, encodedWindowsGradleCommand = (windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task) => {
|
|
1655
|
+
}, encodedWindowsGradleCommand = (windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task, gradleArguments) => {
|
|
1647
1656
|
const sourceDirectory = Buffer.from(windowsSource, "utf8").toString("base64");
|
|
1648
1657
|
const buildDirectory = Buffer.from(windowsDirectory, "utf8").toString("base64");
|
|
1649
1658
|
const androidRoot = Buffer.from(windowsAndroidRoot, "utf8").toString("base64");
|
|
1650
1659
|
const dependencyData = Buffer.from(JSON.stringify(dependencies), "utf8").toString("base64");
|
|
1651
1660
|
const settingsData = Buffer.from(rewrittenSettings, "utf8").toString("base64");
|
|
1661
|
+
const argumentsData = Buffer.from(JSON.stringify(gradleArguments), "utf8").toString("base64");
|
|
1652
1662
|
const source = [
|
|
1653
1663
|
"$ErrorActionPreference = 'Stop'",
|
|
1654
1664
|
`$source = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${sourceDirectory}'))`,
|
|
@@ -1656,6 +1666,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
|
|
|
1656
1666
|
`$androidHome = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${androidRoot}'))`,
|
|
1657
1667
|
`$dependencies = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${dependencyData}')) | ConvertFrom-Json`,
|
|
1658
1668
|
`$settings = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${settingsData}'))`,
|
|
1669
|
+
`$gradleArguments = @([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${argumentsData}')) | ConvertFrom-Json)`,
|
|
1659
1670
|
"$env:ANDROID_HOME = $androidHome",
|
|
1660
1671
|
"$env:ANDROID_SDK_ROOT = $androidHome",
|
|
1661
1672
|
"New-Item -ItemType Directory -Force -Path $directory | Out-Null",
|
|
@@ -1665,7 +1676,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
|
|
|
1665
1676
|
"foreach ($dependency in @($dependencies)) { $target = Join-Path $directory ('.absolutejs-dependencies\\' + $dependency.name); New-Item -ItemType Directory -Force -Path $target | Out-Null; & robocopy.exe $dependency.windowsSource $target /MIR /XD .gradle build /NFL /NDL /NJH /NJS /NP; if ($LASTEXITCODE -ge 8) { exit $LASTEXITCODE } }",
|
|
1666
1677
|
"[IO.File]::WriteAllText((Join-Path $directory 'capacitor.settings.gradle'), $settings)",
|
|
1667
1678
|
"$wrapper = Join-Path $directory 'gradlew.bat'",
|
|
1668
|
-
`& $wrapper --no-daemon --console=plain -p $directory ${task}`,
|
|
1679
|
+
`& $wrapper --no-daemon --console=plain -p $directory @gradleArguments ${task}`,
|
|
1669
1680
|
"if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }",
|
|
1670
1681
|
"exit 0"
|
|
1671
1682
|
].join("; ");
|
|
@@ -1690,6 +1701,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
|
|
|
1690
1701
|
const capture = options.capture ?? captureCommand2;
|
|
1691
1702
|
const run = options.run ?? runCommand;
|
|
1692
1703
|
const env = options.env ?? process.env;
|
|
1704
|
+
const gradleArguments = options.gradleArguments ?? [];
|
|
1693
1705
|
if (project.host === "wsl") {
|
|
1694
1706
|
const windowsSource = windowsPathFromWsl(project.nativeDirectory, capture);
|
|
1695
1707
|
const buildId = Bun.hash(project.projectRoot).toString(HASH_RADIX);
|
|
@@ -1701,7 +1713,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
|
|
|
1701
1713
|
"powershell.exe",
|
|
1702
1714
|
"-NoProfile",
|
|
1703
1715
|
"-EncodedCommand",
|
|
1704
|
-
encodedWindowsGradleCommand(windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task)
|
|
1716
|
+
encodedWindowsGradleCommand(windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task, gradleArguments)
|
|
1705
1717
|
], "Android Gradle build", run, { env, signal: options.signal });
|
|
1706
1718
|
const artifactPath2 = await resolveGradleArtifactPath(managedBuildDirectory, task);
|
|
1707
1719
|
const unsigned = artifactPath2.endsWith("-unsigned.apk");
|
|
@@ -1711,7 +1723,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
|
|
|
1711
1723
|
};
|
|
1712
1724
|
}
|
|
1713
1725
|
const wrapper = project.host === "windows" ? "gradlew.bat" : "./gradlew";
|
|
1714
|
-
await requireSuccess([wrapper, "--no-daemon", "--console=plain", task], "Android Gradle build", run, { cwd: project.nativeDirectory, env, signal: options.signal });
|
|
1726
|
+
await requireSuccess([wrapper, "--no-daemon", "--console=plain", ...gradleArguments, task], "Android Gradle build", run, { cwd: project.nativeDirectory, env, signal: options.signal });
|
|
1715
1727
|
const artifactPath = await resolveGradleArtifactPath(project.nativeDirectory, task);
|
|
1716
1728
|
return { artifactPath, installPath: artifactPath };
|
|
1717
1729
|
}, buildAndroidDebugApp = async (project, capture, run, env, signal) => (await buildAbsoluteAndroidGradleArtifact({
|
|
@@ -14523,10 +14535,40 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists3 = async (path) => {
|
|
|
14523
14535
|
...check2,
|
|
14524
14536
|
path: check2.path ? relative19(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
|
|
14525
14537
|
}));
|
|
14538
|
+
}, inspectIosRelease = async (config, projectRoot) => {
|
|
14539
|
+
const iosAppRoot = join41(config.nativeProjectDirectory, "ios", "App", "App");
|
|
14540
|
+
const nativeConfigPath = join41(iosAppRoot, "capacitor.config.json");
|
|
14541
|
+
const infoPath = join41(iosAppRoot, "Info.plist");
|
|
14542
|
+
const publicRoot = join41(iosAppRoot, "public");
|
|
14543
|
+
const checks = [];
|
|
14544
|
+
if (!config.iosVersion) {
|
|
14545
|
+
checks.push(fail5("ios.marketing-version", "iOS has no explicit App Store marketing version.", projectRoot, "Add mobile.ios.version to absolutejs.config.ts, for example 1.0.0."));
|
|
14546
|
+
} else {
|
|
14547
|
+
checks.push(pass("ios.marketing-version", `The iOS marketing version is ${config.iosVersion}.`));
|
|
14548
|
+
}
|
|
14549
|
+
if (!await pathExists3(nativeConfigPath)) {
|
|
14550
|
+
checks.push(fail5("ios.capacitor-config", "The generated iOS Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync ios` before release validation."));
|
|
14551
|
+
} else if (isUnsafeCapacitorConfig(await readFile10(nativeConfigPath, "utf8"))) {
|
|
14552
|
+
checks.push(fail5("ios.capacitor-config", "iOS Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync ios`; do not ship development transport overrides."));
|
|
14553
|
+
} else {
|
|
14554
|
+
checks.push(pass("ios.capacitor-config", "iOS Capacitor config contains no development transport overrides.", nativeConfigPath));
|
|
14555
|
+
}
|
|
14556
|
+
if (!await pathExists3(infoPath)) {
|
|
14557
|
+
checks.push(fail5("ios.transport-security", "The iOS Info.plist is missing.", infoPath, "Run `absolute mobile sync ios` before release validation."));
|
|
14558
|
+
} else {
|
|
14559
|
+
const info2 = await readFile10(infoPath, "utf8");
|
|
14560
|
+
checks.push(/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info2) ? fail5("ios.transport-security", "iOS App Transport Security permits arbitrary network loads.", infoPath, "Remove NSAllowsArbitraryLoads from the release Info.plist.") : pass("ios.transport-security", "iOS App Transport Security does not permit arbitrary loads.", infoPath));
|
|
14561
|
+
}
|
|
14562
|
+
const hmrAsset = await findHmrAsset(publicRoot);
|
|
14563
|
+
checks.push(hmrAsset ? fail5("ios.hmr-assets", "A packaged iOS asset contains the development HMR client.", hmrAsset, "Rebuild the production mobile bundle and run Capacitor sync again.") : pass("ios.hmr-assets", "Packaged iOS assets contain no development HMR markers.", publicRoot));
|
|
14564
|
+
return checks.map((check2) => ({
|
|
14565
|
+
...check2,
|
|
14566
|
+
path: check2.path ? relative19(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
|
|
14567
|
+
}));
|
|
14526
14568
|
}, inspectAbsoluteMobileRelease = async (config, projectRoot) => {
|
|
14527
14569
|
const checks = config.platforms.includes("android") ? await inspectAndroidRelease(config, projectRoot) : [];
|
|
14528
14570
|
if (config.platforms.includes("ios")) {
|
|
14529
|
-
checks.push(
|
|
14571
|
+
checks.push(...await inspectIosRelease(config, projectRoot));
|
|
14530
14572
|
}
|
|
14531
14573
|
return {
|
|
14532
14574
|
checks,
|
|
@@ -14651,12 +14693,32 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord11 = (value) => typeof value ==
|
|
|
14651
14693
|
}
|
|
14652
14694
|
return { ...expected, artifact };
|
|
14653
14695
|
}, buildAbsoluteAndroidRelease = async (options) => {
|
|
14696
|
+
if (options.versionCode !== undefined && options.prepareVersionCode) {
|
|
14697
|
+
throw new TypeError("Android release versionCode and prepareVersionCode cannot be combined.");
|
|
14698
|
+
}
|
|
14699
|
+
if (options.versionCode !== undefined && (!Number.isSafeInteger(options.versionCode) || options.versionCode < 1 || options.versionCode > 2100000000)) {
|
|
14700
|
+
throw new TypeError("Android versionCode must be an integer from 1 through 2100000000.");
|
|
14701
|
+
}
|
|
14654
14702
|
const projectRoot = resolve35(options.projectRoot);
|
|
14655
14703
|
const host = options.host ?? detectAbsoluteMobileHost();
|
|
14656
14704
|
const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host);
|
|
14657
14705
|
const nativeDirectory = join42(options.config.nativeProjectDirectory, "android");
|
|
14706
|
+
const manifest = requireManifest(JSON.parse(await readFile11(join42(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
14707
|
+
if (manifest.appId !== options.config.appId) {
|
|
14708
|
+
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
14709
|
+
}
|
|
14710
|
+
let { versionCode } = options;
|
|
14711
|
+
if (options.prepareVersionCode) {
|
|
14712
|
+
const nativeFingerprint = await fingerprintAbsoluteAndroidNativeProject({ nativeDirectory });
|
|
14713
|
+
const buildIdentity = createHash9("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}`).digest("hex");
|
|
14714
|
+
versionCode = await options.prepareVersionCode(buildIdentity);
|
|
14715
|
+
}
|
|
14716
|
+
if (versionCode !== undefined && (!Number.isSafeInteger(versionCode) || versionCode < 1 || versionCode > 2100000000)) {
|
|
14717
|
+
throw new TypeError("Android versionCode must be an integer from 1 through 2100000000.");
|
|
14718
|
+
}
|
|
14658
14719
|
const { artifactPath } = await buildAbsoluteAndroidGradleArtifact({
|
|
14659
14720
|
capture: options.capture,
|
|
14721
|
+
gradleArguments: versionCode === undefined ? [] : [`-Pandroid.injected.version.code=${versionCode}`],
|
|
14660
14722
|
project: {
|
|
14661
14723
|
androidRoot,
|
|
14662
14724
|
config: options.config,
|
|
@@ -14678,10 +14740,6 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord11 = (value) => typeof value ==
|
|
|
14678
14740
|
if (!signed2 && !options.allowUnsigned) {
|
|
14679
14741
|
throw new TypeError("Android Gradle produced an unsigned App Bundle. Configure the release signingConfig in the source-owned Android project (prefer external Gradle properties), or pass --unsigned only for a non-publishable build.");
|
|
14680
14742
|
}
|
|
14681
|
-
const manifest = requireManifest(JSON.parse(await readFile11(join42(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
14682
|
-
if (manifest.appId !== options.config.appId) {
|
|
14683
|
-
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
14684
|
-
}
|
|
14685
14743
|
const [bytes, sha2562] = await Promise.all([
|
|
14686
14744
|
stat(artifactPath).then(({ size }) => size),
|
|
14687
14745
|
sha256File(artifactPath)
|
|
@@ -14698,7 +14756,8 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord11 = (value) => typeof value ==
|
|
|
14698
14756
|
runtime: manifest.runtime,
|
|
14699
14757
|
sha256: sha2562,
|
|
14700
14758
|
signed: signed2 === true,
|
|
14701
|
-
type: "aab"
|
|
14759
|
+
type: "aab",
|
|
14760
|
+
...versionCode === undefined ? {} : { versionCode }
|
|
14702
14761
|
};
|
|
14703
14762
|
return installRelease(artifactPath, metadata, safeOutputDirectory(projectRoot, options.outputDirectory));
|
|
14704
14763
|
};
|
|
@@ -14707,13 +14766,377 @@ var init_androidRelease = __esm(() => {
|
|
|
14707
14766
|
init_androidEmulatorController();
|
|
14708
14767
|
});
|
|
14709
14768
|
|
|
14769
|
+
// src/mobile/iosRelease.ts
|
|
14770
|
+
import { createHash as createHash10 } from "crypto";
|
|
14771
|
+
import {
|
|
14772
|
+
access as access8,
|
|
14773
|
+
copyFile as copyFile4,
|
|
14774
|
+
mkdir as mkdir9,
|
|
14775
|
+
mkdtemp as mkdtemp5,
|
|
14776
|
+
readdir as readdir4,
|
|
14777
|
+
readFile as readFile12,
|
|
14778
|
+
rename as rename8,
|
|
14779
|
+
rm as rm7,
|
|
14780
|
+
stat as stat2,
|
|
14781
|
+
writeFile as writeFile9
|
|
14782
|
+
} from "fs/promises";
|
|
14783
|
+
import { dirname as dirname25, isAbsolute as isAbsolute5, join as join43, relative as relative21, resolve as resolve36, sep as sep4 } from "path";
|
|
14784
|
+
var isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest2 = (value) => {
|
|
14785
|
+
if (!isRecord12(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
|
|
14786
|
+
throw new TypeError("Invalid embedded AbsoluteJS mobile manifest.");
|
|
14787
|
+
}
|
|
14788
|
+
return {
|
|
14789
|
+
appBuild: value.appBuild,
|
|
14790
|
+
appId: value.appId,
|
|
14791
|
+
runtime: value.runtime
|
|
14792
|
+
};
|
|
14793
|
+
}, pathExists5 = async (path) => {
|
|
14794
|
+
try {
|
|
14795
|
+
await access8(path);
|
|
14796
|
+
return true;
|
|
14797
|
+
} catch {
|
|
14798
|
+
return false;
|
|
14799
|
+
}
|
|
14800
|
+
}, defaultRun2 = async (command, options = {}) => {
|
|
14801
|
+
const process2 = Bun.spawn(command, {
|
|
14802
|
+
cwd: options.cwd,
|
|
14803
|
+
env: options.env,
|
|
14804
|
+
stderr: "inherit",
|
|
14805
|
+
stdin: "inherit",
|
|
14806
|
+
stdout: "inherit"
|
|
14807
|
+
});
|
|
14808
|
+
return process2.exited;
|
|
14809
|
+
}, defaultCapture3 = (command, options = {}) => {
|
|
14810
|
+
try {
|
|
14811
|
+
const result = Bun.spawnSync(command, {
|
|
14812
|
+
cwd: options.cwd,
|
|
14813
|
+
env: options.env,
|
|
14814
|
+
stderr: "pipe",
|
|
14815
|
+
stdin: "ignore",
|
|
14816
|
+
stdout: "pipe"
|
|
14817
|
+
});
|
|
14818
|
+
return {
|
|
14819
|
+
exitCode: result.exitCode,
|
|
14820
|
+
stderr: result.stderr.toString(),
|
|
14821
|
+
stdout: result.stdout.toString()
|
|
14822
|
+
};
|
|
14823
|
+
} catch (error) {
|
|
14824
|
+
return {
|
|
14825
|
+
exitCode: 1,
|
|
14826
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
14827
|
+
stdout: ""
|
|
14828
|
+
};
|
|
14829
|
+
}
|
|
14830
|
+
}, ignoredFingerprintDirectories, fingerprintFiles = async (root, current = root) => {
|
|
14831
|
+
const entries = await readdir4(current, { withFileTypes: true });
|
|
14832
|
+
const nested = await Promise.all(entries.sort((left, right) => left.name.localeCompare(right.name)).map(async (entry) => {
|
|
14833
|
+
const path = join43(current, entry.name);
|
|
14834
|
+
const projectRelative = relative21(root, path).replaceAll("\\", "/");
|
|
14835
|
+
const ignored = entry.isDirectory() && (ignoredFingerprintDirectories.has(entry.name) || projectRelative === "App/App/public");
|
|
14836
|
+
if (ignored)
|
|
14837
|
+
return [];
|
|
14838
|
+
if (entry.isDirectory())
|
|
14839
|
+
return fingerprintFiles(root, path);
|
|
14840
|
+
return entry.isFile() ? [path] : [];
|
|
14841
|
+
}));
|
|
14842
|
+
return nested.flat();
|
|
14843
|
+
}, fingerprintAbsoluteIosNativeProject = async (nativeDirectory) => {
|
|
14844
|
+
const hasher = createHash10("sha256");
|
|
14845
|
+
const files = await fingerprintFiles(nativeDirectory);
|
|
14846
|
+
const contents = await Promise.all(files.map((file) => readFile12(file)));
|
|
14847
|
+
files.forEach((file, index) => {
|
|
14848
|
+
hasher.update(relative21(nativeDirectory, file).replaceAll("\\", "/"));
|
|
14849
|
+
hasher.update("\x00");
|
|
14850
|
+
hasher.update(contents[index] ?? new Uint8Array);
|
|
14851
|
+
hasher.update("\x00");
|
|
14852
|
+
});
|
|
14853
|
+
return hasher.digest("hex");
|
|
14854
|
+
}, safeOutputDirectory2 = (projectRoot, requested) => {
|
|
14855
|
+
const root = resolve36(projectRoot);
|
|
14856
|
+
const output = resolve36(root, requested ?? ".absolutejs/mobile/releases/ios");
|
|
14857
|
+
const projectRelative = relative21(root, output);
|
|
14858
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep4}`) || isAbsolute5(projectRelative)) {
|
|
14859
|
+
throw new TypeError("mobile build --outdir must remain inside the project.");
|
|
14860
|
+
}
|
|
14861
|
+
return output;
|
|
14862
|
+
}, sha256File2 = async (path) => createHash10("sha256").update(await readFile12(path)).digest("hex"), findByExtension = async (root, extension) => {
|
|
14863
|
+
if (!await pathExists5(root))
|
|
14864
|
+
return;
|
|
14865
|
+
const entries = await readdir4(root, { withFileTypes: true });
|
|
14866
|
+
const matches = await Promise.all(entries.map(async (entry) => {
|
|
14867
|
+
const path = join43(root, entry.name);
|
|
14868
|
+
if (entry.isDirectory() && entry.name.endsWith(extension))
|
|
14869
|
+
return path;
|
|
14870
|
+
if (entry.isFile() && entry.name.endsWith(extension))
|
|
14871
|
+
return path;
|
|
14872
|
+
return entry.isDirectory() ? findByExtension(path, extension) : undefined;
|
|
14873
|
+
}));
|
|
14874
|
+
return matches.find((match) => match !== undefined);
|
|
14875
|
+
}, exportOptions = () => `<?xml version="1.0" encoding="UTF-8"?>
|
|
14876
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
14877
|
+
<plist version="1.0"><dict>
|
|
14878
|
+
<key>destination</key><string>export</string>
|
|
14879
|
+
<key>manageAppVersionAndBuildNumber</key><false/>
|
|
14880
|
+
<key>method</key><string>app-store-connect</string>
|
|
14881
|
+
<key>signingStyle</key><string>automatic</string>
|
|
14882
|
+
<key>stripSwiftSymbols</key><true/>
|
|
14883
|
+
<key>uploadSymbols</key><true/>
|
|
14884
|
+
</dict></plist>
|
|
14885
|
+
`, requireBuildNumber = (value) => {
|
|
14886
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 1))
|
|
14887
|
+
throw new TypeError("iOS build number must be a positive integer.");
|
|
14888
|
+
return value;
|
|
14889
|
+
}, installRelease2 = async (artifactPath, metadata, outputRoot) => {
|
|
14890
|
+
const releaseRoot = join43(outputRoot, metadata.releaseId);
|
|
14891
|
+
const destination = join43(releaseRoot, "App.ipa");
|
|
14892
|
+
if (await pathExists5(releaseRoot)) {
|
|
14893
|
+
const value = JSON.parse(await readFile12(join43(releaseRoot, "release.json"), "utf8"));
|
|
14894
|
+
if (!isRecord12(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
|
|
14895
|
+
throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
|
|
14896
|
+
}
|
|
14897
|
+
const [bytes, sha2562] = await Promise.all([
|
|
14898
|
+
stat2(destination).then(({ size }) => size),
|
|
14899
|
+
sha256File2(destination)
|
|
14900
|
+
]);
|
|
14901
|
+
if (bytes !== metadata.bytes || sha2562 !== metadata.sha256)
|
|
14902
|
+
throw new TypeError(`Immutable iOS release ${metadata.releaseId} artifact is missing or modified.`);
|
|
14903
|
+
return {
|
|
14904
|
+
artifactPath: destination,
|
|
14905
|
+
metadata: {
|
|
14906
|
+
...metadata,
|
|
14907
|
+
artifact: "App.ipa"
|
|
14908
|
+
},
|
|
14909
|
+
releaseRoot
|
|
14910
|
+
};
|
|
14911
|
+
}
|
|
14912
|
+
await mkdir9(dirname25(releaseRoot), { recursive: true });
|
|
14913
|
+
const staging = await mkdtemp5(join43(dirname25(releaseRoot), ".ios-stage-"));
|
|
14914
|
+
try {
|
|
14915
|
+
await copyFile4(artifactPath, join43(staging, "App.ipa"));
|
|
14916
|
+
const complete = {
|
|
14917
|
+
...metadata,
|
|
14918
|
+
artifact: "App.ipa"
|
|
14919
|
+
};
|
|
14920
|
+
await writeFile9(join43(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
|
|
14921
|
+
`, { flag: "wx" });
|
|
14922
|
+
await rename8(staging, releaseRoot);
|
|
14923
|
+
return { artifactPath: destination, metadata: complete, releaseRoot };
|
|
14924
|
+
} finally {
|
|
14925
|
+
await rm7(staging, { force: true, recursive: true }).catch(() => {
|
|
14926
|
+
return;
|
|
14927
|
+
});
|
|
14928
|
+
}
|
|
14929
|
+
}, buildAbsoluteIosRelease = async (options) => {
|
|
14930
|
+
if (options.buildNumber !== undefined && options.prepareBuildNumber)
|
|
14931
|
+
throw new TypeError("iOS release buildNumber and prepareBuildNumber cannot be combined.");
|
|
14932
|
+
if ((options.host ?? detectAbsoluteMobileHost()) !== "macos")
|
|
14933
|
+
throw new TypeError("iOS release builds require macOS and Xcode.");
|
|
14934
|
+
const marketingVersion = options.config.iosVersion;
|
|
14935
|
+
if (!marketingVersion)
|
|
14936
|
+
throw new TypeError("iOS release builds require mobile.ios.version in absolutejs.config.ts.");
|
|
14937
|
+
const manifest = requireManifest2(JSON.parse(await readFile12(join43(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
14938
|
+
if (manifest.appId !== options.config.appId)
|
|
14939
|
+
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
14940
|
+
const nativeDirectory = join43(options.config.nativeProjectDirectory, "ios");
|
|
14941
|
+
let buildNumber = requireBuildNumber(options.buildNumber);
|
|
14942
|
+
if (options.prepareBuildNumber) {
|
|
14943
|
+
const nativeFingerprint = await fingerprintAbsoluteIosNativeProject(nativeDirectory);
|
|
14944
|
+
const buildIdentity = createHash10("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}\x00${marketingVersion}`).digest("hex");
|
|
14945
|
+
buildNumber = requireBuildNumber(await options.prepareBuildNumber(buildIdentity));
|
|
14946
|
+
}
|
|
14947
|
+
const stagingParent = resolve36(options.projectRoot, ".absolutejs/mobile");
|
|
14948
|
+
await mkdir9(stagingParent, { recursive: true });
|
|
14949
|
+
const staging = await mkdtemp5(join43(stagingParent, ".ios-build-"));
|
|
14950
|
+
const archivePath = join43(staging, "App.xcarchive");
|
|
14951
|
+
const exportPath = join43(staging, "export");
|
|
14952
|
+
const exportPlist = join43(staging, "ExportOptions.plist");
|
|
14953
|
+
await mkdir9(exportPath, { recursive: true });
|
|
14954
|
+
await writeFile9(exportPlist, exportOptions());
|
|
14955
|
+
const run = options.run ?? defaultRun2;
|
|
14956
|
+
try {
|
|
14957
|
+
const versionArguments = [
|
|
14958
|
+
`MARKETING_VERSION=${marketingVersion}`,
|
|
14959
|
+
...buildNumber === undefined ? [] : [`CURRENT_PROJECT_VERSION=${buildNumber}`]
|
|
14960
|
+
];
|
|
14961
|
+
const archiveExit = await run([
|
|
14962
|
+
"xcodebuild",
|
|
14963
|
+
"-workspace",
|
|
14964
|
+
join43(nativeDirectory, "App", "App.xcworkspace"),
|
|
14965
|
+
"-scheme",
|
|
14966
|
+
"App",
|
|
14967
|
+
"-configuration",
|
|
14968
|
+
"Release",
|
|
14969
|
+
"-destination",
|
|
14970
|
+
"generic/platform=iOS",
|
|
14971
|
+
"-archivePath",
|
|
14972
|
+
archivePath,
|
|
14973
|
+
...versionArguments,
|
|
14974
|
+
"archive"
|
|
14975
|
+
], { cwd: nativeDirectory });
|
|
14976
|
+
if (archiveExit !== 0)
|
|
14977
|
+
throw new TypeError("Xcode failed to archive the iOS app.");
|
|
14978
|
+
const archivedApp = await findByExtension(join43(archivePath, "Products", "Applications"), ".app");
|
|
14979
|
+
const capture = options.capture ?? defaultCapture3;
|
|
14980
|
+
const signed2 = archivedApp ? capture([
|
|
14981
|
+
"codesign",
|
|
14982
|
+
"--verify",
|
|
14983
|
+
"--deep",
|
|
14984
|
+
"--strict",
|
|
14985
|
+
archivedApp
|
|
14986
|
+
]).exitCode === 0 : false;
|
|
14987
|
+
if (!signed2 && !options.allowUnsigned)
|
|
14988
|
+
throw new TypeError("Xcode produced an unsigned iOS archive. Configure signing in the source-owned Xcode project, or pass --unsigned only for a non-publishable build.");
|
|
14989
|
+
const exportExit = await run([
|
|
14990
|
+
"xcodebuild",
|
|
14991
|
+
"-exportArchive",
|
|
14992
|
+
"-archivePath",
|
|
14993
|
+
archivePath,
|
|
14994
|
+
"-exportPath",
|
|
14995
|
+
exportPath,
|
|
14996
|
+
"-exportOptionsPlist",
|
|
14997
|
+
exportPlist
|
|
14998
|
+
], { cwd: nativeDirectory });
|
|
14999
|
+
if (exportExit !== 0)
|
|
15000
|
+
throw new TypeError("Xcode failed to export the App Store IPA.");
|
|
15001
|
+
const artifactPath = await findByExtension(exportPath, ".ipa");
|
|
15002
|
+
if (!artifactPath)
|
|
15003
|
+
throw new TypeError("Xcode did not produce an exported IPA.");
|
|
15004
|
+
const [bytes, sha2562] = await Promise.all([
|
|
15005
|
+
stat2(artifactPath).then(({ size }) => size),
|
|
15006
|
+
sha256File2(artifactPath)
|
|
15007
|
+
]);
|
|
15008
|
+
const releaseId = `amobile_ios_${sha2562}`;
|
|
15009
|
+
return await installRelease2(artifactPath, {
|
|
15010
|
+
appBuild: manifest.appBuild,
|
|
15011
|
+
appId: manifest.appId,
|
|
15012
|
+
...buildNumber === undefined ? {} : { buildNumber },
|
|
15013
|
+
bytes,
|
|
15014
|
+
engine: "capacitor",
|
|
15015
|
+
format: 1,
|
|
15016
|
+
marketingVersion,
|
|
15017
|
+
platform: "ios",
|
|
15018
|
+
releaseId,
|
|
15019
|
+
runtime: manifest.runtime,
|
|
15020
|
+
sha256: sha2562,
|
|
15021
|
+
signed: signed2,
|
|
15022
|
+
type: "ipa"
|
|
15023
|
+
}, safeOutputDirectory2(options.projectRoot, options.outputDirectory));
|
|
15024
|
+
} finally {
|
|
15025
|
+
await rm7(staging, { force: true, recursive: true }).catch(() => {
|
|
15026
|
+
return;
|
|
15027
|
+
});
|
|
15028
|
+
}
|
|
15029
|
+
};
|
|
15030
|
+
var init_iosRelease = __esm(() => {
|
|
15031
|
+
init_emulatorDoctor();
|
|
15032
|
+
ignoredFingerprintDirectories = new Set([
|
|
15033
|
+
"Pods",
|
|
15034
|
+
"DerivedData",
|
|
15035
|
+
"build",
|
|
15036
|
+
"xcuserdata"
|
|
15037
|
+
]);
|
|
15038
|
+
});
|
|
15039
|
+
|
|
15040
|
+
// src/mobile/releasePublisher.ts
|
|
15041
|
+
import { access as access9 } from "fs/promises";
|
|
15042
|
+
import { isAbsolute as isAbsolute6, relative as relative22, resolve as resolve37, sep as sep5 } from "path";
|
|
15043
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
15044
|
+
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
15045
|
+
if (typeof publisher.prepareIosRelease !== "function") {
|
|
15046
|
+
throw new TypeError("App Store Connect publishing requires a registry module created with @absolutejs/deploy/app-store-connect.");
|
|
15047
|
+
}
|
|
15048
|
+
const { buildNumber } = await publisher.prepareIosRelease(options);
|
|
15049
|
+
if (!Number.isSafeInteger(buildNumber) || buildNumber < 1) {
|
|
15050
|
+
throw new TypeError("App Store Connect publisher returned an invalid iOS build number.");
|
|
15051
|
+
}
|
|
15052
|
+
return buildNumber;
|
|
15053
|
+
}, prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
15054
|
+
if (typeof publisher.prepareAndroidRelease !== "function") {
|
|
15055
|
+
throw new TypeError("Google Play publishing requires a registry module created with @absolutejs/deploy/google-play.");
|
|
15056
|
+
}
|
|
15057
|
+
const prepared = await publisher.prepareAndroidRelease(options);
|
|
15058
|
+
const { versionCode } = prepared;
|
|
15059
|
+
if (typeof versionCode !== "number" || !Number.isSafeInteger(versionCode) || versionCode < 1 || versionCode > 2100000000) {
|
|
15060
|
+
throw new TypeError("Google Play publisher returned an invalid Android versionCode.");
|
|
15061
|
+
}
|
|
15062
|
+
return versionCode;
|
|
15063
|
+
}, isRecord13 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPublisher = (value) => isRecord13(value) && typeof value.publish === "function", publisherModulePath = (projectRoot, requested) => {
|
|
15064
|
+
const root = resolve37(projectRoot);
|
|
15065
|
+
const path = resolve37(root, requested);
|
|
15066
|
+
const projectRelative = relative22(root, path);
|
|
15067
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute6(projectRelative)) {
|
|
15068
|
+
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
15069
|
+
}
|
|
15070
|
+
return path;
|
|
15071
|
+
}, loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
15072
|
+
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
15073
|
+
await access9(modulePath).catch(() => {
|
|
15074
|
+
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
15075
|
+
});
|
|
15076
|
+
const loaded = await import(pathToFileURL2(modulePath).href);
|
|
15077
|
+
const publisher = isRecord13(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
15078
|
+
if (!isPublisher(publisher)) {
|
|
15079
|
+
throw new TypeError("Native release registry module must default-export a registry with publish(options).");
|
|
15080
|
+
}
|
|
15081
|
+
return publisher;
|
|
15082
|
+
}, publishAbsoluteAndroidRelease = async (options) => {
|
|
15083
|
+
const publisher = await loadAbsoluteNativeReleasePublisher(options.projectRoot, options.modulePath);
|
|
15084
|
+
const publication = await publisher.publish({
|
|
15085
|
+
allowUnsigned: options.allowUnsigned,
|
|
15086
|
+
channel: options.channel,
|
|
15087
|
+
googlePlay: options.googlePlay,
|
|
15088
|
+
releaseRoot: options.release.releaseRoot,
|
|
15089
|
+
signal: options.signal
|
|
15090
|
+
});
|
|
15091
|
+
const expected = options.release.metadata;
|
|
15092
|
+
const actual = publication.record?.metadata;
|
|
15093
|
+
if (!actual || actual.appId !== expected.appId || actual.platform !== "android" || actual.releaseId !== expected.releaseId || actual.sha256 !== expected.sha256 || actual.signed !== expected.signed || actual.versionCode !== expected.versionCode || typeof publication.reused !== "boolean") {
|
|
15094
|
+
throw new TypeError("Native release registry returned a different Android release identity.");
|
|
15095
|
+
}
|
|
15096
|
+
if (options.channel !== undefined && (publication.channel?.channel !== options.channel || publication.channel.releaseId !== expected.releaseId)) {
|
|
15097
|
+
throw new TypeError("Native release registry did not promote the requested channel.");
|
|
15098
|
+
}
|
|
15099
|
+
const { googlePlay } = publication;
|
|
15100
|
+
if (options.googlePlay) {
|
|
15101
|
+
if (!expected.versionCode || !googlePlay || googlePlay.receipt.provider !== "google-play" || googlePlay.receipt.packageName !== expected.appId || googlePlay.receipt.releaseId !== expected.releaseId || googlePlay.receipt.sha256 !== expected.sha256 || googlePlay.receipt.stage !== "committed" || googlePlay.receipt.intent.track !== options.googlePlay.track || typeof googlePlay.receipt.versionCode !== "string" || !/^\d+$/.test(googlePlay.receipt.versionCode) || Number(googlePlay.receipt.versionCode) !== expected.versionCode || typeof googlePlay.reused !== "boolean") {
|
|
15102
|
+
throw new TypeError("Native release publisher did not commit the requested Google Play release.");
|
|
15103
|
+
}
|
|
15104
|
+
}
|
|
15105
|
+
return publication;
|
|
15106
|
+
}, publishAbsoluteIosRelease = async (options) => {
|
|
15107
|
+
const publisher = await loadAbsoluteNativeReleasePublisher(options.projectRoot, options.modulePath);
|
|
15108
|
+
const publication = await publisher.publish({
|
|
15109
|
+
allowUnsigned: options.allowUnsigned,
|
|
15110
|
+
appStoreConnect: options.appStoreConnect,
|
|
15111
|
+
channel: options.channel,
|
|
15112
|
+
releaseRoot: options.release.releaseRoot,
|
|
15113
|
+
signal: options.signal
|
|
15114
|
+
});
|
|
15115
|
+
const expected = options.release.metadata;
|
|
15116
|
+
const actual = publication.record?.metadata;
|
|
15117
|
+
if (!actual || actual.appId !== expected.appId || actual.platform !== "ios" || actual.releaseId !== expected.releaseId || actual.sha256 !== expected.sha256 || actual.signed !== expected.signed || actual.buildNumber !== expected.buildNumber || actual.marketingVersion !== expected.marketingVersion || typeof publication.reused !== "boolean") {
|
|
15118
|
+
throw new TypeError("Native release registry returned a different iOS release identity.");
|
|
15119
|
+
}
|
|
15120
|
+
if (options.channel !== undefined && (publication.channel?.channel !== options.channel || publication.channel.releaseId !== expected.releaseId)) {
|
|
15121
|
+
throw new TypeError("Native release registry did not promote the requested channel.");
|
|
15122
|
+
}
|
|
15123
|
+
if (options.appStoreConnect) {
|
|
15124
|
+
const distributed = publication.appStoreConnect;
|
|
15125
|
+
if (!expected.buildNumber || !distributed || distributed.receipt.provider !== "app-store-connect" || distributed.receipt.releaseId !== expected.releaseId || distributed.receipt.sha256 !== expected.sha256 || distributed.receipt.buildNumber !== expected.buildNumber || distributed.receipt.marketingVersion !== expected.marketingVersion || !["distributed", "review-submitted"].includes(distributed.receipt.stage) || JSON.stringify([...distributed.receipt.intent.groups].sort()) !== JSON.stringify([...options.appStoreConnect.groups ?? []].sort()) || distributed.receipt.intent.submitForReview !== (options.appStoreConnect.submitForReview ?? false) || typeof distributed.reused !== "boolean") {
|
|
15126
|
+
throw new TypeError("Native release publisher did not complete the requested App Store Connect release.");
|
|
15127
|
+
}
|
|
15128
|
+
}
|
|
15129
|
+
return publication;
|
|
15130
|
+
};
|
|
15131
|
+
var init_releasePublisher = () => {};
|
|
15132
|
+
|
|
14710
15133
|
// src/cli/scripts/mobile.ts
|
|
14711
15134
|
var exports_mobile = {};
|
|
14712
15135
|
__export(exports_mobile, {
|
|
14713
15136
|
runMobile: () => runMobile
|
|
14714
15137
|
});
|
|
14715
|
-
import { access as
|
|
14716
|
-
import { join as
|
|
15138
|
+
import { access as access10, mkdir as mkdir10, writeFile as writeFile10 } from "fs/promises";
|
|
15139
|
+
import { join as join44, resolve as resolve38 } from "path";
|
|
14717
15140
|
import { createInterface } from "readline/promises";
|
|
14718
15141
|
var NOT_FOUND2 = -1, CAPACITOR_PACKAGES, valueAfter = (args, flag) => {
|
|
14719
15142
|
const index = args.indexOf(flag);
|
|
@@ -14727,9 +15150,9 @@ var NOT_FOUND2 = -1, CAPACITOR_PACKAGES, valueAfter = (args, flag) => {
|
|
|
14727
15150
|
}
|
|
14728
15151
|
return value;
|
|
14729
15152
|
}, capacitorExecutable = async (projectRoot) => {
|
|
14730
|
-
const executable =
|
|
15153
|
+
const executable = join44(projectRoot, "node_modules", ".bin", "cap");
|
|
14731
15154
|
try {
|
|
14732
|
-
await
|
|
15155
|
+
await access10(executable);
|
|
14733
15156
|
return executable;
|
|
14734
15157
|
} catch {
|
|
14735
15158
|
throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
|
|
@@ -14770,7 +15193,7 @@ var NOT_FOUND2 = -1, CAPACITOR_PACKAGES, valueAfter = (args, flag) => {
|
|
|
14770
15193
|
await applyAbsoluteNativeDeepLinks(mobile, platforms);
|
|
14771
15194
|
}, associations = async (args) => {
|
|
14772
15195
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
14773
|
-
const outputDirectory =
|
|
15196
|
+
const outputDirectory = resolve38(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
|
|
14774
15197
|
if (args.includes("--verify")) {
|
|
14775
15198
|
const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
|
|
14776
15199
|
console.log(`Verified ${result2.results.length} hosted association files`);
|
|
@@ -14830,7 +15253,21 @@ Mobile release transport checks failed.`);
|
|
|
14830
15253
|
throw new TypeError("Mobile release validation failed. Resolve every failed check before signing or publishing the app.");
|
|
14831
15254
|
}
|
|
14832
15255
|
}, mobileBuildServerEntry = (args) => {
|
|
14833
|
-
const valueFlags = new Set([
|
|
15256
|
+
const valueFlags = new Set([
|
|
15257
|
+
"--channel",
|
|
15258
|
+
"--config",
|
|
15259
|
+
"--outdir",
|
|
15260
|
+
"--play-name",
|
|
15261
|
+
"--play-notes",
|
|
15262
|
+
"--play-rollout",
|
|
15263
|
+
"--play-status",
|
|
15264
|
+
"--play-track",
|
|
15265
|
+
"--play-update-priority",
|
|
15266
|
+
"--registry",
|
|
15267
|
+
"--testflight-group",
|
|
15268
|
+
"--testflight-notes",
|
|
15269
|
+
"--web-outdir"
|
|
15270
|
+
]);
|
|
14834
15271
|
const skipped = new Set;
|
|
14835
15272
|
args.forEach((value, index) => {
|
|
14836
15273
|
if (valueFlags.has(value)) {
|
|
@@ -14839,6 +15276,111 @@ Mobile release transport checks failed.`);
|
|
|
14839
15276
|
}
|
|
14840
15277
|
});
|
|
14841
15278
|
return args.find((value, index) => !skipped.has(index) && value !== "--unsigned" && !value.startsWith("-")) ?? DEFAULT_SERVER_ENTRY;
|
|
15279
|
+
}, requireValueAfter = (args, flag) => {
|
|
15280
|
+
const value = valueAfter(args, flag);
|
|
15281
|
+
if (!value || value.startsWith("-")) {
|
|
15282
|
+
throw new TypeError(`mobile publish android requires ${flag} <value>.`);
|
|
15283
|
+
}
|
|
15284
|
+
return value;
|
|
15285
|
+
}, requireIosValueAfter = (args, flag) => {
|
|
15286
|
+
const value = valueAfter(args, flag);
|
|
15287
|
+
if (!value || value.startsWith("-")) {
|
|
15288
|
+
throw new TypeError(`mobile publish ios requires ${flag} <value>.`);
|
|
15289
|
+
}
|
|
15290
|
+
return value;
|
|
15291
|
+
}, appStoreConnectTarget = (args) => {
|
|
15292
|
+
const testFlightFlags = args.filter((value) => value.startsWith("--testflight-"));
|
|
15293
|
+
if (testFlightFlags.length === 0)
|
|
15294
|
+
return;
|
|
15295
|
+
if (args.some((value, index) => (value === "--testflight-group" || value === "--testflight-notes") && (!args[index + 1] || args[index + 1]?.startsWith("-")))) {
|
|
15296
|
+
throw new TypeError("mobile publish ios requires a value after every TestFlight group or notes flag.");
|
|
15297
|
+
}
|
|
15298
|
+
const groups = valuesAfter(args, "--testflight-group");
|
|
15299
|
+
const submitForReview = args.includes("--testflight-submit-review");
|
|
15300
|
+
if (submitForReview && groups.length === 0) {
|
|
15301
|
+
throw new TypeError("mobile publish ios --testflight-submit-review requires at least one --testflight-group.");
|
|
15302
|
+
}
|
|
15303
|
+
const whatsNew = valuesAfter(args, "--testflight-notes").map((note) => {
|
|
15304
|
+
const separator = note.indexOf("=");
|
|
15305
|
+
if (separator < 1 || separator === note.length - 1) {
|
|
15306
|
+
throw new TypeError("mobile publish ios --testflight-notes must use locale=text.");
|
|
15307
|
+
}
|
|
15308
|
+
return {
|
|
15309
|
+
locale: note.slice(0, separator),
|
|
15310
|
+
text: note.slice(separator + 1)
|
|
15311
|
+
};
|
|
15312
|
+
});
|
|
15313
|
+
return {
|
|
15314
|
+
...groups.length === 0 ? {} : { groups },
|
|
15315
|
+
submitForReview,
|
|
15316
|
+
...whatsNew.length === 0 ? {} : { whatsNew }
|
|
15317
|
+
};
|
|
15318
|
+
}, googlePlayTarget = (args) => {
|
|
15319
|
+
const playFlags = args.filter((value) => value.startsWith("--play-"));
|
|
15320
|
+
if (playFlags.length === 0)
|
|
15321
|
+
return;
|
|
15322
|
+
if (!args.includes("--play-track")) {
|
|
15323
|
+
throw new TypeError("mobile publish android requires --play-track <track> when using Google Play options.");
|
|
15324
|
+
}
|
|
15325
|
+
const track = requireValueAfter(args, "--play-track");
|
|
15326
|
+
const rolloutValue = args.includes("--play-rollout") ? requireValueAfter(args, "--play-rollout") : undefined;
|
|
15327
|
+
const userFraction = rolloutValue === undefined ? undefined : Number(rolloutValue);
|
|
15328
|
+
if (userFraction !== undefined && (!Number.isFinite(userFraction) || userFraction <= 0 || userFraction >= 1)) {
|
|
15329
|
+
throw new TypeError("mobile publish android --play-rollout must be greater than 0 and less than 1.");
|
|
15330
|
+
}
|
|
15331
|
+
const requestedStatus = args.includes("--play-status") ? requireValueAfter(args, "--play-status") : undefined;
|
|
15332
|
+
const statuses = {
|
|
15333
|
+
completed: "completed",
|
|
15334
|
+
draft: "draft",
|
|
15335
|
+
halted: "halted",
|
|
15336
|
+
"in-progress": "inProgress"
|
|
15337
|
+
};
|
|
15338
|
+
if (requestedStatus !== undefined && !(requestedStatus in statuses)) {
|
|
15339
|
+
throw new TypeError("mobile publish android --play-status must be completed, draft, halted, or in-progress.");
|
|
15340
|
+
}
|
|
15341
|
+
let status2;
|
|
15342
|
+
if (requestedStatus === undefined) {
|
|
15343
|
+
status2 = userFraction === undefined ? "completed" : "inProgress";
|
|
15344
|
+
} else if (requestedStatus === "in-progress") {
|
|
15345
|
+
status2 = statuses["in-progress"];
|
|
15346
|
+
} else if (requestedStatus === "completed") {
|
|
15347
|
+
status2 = statuses.completed;
|
|
15348
|
+
} else if (requestedStatus === "draft") {
|
|
15349
|
+
status2 = statuses.draft;
|
|
15350
|
+
} else {
|
|
15351
|
+
status2 = statuses.halted;
|
|
15352
|
+
}
|
|
15353
|
+
if (userFraction === undefined ? status2 === "inProgress" || status2 === "halted" : status2 !== "inProgress" && status2 !== "halted") {
|
|
15354
|
+
throw new TypeError("mobile publish android staged statuses require --play-rollout, and other statuses forbid it.");
|
|
15355
|
+
}
|
|
15356
|
+
const priorityValue = args.includes("--play-update-priority") ? requireValueAfter(args, "--play-update-priority") : undefined;
|
|
15357
|
+
const inAppUpdatePriority = priorityValue === undefined ? undefined : Number(priorityValue);
|
|
15358
|
+
if (inAppUpdatePriority !== undefined && (!Number.isInteger(inAppUpdatePriority) || inAppUpdatePriority < 0 || inAppUpdatePriority > 5)) {
|
|
15359
|
+
throw new TypeError("mobile publish android --play-update-priority must be an integer from 0 through 5.");
|
|
15360
|
+
}
|
|
15361
|
+
if (args.some((value, index) => value === "--play-notes" && (!args[index + 1] || args[index + 1]?.startsWith("-")))) {
|
|
15362
|
+
throw new TypeError("mobile publish android requires --play-notes <language=text>.");
|
|
15363
|
+
}
|
|
15364
|
+
const releaseNotes = valuesAfter(args, "--play-notes").map((note) => {
|
|
15365
|
+
const separator = note.indexOf("=");
|
|
15366
|
+
if (separator < 1 || separator === note.length - 1) {
|
|
15367
|
+
throw new TypeError("mobile publish android --play-notes must use language=text.");
|
|
15368
|
+
}
|
|
15369
|
+
return {
|
|
15370
|
+
language: note.slice(0, separator),
|
|
15371
|
+
text: note.slice(separator + 1)
|
|
15372
|
+
};
|
|
15373
|
+
});
|
|
15374
|
+
return {
|
|
15375
|
+
changesNotSentForReview: args.includes("--play-hold-review"),
|
|
15376
|
+
...inAppUpdatePriority === undefined ? {} : { inAppUpdatePriority },
|
|
15377
|
+
...args.includes("--play-name") ? { name: requireValueAfter(args, "--play-name") } : {},
|
|
15378
|
+
...releaseNotes.length === 0 ? {} : { releaseNotes },
|
|
15379
|
+
reviewBehavior: args.includes("--play-cancel-existing-review") ? "CANCEL_IN_REVIEW_AND_SUBMIT" : "ERROR_IF_IN_REVIEW",
|
|
15380
|
+
status: status2,
|
|
15381
|
+
track,
|
|
15382
|
+
...userFraction === undefined ? {} : { userFraction }
|
|
15383
|
+
};
|
|
14842
15384
|
}, requireAndroidReleaseReady = async (mobile, projectRoot) => {
|
|
14843
15385
|
const releaseCheck = await inspectAbsoluteMobileRelease({ ...mobile, platforms: ["android"] }, projectRoot);
|
|
14844
15386
|
if (releaseCheck.ready)
|
|
@@ -14852,7 +15394,7 @@ Mobile release transport checks failed.`);
|
|
|
14852
15394
|
status: check2.status
|
|
14853
15395
|
})));
|
|
14854
15396
|
throw new TypeError("Android release validation failed before Gradle signing.");
|
|
14855
|
-
}, buildAndroid = async (args) => {
|
|
15397
|
+
}, buildAndroid = async (args, prepareVersionCode) => {
|
|
14856
15398
|
const configPath2 = valueAfter(args, "--config");
|
|
14857
15399
|
const { mobile, projectRoot } = await loadMobile(configPath2);
|
|
14858
15400
|
if (!mobile.platforms.includes("android")) {
|
|
@@ -14871,13 +15413,14 @@ Mobile release transport checks failed.`);
|
|
|
14871
15413
|
allowUnsigned: args.includes("--unsigned"),
|
|
14872
15414
|
config: mobile,
|
|
14873
15415
|
outputDirectory: valueAfter(args, "--outdir"),
|
|
14874
|
-
projectRoot
|
|
15416
|
+
projectRoot,
|
|
15417
|
+
...prepareVersionCode === undefined ? {} : { prepareVersionCode }
|
|
14875
15418
|
});
|
|
14876
15419
|
success = true;
|
|
14877
15420
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
14878
15421
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
14879
15422
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
14880
|
-
console.log(`Metadata: ${
|
|
15423
|
+
console.log(`Metadata: ${join44(release.releaseRoot, "release.json")}`);
|
|
14881
15424
|
return release;
|
|
14882
15425
|
} finally {
|
|
14883
15426
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -14889,6 +15432,154 @@ Mobile release transport checks failed.`);
|
|
|
14889
15432
|
unsignedAllowed: args.includes("--unsigned")
|
|
14890
15433
|
});
|
|
14891
15434
|
}
|
|
15435
|
+
}, printGooglePlayPublication = (googlePlay) => {
|
|
15436
|
+
console.log(`${googlePlay.reused ? "Reused" : "Committed"} Google Play version ${googlePlay.receipt.versionCode} on ${googlePlay.receipt.intent.track}.`);
|
|
15437
|
+
}, printAppStoreConnectPublication = (publication) => {
|
|
15438
|
+
const { receipt } = publication;
|
|
15439
|
+
console.log(`${publication.reused ? "Reused" : "Uploaded"} App Store Connect build ${receipt.marketingVersion} (${receipt.buildNumber}); ${receipt.stage}.`);
|
|
15440
|
+
}, publishAndroid = async (args) => {
|
|
15441
|
+
const registryModule = args.includes("--registry") ? requireValueAfter(args, "--registry") : "mobile.release.ts";
|
|
15442
|
+
const channel = args.includes("--channel") ? requireValueAfter(args, "--channel") : undefined;
|
|
15443
|
+
const configPath2 = valueAfter(args, "--config");
|
|
15444
|
+
const googlePlay = googlePlayTarget(args);
|
|
15445
|
+
const { mobile, projectRoot } = await loadMobile(configPath2);
|
|
15446
|
+
const startedAt = performance.now();
|
|
15447
|
+
let reused = false;
|
|
15448
|
+
let success = false;
|
|
15449
|
+
try {
|
|
15450
|
+
const publisher = await loadAbsoluteNativeReleasePublisher(projectRoot, registryModule);
|
|
15451
|
+
const release = await buildAndroid(args, googlePlay ? (buildIdentity) => prepareAbsoluteAndroidRelease(publisher, {
|
|
15452
|
+
buildIdentity,
|
|
15453
|
+
googlePlay,
|
|
15454
|
+
packageName: mobile.appId
|
|
15455
|
+
}) : undefined);
|
|
15456
|
+
const publication = await publishAbsoluteAndroidRelease({
|
|
15457
|
+
allowUnsigned: args.includes("--unsigned"),
|
|
15458
|
+
channel,
|
|
15459
|
+
googlePlay,
|
|
15460
|
+
modulePath: registryModule,
|
|
15461
|
+
projectRoot,
|
|
15462
|
+
release
|
|
15463
|
+
});
|
|
15464
|
+
const { reused: publicationReused } = publication;
|
|
15465
|
+
reused = publicationReused;
|
|
15466
|
+
success = true;
|
|
15467
|
+
console.log(`${publication.reused ? "Reused" : "Published"} Android release ${release.metadata.releaseId}${publication.channel ? ` on ${publication.channel.channel}` : ""}.`);
|
|
15468
|
+
if (publication.googlePlay)
|
|
15469
|
+
printGooglePlayPublication(publication.googlePlay);
|
|
15470
|
+
return publication;
|
|
15471
|
+
} finally {
|
|
15472
|
+
sendTelemetryEvent("mobile:android-release-publish", {
|
|
15473
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
15474
|
+
engine: "capacitor",
|
|
15475
|
+
platform: "android",
|
|
15476
|
+
provider: googlePlay ? "google-play" : "registry-module",
|
|
15477
|
+
reused,
|
|
15478
|
+
success,
|
|
15479
|
+
type: "aab",
|
|
15480
|
+
unsignedAllowed: args.includes("--unsigned")
|
|
15481
|
+
});
|
|
15482
|
+
}
|
|
15483
|
+
}, requireIosReleaseReady = async (mobile, projectRoot) => {
|
|
15484
|
+
const releaseCheck = await inspectAbsoluteMobileRelease({ ...mobile, platforms: ["ios"] }, projectRoot);
|
|
15485
|
+
if (releaseCheck.ready)
|
|
15486
|
+
return;
|
|
15487
|
+
printDoctorChecks(releaseCheck.checks.map((check2) => ({
|
|
15488
|
+
id: check2.id,
|
|
15489
|
+
label: check2.detail,
|
|
15490
|
+
path: check2.path,
|
|
15491
|
+
platform: "ios",
|
|
15492
|
+
remediation: check2.remediation,
|
|
15493
|
+
status: check2.status
|
|
15494
|
+
})));
|
|
15495
|
+
throw new TypeError("iOS release validation failed before Xcode signing.");
|
|
15496
|
+
}, buildIos = async (args, prepareBuildNumber) => {
|
|
15497
|
+
const configPath2 = valueAfter(args, "--config");
|
|
15498
|
+
const { mobile, projectRoot } = await loadMobile(configPath2);
|
|
15499
|
+
if (!mobile.platforms.includes("ios")) {
|
|
15500
|
+
throw new TypeError("mobile build ios requires ios in mobile.platforms.");
|
|
15501
|
+
}
|
|
15502
|
+
const startedAt = performance.now();
|
|
15503
|
+
let success = false;
|
|
15504
|
+
try {
|
|
15505
|
+
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath2, { prepareOnly: true });
|
|
15506
|
+
await writeAbsoluteCapacitorConfig(mobile, { projectRoot });
|
|
15507
|
+
await runCapacitorForPlatforms(projectRoot, "sync", ["ios"]);
|
|
15508
|
+
await applyAbsoluteNativeDeepLinks(mobile, ["ios"]);
|
|
15509
|
+
await requireIosReleaseReady(mobile, projectRoot);
|
|
15510
|
+
const release = await buildAbsoluteIosRelease({
|
|
15511
|
+
allowUnsigned: args.includes("--unsigned"),
|
|
15512
|
+
config: mobile,
|
|
15513
|
+
outputDirectory: valueAfter(args, "--outdir"),
|
|
15514
|
+
...prepareBuildNumber === undefined ? {} : { prepareBuildNumber },
|
|
15515
|
+
projectRoot
|
|
15516
|
+
});
|
|
15517
|
+
success = true;
|
|
15518
|
+
const durationMs = Math.round(performance.now() - startedAt);
|
|
15519
|
+
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
|
|
15520
|
+
console.log(`Artifact: ${release.artifactPath}`);
|
|
15521
|
+
console.log(`Metadata: ${join44(release.releaseRoot, "release.json")}`);
|
|
15522
|
+
return release;
|
|
15523
|
+
} finally {
|
|
15524
|
+
sendTelemetryEvent("mobile:ios-release-build", {
|
|
15525
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
15526
|
+
engine: "capacitor",
|
|
15527
|
+
platform: "ios",
|
|
15528
|
+
success,
|
|
15529
|
+
type: "ipa",
|
|
15530
|
+
unsignedAllowed: args.includes("--unsigned")
|
|
15531
|
+
});
|
|
15532
|
+
}
|
|
15533
|
+
}, publishIos = async (args) => {
|
|
15534
|
+
const registryModule = args.includes("--registry") ? requireIosValueAfter(args, "--registry") : "mobile.release.ts";
|
|
15535
|
+
const channel = args.includes("--channel") ? requireIosValueAfter(args, "--channel") : undefined;
|
|
15536
|
+
const configPath2 = valueAfter(args, "--config");
|
|
15537
|
+
const appStoreConnect = appStoreConnectTarget(args);
|
|
15538
|
+
const { mobile, projectRoot } = await loadMobile(configPath2);
|
|
15539
|
+
const startedAt = performance.now();
|
|
15540
|
+
let reused = false;
|
|
15541
|
+
let success = false;
|
|
15542
|
+
try {
|
|
15543
|
+
const publisher = await loadAbsoluteNativeReleasePublisher(projectRoot, registryModule);
|
|
15544
|
+
const release = await buildIos(args, appStoreConnect ? (buildIdentity) => {
|
|
15545
|
+
if (!mobile.iosVersion)
|
|
15546
|
+
throw new TypeError("iOS publishing requires mobile.ios.version.");
|
|
15547
|
+
return prepareAbsoluteIosRelease(publisher, {
|
|
15548
|
+
buildIdentity,
|
|
15549
|
+
bundleId: mobile.appId,
|
|
15550
|
+
marketingVersion: mobile.iosVersion
|
|
15551
|
+
});
|
|
15552
|
+
} : undefined);
|
|
15553
|
+
const publication = await publishAbsoluteIosRelease({
|
|
15554
|
+
allowUnsigned: args.includes("--unsigned"),
|
|
15555
|
+
appStoreConnect,
|
|
15556
|
+
channel,
|
|
15557
|
+
modulePath: registryModule,
|
|
15558
|
+
projectRoot,
|
|
15559
|
+
release
|
|
15560
|
+
});
|
|
15561
|
+
const {
|
|
15562
|
+
appStoreConnect: appStoreConnectPublication,
|
|
15563
|
+
reused: publicationReused
|
|
15564
|
+
} = publication;
|
|
15565
|
+
reused = publicationReused;
|
|
15566
|
+
success = true;
|
|
15567
|
+
console.log(`${publication.reused ? "Reused" : "Published"} iOS release ${release.metadata.releaseId}${publication.channel ? ` on ${publication.channel.channel}` : ""}.`);
|
|
15568
|
+
if (appStoreConnectPublication)
|
|
15569
|
+
printAppStoreConnectPublication(appStoreConnectPublication);
|
|
15570
|
+
return publication;
|
|
15571
|
+
} finally {
|
|
15572
|
+
sendTelemetryEvent("mobile:ios-release-publish", {
|
|
15573
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
15574
|
+
engine: "capacitor",
|
|
15575
|
+
platform: "ios",
|
|
15576
|
+
provider: appStoreConnect ? "app-store-connect" : "registry-module",
|
|
15577
|
+
reused,
|
|
15578
|
+
success,
|
|
15579
|
+
type: "ipa",
|
|
15580
|
+
unsignedAllowed: args.includes("--unsigned")
|
|
15581
|
+
});
|
|
15582
|
+
}
|
|
14892
15583
|
}, doctor = async (args) => {
|
|
14893
15584
|
if (args.includes("release")) {
|
|
14894
15585
|
await runReleaseDoctor(args);
|
|
@@ -14955,7 +15646,7 @@ Emulator setup verification:`);
|
|
|
14955
15646
|
}
|
|
14956
15647
|
return { https: args.includes("--https"), port };
|
|
14957
15648
|
}
|
|
14958
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
15649
|
+
const instances = listLiveInstances().filter((instance2) => resolve38(instance2.cwd) === resolve38(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
14959
15650
|
if (instances.length !== 1) {
|
|
14960
15651
|
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>.");
|
|
14961
15652
|
}
|
|
@@ -14998,8 +15689,8 @@ Emulator setup verification:`);
|
|
|
14998
15689
|
}
|
|
14999
15690
|
return selected;
|
|
15000
15691
|
}, safeArtifactRoot = (projectRoot, value) => {
|
|
15001
|
-
const root =
|
|
15002
|
-
if (root !== projectRoot && !root.startsWith(`${
|
|
15692
|
+
const root = resolve38(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
|
|
15693
|
+
if (root !== projectRoot && !root.startsWith(`${resolve38(projectRoot)}/`)) {
|
|
15003
15694
|
throw new TypeError("mobile test --artifacts must remain inside the project.");
|
|
15004
15695
|
}
|
|
15005
15696
|
return root;
|
|
@@ -15023,12 +15714,12 @@ Emulator setup verification:`);
|
|
|
15023
15714
|
timeoutMs
|
|
15024
15715
|
});
|
|
15025
15716
|
}, writeAndroidFailureArtifacts = async (options) => {
|
|
15026
|
-
await
|
|
15027
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
15717
|
+
await mkdir10(options.artifactRoot, { recursive: true });
|
|
15718
|
+
const screenshot = options.session ? await options.session.screenshot(join44(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
15028
15719
|
return;
|
|
15029
15720
|
}) : undefined;
|
|
15030
|
-
const diagnosticPath =
|
|
15031
|
-
await
|
|
15721
|
+
const diagnosticPath = join44(options.artifactRoot, "android-failure.json");
|
|
15722
|
+
await writeFile10(diagnosticPath, `${JSON.stringify({
|
|
15032
15723
|
diagnostics: options.session?.diagnostics ?? [],
|
|
15033
15724
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
15034
15725
|
platform: "android",
|
|
@@ -15138,7 +15829,19 @@ Emulator setup verification:`);
|
|
|
15138
15829
|
await buildAndroid(args.slice(2));
|
|
15139
15830
|
return;
|
|
15140
15831
|
}
|
|
15141
|
-
|
|
15832
|
+
if (command === "build" && args[1] === "ios") {
|
|
15833
|
+
await buildIos(args.slice(2));
|
|
15834
|
+
return;
|
|
15835
|
+
}
|
|
15836
|
+
if (command === "publish" && args[1] === "android") {
|
|
15837
|
+
await publishAndroid(args.slice(2));
|
|
15838
|
+
return;
|
|
15839
|
+
}
|
|
15840
|
+
if (command === "publish" && args[1] === "ios") {
|
|
15841
|
+
await publishIos(args.slice(2));
|
|
15842
|
+
return;
|
|
15843
|
+
}
|
|
15844
|
+
throw new TypeError("Usage: absolute mobile <init [--no-native] [--force] | sync [ios|android] | associations [--outdir dir] [--verify] | doctor [ios|android|release] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--outdir dir] [--web-outdir dir] [--unsigned] | 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] [--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] [--wait-for-hmr] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json]> [--config path]");
|
|
15142
15845
|
};
|
|
15143
15846
|
var init_mobile = __esm(() => {
|
|
15144
15847
|
init_capacitorProject();
|
|
@@ -15154,6 +15857,8 @@ var init_mobile = __esm(() => {
|
|
|
15154
15857
|
init_telemetryEvent();
|
|
15155
15858
|
init_releaseDoctor();
|
|
15156
15859
|
init_androidRelease();
|
|
15860
|
+
init_iosRelease();
|
|
15861
|
+
init_releasePublisher();
|
|
15157
15862
|
init_start();
|
|
15158
15863
|
init_utils();
|
|
15159
15864
|
init_getDurationString();
|
|
@@ -15171,10 +15876,10 @@ var exports_typecheck = {};
|
|
|
15171
15876
|
__export(exports_typecheck, {
|
|
15172
15877
|
typecheck: () => typecheck
|
|
15173
15878
|
});
|
|
15174
|
-
import { resolve as
|
|
15879
|
+
import { resolve as resolve39, join as join45 } from "path";
|
|
15175
15880
|
import { existsSync as existsSync42, readFileSync as readFileSync37 } from "fs";
|
|
15176
|
-
import { mkdir as
|
|
15177
|
-
var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) =>
|
|
15881
|
+
import { mkdir as mkdir11, writeFile as writeFile11 } from "fs/promises";
|
|
15882
|
+
var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve39(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
|
|
15178
15883
|
if (!existsSync42(resolveConfigPath(configPath2))) {
|
|
15179
15884
|
const defaultService = {};
|
|
15180
15885
|
return [defaultService];
|
|
@@ -15196,7 +15901,7 @@ var isCommandService3 = (service) => service.kind === "command" || Array.isArray
|
|
|
15196
15901
|
const exitCode = await proc.exited;
|
|
15197
15902
|
return { exitCode, name, output: (stdout + stderr).trim() };
|
|
15198
15903
|
}, shellEscape = (value) => `'${value.replaceAll("'", "'\\''")}'`, runShell = async (name, command) => run(name, ["/bin/bash", "-lc", command]), findBin = (name) => {
|
|
15199
|
-
const local =
|
|
15904
|
+
const local = resolve39("node_modules", ".bin", name);
|
|
15200
15905
|
return existsSync42(local) ? local : null;
|
|
15201
15906
|
}, ANSI_COLOR_REGEX, ANSI_PURPLE_REGEX, ANSI_CYAN_REGEX, ANSI_TOKEN_END_REGEX, stripAnsi4 = (str) => str.replace(ANSI_COLOR_REGEX, ""), formatSvelteOutput = (output) => {
|
|
15202
15907
|
const cwd = `${process.cwd()}/`;
|
|
@@ -15244,15 +15949,15 @@ Found ${errorCount} error${suffix}.`;
|
|
|
15244
15949
|
return formatted;
|
|
15245
15950
|
}, ABSOLUTE_INTERNAL_EXCLUDES, resolveAbsoluteTypeFile = (fileName) => {
|
|
15246
15951
|
const candidates = [
|
|
15247
|
-
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15952
|
+
resolve39("node_modules/@absolutejs/absolute/dist/types", fileName),
|
|
15953
|
+
resolve39(import.meta.dir, "../types", fileName),
|
|
15954
|
+
resolve39(import.meta.dir, "../../types", fileName),
|
|
15955
|
+
resolve39(import.meta.dir, "../../../types", fileName)
|
|
15251
15956
|
];
|
|
15252
15957
|
return candidates.find((candidate) => existsSync42(candidate)) ?? candidates[0];
|
|
15253
15958
|
}, ABSOLUTE_TYPECHECK_FILES, readProjectTsconfig = () => {
|
|
15254
15959
|
try {
|
|
15255
|
-
return JSON.parse(readFileSync37(
|
|
15960
|
+
return JSON.parse(readFileSync37(resolve39("tsconfig.json"), "utf-8"));
|
|
15256
15961
|
} catch {
|
|
15257
15962
|
return {};
|
|
15258
15963
|
}
|
|
@@ -15280,22 +15985,22 @@ Found ${errorCount} error${suffix}.`;
|
|
|
15280
15985
|
console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
|
|
15281
15986
|
process.exit(1);
|
|
15282
15987
|
}
|
|
15283
|
-
const vueTsconfigPath =
|
|
15284
|
-
return
|
|
15988
|
+
const vueTsconfigPath = join45(cacheDir, "tsconfig.vue-check.json");
|
|
15989
|
+
return writeFile11(vueTsconfigPath, JSON.stringify({
|
|
15285
15990
|
compilerOptions: {
|
|
15286
15991
|
rootDir: ".."
|
|
15287
15992
|
},
|
|
15288
15993
|
exclude: getProjectTypecheckExcludes(),
|
|
15289
|
-
extends:
|
|
15994
|
+
extends: resolve39("tsconfig.json"),
|
|
15290
15995
|
include: getProjectTypecheckIncludes()
|
|
15291
15996
|
}, null, "\t")).then(() => run("vue-tsc", [
|
|
15292
15997
|
vueTscBin,
|
|
15293
15998
|
"--noEmit",
|
|
15294
15999
|
"--project",
|
|
15295
|
-
|
|
16000
|
+
resolve39(vueTsconfigPath),
|
|
15296
16001
|
"--incremental",
|
|
15297
16002
|
"--tsBuildInfoFile",
|
|
15298
|
-
|
|
16003
|
+
join45(cacheDir, "vue-tsc.tsbuildinfo"),
|
|
15299
16004
|
"--pretty"
|
|
15300
16005
|
]));
|
|
15301
16006
|
}, buildAngularCheck = async (cacheDir, angularDir) => {
|
|
@@ -15304,8 +16009,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
15304
16009
|
console.error("\x1B[31m\u2717\x1B[0m @angular/compiler-cli is required for Angular type checking. Install it: bun add -d @angular/compiler-cli");
|
|
15305
16010
|
process.exit(1);
|
|
15306
16011
|
}
|
|
15307
|
-
const angularTsconfigPath =
|
|
15308
|
-
await
|
|
16012
|
+
const angularTsconfigPath = join45(cacheDir, "tsconfig.angular-check.json");
|
|
16013
|
+
await writeFile11(angularTsconfigPath, JSON.stringify({
|
|
15309
16014
|
angularCompilerOptions: {
|
|
15310
16015
|
strictTemplates: true
|
|
15311
16016
|
},
|
|
@@ -15314,32 +16019,32 @@ Found ${errorCount} error${suffix}.`;
|
|
|
15314
16019
|
rootDir: ".."
|
|
15315
16020
|
},
|
|
15316
16021
|
exclude: ABSOLUTE_INTERNAL_EXCLUDES.map(toGeneratedConfigPath),
|
|
15317
|
-
extends:
|
|
16022
|
+
extends: resolve39("tsconfig.json"),
|
|
15318
16023
|
include: [`../${angularDir}/**/*`]
|
|
15319
16024
|
}, null, "\t"));
|
|
15320
|
-
return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(
|
|
16025
|
+
return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(resolve39(angularTsconfigPath))}`);
|
|
15321
16026
|
}, buildTscCheck = (cacheDir) => {
|
|
15322
16027
|
const tscBin = findBin("tsc");
|
|
15323
16028
|
if (!tscBin) {
|
|
15324
16029
|
console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
|
|
15325
16030
|
process.exit(1);
|
|
15326
16031
|
}
|
|
15327
|
-
const tscConfigPath =
|
|
15328
|
-
return
|
|
16032
|
+
const tscConfigPath = join45(cacheDir, "tsconfig.typecheck.json");
|
|
16033
|
+
return writeFile11(tscConfigPath, JSON.stringify({
|
|
15329
16034
|
compilerOptions: {
|
|
15330
16035
|
rootDir: ".."
|
|
15331
16036
|
},
|
|
15332
16037
|
exclude: getProjectTypecheckExcludes(),
|
|
15333
|
-
extends:
|
|
16038
|
+
extends: resolve39("tsconfig.json"),
|
|
15334
16039
|
include: getProjectTypecheckIncludes()
|
|
15335
16040
|
}, null, "\t")).then(() => run("tsc", [
|
|
15336
16041
|
tscBin,
|
|
15337
16042
|
"--noEmit",
|
|
15338
16043
|
"--project",
|
|
15339
|
-
|
|
16044
|
+
resolve39(tscConfigPath),
|
|
15340
16045
|
"--incremental",
|
|
15341
16046
|
"--tsBuildInfoFile",
|
|
15342
|
-
|
|
16047
|
+
join45(cacheDir, "tsc.tsbuildinfo"),
|
|
15343
16048
|
"--pretty"
|
|
15344
16049
|
]));
|
|
15345
16050
|
}, buildSvelteCheck = async (cacheDir, svelteDir) => {
|
|
@@ -15348,16 +16053,16 @@ Found ${errorCount} error${suffix}.`;
|
|
|
15348
16053
|
console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
|
|
15349
16054
|
process.exit(1);
|
|
15350
16055
|
}
|
|
15351
|
-
const svelteTsconfigPath =
|
|
15352
|
-
await
|
|
15353
|
-
extends:
|
|
16056
|
+
const svelteTsconfigPath = join45(cacheDir, "tsconfig.svelte-check.json");
|
|
16057
|
+
await writeFile11(svelteTsconfigPath, JSON.stringify({
|
|
16058
|
+
extends: resolve39("tsconfig.json"),
|
|
15354
16059
|
files: ABSOLUTE_TYPECHECK_FILES,
|
|
15355
16060
|
include: [`../${svelteDir}/**/*`]
|
|
15356
16061
|
}, null, "\t"));
|
|
15357
16062
|
return run("svelte-check", [
|
|
15358
16063
|
svelteBin,
|
|
15359
16064
|
"--tsconfig",
|
|
15360
|
-
|
|
16065
|
+
resolve39(svelteTsconfigPath),
|
|
15361
16066
|
"--threshold",
|
|
15362
16067
|
"error",
|
|
15363
16068
|
"--compiler-warnings",
|
|
@@ -15378,7 +16083,7 @@ Found ${errorCount} error${suffix}.`;
|
|
|
15378
16083
|
...new Set(targets.map((config) => config.angularDirectory).filter((dir) => typeof dir === "string" && dir.length > 0))
|
|
15379
16084
|
];
|
|
15380
16085
|
const cacheDir = ".absolutejs";
|
|
15381
|
-
await
|
|
16086
|
+
await mkdir11(cacheDir, { recursive: true });
|
|
15382
16087
|
const checks = [];
|
|
15383
16088
|
checks.push(hasVue ? buildVueTscCheck(cacheDir) : buildTscCheck(cacheDir));
|
|
15384
16089
|
for (const svelteDir of hasSvelte ? svelteDirs : []) {
|
|
@@ -15551,11 +16256,11 @@ var DEFAULT_RELAY_PORT = 8787, DEFAULT_REQUEST_TIMEOUT_MS = 30000, headersToObje
|
|
|
15551
16256
|
url: url.pathname + url.search,
|
|
15552
16257
|
...bodyBytes && bodyBytes.length > 0 ? { bodyBase64: Buffer.from(bodyBytes).toString("base64") } : {}
|
|
15553
16258
|
};
|
|
15554
|
-
const responsePromise = new Promise((
|
|
15555
|
-
pending.set(id,
|
|
16259
|
+
const responsePromise = new Promise((resolve40) => {
|
|
16260
|
+
pending.set(id, resolve40);
|
|
15556
16261
|
});
|
|
15557
16262
|
client.send(encodeTunnelMessage(message));
|
|
15558
|
-
const timeout = new Promise((
|
|
16263
|
+
const timeout = new Promise((resolve40) => setTimeout(() => resolve40({ id, message: "timeout", type: "error" }), requestTimeoutMs));
|
|
15559
16264
|
const result = await Promise.race([responsePromise, timeout]);
|
|
15560
16265
|
pending.delete(id);
|
|
15561
16266
|
if (result.type === "error") {
|