@absolutejs/absolute 0.20.0-beta.63 → 0.20.0-beta.64
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 +464 -250
- package/dist/mobile/index.js +1189 -1023
- package/dist/mobile/index.js.map +9 -8
- package/dist/mobile/remoteMacAgentEntry.js +233 -200
- package/dist/src/mobile/expoUpdate.d.ts +27 -0
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/updateRuntime.d.ts +12 -2
- package/package.json +7 -7
package/dist/cli/index.js
CHANGED
|
@@ -1693,6 +1693,158 @@ var init_deviceCapabilities = __esm(() => {
|
|
|
1693
1693
|
];
|
|
1694
1694
|
});
|
|
1695
1695
|
|
|
1696
|
+
// src/mobile/updateProtocol.ts
|
|
1697
|
+
var ABSOLUTE_MOBILE_UPDATE_FORMAT = 1, ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES, ABSOLUTE_MOBILE_UPDATE_MAX_FILES = 1e4, ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES, ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM = "ecdsa-p256-sha256", HASH_PATTERN, RELEASE_PATTERN, KEY_ID_PATTERN, CHANNEL_PATTERN, isClassification = (value) => value === "bug-fix" || value === "content" || value === "security", object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), canonicalValue = (value) => {
|
|
1698
|
+
if (Array.isArray(value))
|
|
1699
|
+
return value.map(canonicalValue);
|
|
1700
|
+
if (!object3(value))
|
|
1701
|
+
return value;
|
|
1702
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalValue(value[key])]));
|
|
1703
|
+
}, absoluteMobileUpdateSigningPayload = (manifest) => new TextEncoder().encode(canonicalizeAbsoluteMobileUpdate(manifest)), canonicalizeAbsoluteMobileUpdate = (value) => JSON.stringify(canonicalValue(value)), requireText2 = (value, field) => {
|
|
1704
|
+
if (typeof value !== "string" || value.length === 0)
|
|
1705
|
+
throw new TypeError(`${field} must be a non-empty string.`);
|
|
1706
|
+
return value;
|
|
1707
|
+
}, normalizeAbsoluteMobileUpdatePath = (value) => {
|
|
1708
|
+
const path = requireText2(value, "Update file path").replaceAll("\\", "/");
|
|
1709
|
+
if (path.startsWith("/") || path.includes("\x00") || path.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
1710
|
+
throw new TypeError("Update file paths must be normalized relative paths.");
|
|
1711
|
+
}
|
|
1712
|
+
return path;
|
|
1713
|
+
}, parseFile = (value) => {
|
|
1714
|
+
if (!object3(value))
|
|
1715
|
+
throw new TypeError("Update files must be objects.");
|
|
1716
|
+
const path = normalizeAbsoluteMobileUpdatePath(value.path);
|
|
1717
|
+
if (typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes < 0 || value.bytes > ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES) {
|
|
1718
|
+
throw new TypeError(`Update file ${path} has an invalid byte length.`);
|
|
1719
|
+
}
|
|
1720
|
+
if (typeof value.sha256 !== "string" || !HASH_PATTERN.test(value.sha256))
|
|
1721
|
+
throw new TypeError(`Update file ${path} has an invalid SHA-256 digest.`);
|
|
1722
|
+
return { bytes: value.bytes, path, sha256: value.sha256 };
|
|
1723
|
+
}, parseAbsoluteMobileUnsignedUpdateManifest = (value) => {
|
|
1724
|
+
if (!object3(value) || value.format !== ABSOLUTE_MOBILE_UPDATE_FORMAT)
|
|
1725
|
+
throw new TypeError("Invalid AbsoluteJS mobile update manifest.");
|
|
1726
|
+
const appId = requireText2(value.appId, "Update appId");
|
|
1727
|
+
const channel = requireText2(value.channel, "Update channel");
|
|
1728
|
+
if (!CHANNEL_PATTERN.test(channel))
|
|
1729
|
+
throw new TypeError("Update channel contains unsupported characters.");
|
|
1730
|
+
if (!isClassification(value.classification))
|
|
1731
|
+
throw new TypeError("Update classification is invalid.");
|
|
1732
|
+
const createdAt = requireText2(value.createdAt, "Update createdAt");
|
|
1733
|
+
if (!Number.isFinite(Date.parse(createdAt)) || new Date(createdAt).toISOString() !== createdAt)
|
|
1734
|
+
throw new TypeError("Update createdAt must be a canonical ISO timestamp.");
|
|
1735
|
+
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
1736
|
+
throw new TypeError("An update must contain at least one file.");
|
|
1737
|
+
if (value.files.length > ABSOLUTE_MOBILE_UPDATE_MAX_FILES)
|
|
1738
|
+
throw new TypeError("Update contains too many files.");
|
|
1739
|
+
const files = value.files.map(parseFile);
|
|
1740
|
+
const sorted = [...files].sort((left, right) => left.path.localeCompare(right.path));
|
|
1741
|
+
if (files.some((file, index) => file.path !== sorted[index]?.path))
|
|
1742
|
+
throw new TypeError("Update files must be sorted by path.");
|
|
1743
|
+
if (new Set(files.map(({ path }) => path)).size !== files.length)
|
|
1744
|
+
throw new TypeError("Update file paths must be unique.");
|
|
1745
|
+
if (files.reduce((total, file) => total + file.bytes, 0) > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
|
|
1746
|
+
throw new TypeError("Update exceeds the maximum uncompressed size.");
|
|
1747
|
+
if (typeof value.releaseId !== "string" || !RELEASE_PATTERN.test(value.releaseId))
|
|
1748
|
+
throw new TypeError("Update releaseId is invalid.");
|
|
1749
|
+
if (typeof value.runtimeFingerprint !== "string" || !HASH_PATTERN.test(value.runtimeFingerprint))
|
|
1750
|
+
throw new TypeError("Update runtime fingerprint is invalid.");
|
|
1751
|
+
if (value.withinSubmittedPurpose !== true)
|
|
1752
|
+
throw new TypeError("OTA updates must attest that they remain within the submitted app purpose.");
|
|
1753
|
+
return {
|
|
1754
|
+
appId,
|
|
1755
|
+
channel,
|
|
1756
|
+
classification: value.classification,
|
|
1757
|
+
createdAt,
|
|
1758
|
+
files,
|
|
1759
|
+
format: ABSOLUTE_MOBILE_UPDATE_FORMAT,
|
|
1760
|
+
releaseId: value.releaseId,
|
|
1761
|
+
runtimeFingerprint: value.runtimeFingerprint,
|
|
1762
|
+
withinSubmittedPurpose: true
|
|
1763
|
+
};
|
|
1764
|
+
}, parseAbsoluteMobileUpdateManifest = (value) => {
|
|
1765
|
+
if (!object3(value))
|
|
1766
|
+
throw new TypeError("Invalid AbsoluteJS mobile update manifest.");
|
|
1767
|
+
const { signature: signatureValue, ...unsignedValue } = value;
|
|
1768
|
+
const unsigned = parseAbsoluteMobileUnsignedUpdateManifest(unsignedValue);
|
|
1769
|
+
if (!object3(signatureValue))
|
|
1770
|
+
throw new TypeError("Update signature is missing.");
|
|
1771
|
+
if (signatureValue.algorithm !== ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM)
|
|
1772
|
+
throw new TypeError("Update signature algorithm is unsupported.");
|
|
1773
|
+
const keyId = requireText2(signatureValue.keyId, "Update signature keyId");
|
|
1774
|
+
if (!KEY_ID_PATTERN.test(keyId))
|
|
1775
|
+
throw new TypeError("Update signature keyId is invalid.");
|
|
1776
|
+
const signature = requireText2(signatureValue.value, "Update signature value");
|
|
1777
|
+
let signatureBytes;
|
|
1778
|
+
try {
|
|
1779
|
+
signatureBytes = Uint8Array.from(atob(signature), (character) => character.charCodeAt(0));
|
|
1780
|
+
} catch {
|
|
1781
|
+
signatureBytes = new Uint8Array;
|
|
1782
|
+
}
|
|
1783
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(signature) || signatureBytes.byteLength !== 64 || btoa(String.fromCharCode(...signatureBytes)) !== signature)
|
|
1784
|
+
throw new TypeError("Update signature is not canonical base64.");
|
|
1785
|
+
return {
|
|
1786
|
+
...unsigned,
|
|
1787
|
+
signature: {
|
|
1788
|
+
algorithm: ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM,
|
|
1789
|
+
keyId,
|
|
1790
|
+
value: signature
|
|
1791
|
+
}
|
|
1792
|
+
};
|
|
1793
|
+
}, unsignedAbsoluteMobileUpdate = (manifest) => {
|
|
1794
|
+
const { signature: _signature, ...unsigned } = manifest;
|
|
1795
|
+
return unsigned;
|
|
1796
|
+
};
|
|
1797
|
+
var init_updateProtocol = __esm(() => {
|
|
1798
|
+
ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
1799
|
+
ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
1800
|
+
HASH_PATTERN = /^[a-f0-9]{64}$/u;
|
|
1801
|
+
RELEASE_PATTERN = /^amu_[a-f0-9]{64}$/u;
|
|
1802
|
+
KEY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
1803
|
+
CHANNEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1806
|
+
// src/mobile/updateRuntime.ts
|
|
1807
|
+
import { createHash } from "crypto";
|
|
1808
|
+
var ABSOLUTE_MOBILE_SHELL_ABI = 2, ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT = 1, createAbsoluteMobileUpdateRuntimeDescriptor = (options) => ({
|
|
1809
|
+
appId: options.config.appId,
|
|
1810
|
+
auth: options.auth ?? null,
|
|
1811
|
+
deepLinks: {
|
|
1812
|
+
hosts: options.config.deepLinkHosts,
|
|
1813
|
+
...options.config.deepLinkScheme ? { scheme: options.config.deepLinkScheme } : {}
|
|
1814
|
+
},
|
|
1815
|
+
deviceCapabilities: options.deviceCapabilities,
|
|
1816
|
+
engine: options.config.engine,
|
|
1817
|
+
format: ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT,
|
|
1818
|
+
shellAbi: ABSOLUTE_MOBILE_SHELL_ABI,
|
|
1819
|
+
syncSchema: options.syncSchema ?? null,
|
|
1820
|
+
updates: options.config.updates ?? null
|
|
1821
|
+
}), fingerprintAbsoluteMobileUpdateRuntime = (descriptor) => createHash("sha256").update(canonicalizeAbsoluteMobileUpdate(descriptor)).digest("hex"), resolveAbsoluteMobileUpdateRuntime = (config, projectRoot) => {
|
|
1822
|
+
const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
|
|
1823
|
+
const sync = auth !== undefined && projectUsesAbsoluteSync(projectRoot);
|
|
1824
|
+
const syncSchema = sync ? discoverAbsoluteSyncSchema(projectRoot) : undefined;
|
|
1825
|
+
const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(projectRoot, config.engine);
|
|
1826
|
+
const descriptor = createAbsoluteMobileUpdateRuntimeDescriptor({
|
|
1827
|
+
...auth ? { auth } : {},
|
|
1828
|
+
config,
|
|
1829
|
+
deviceCapabilities,
|
|
1830
|
+
...syncSchema ? { syncSchema } : {}
|
|
1831
|
+
});
|
|
1832
|
+
return {
|
|
1833
|
+
auth,
|
|
1834
|
+
descriptor,
|
|
1835
|
+
deviceCapabilities,
|
|
1836
|
+
fingerprint: fingerprintAbsoluteMobileUpdateRuntime(descriptor),
|
|
1837
|
+
sync,
|
|
1838
|
+
syncSchema
|
|
1839
|
+
};
|
|
1840
|
+
};
|
|
1841
|
+
var init_updateRuntime = __esm(() => {
|
|
1842
|
+
init_deviceCapabilities();
|
|
1843
|
+
init_nativeAuth();
|
|
1844
|
+
init_updateProtocol();
|
|
1845
|
+
init_syncSchema();
|
|
1846
|
+
});
|
|
1847
|
+
|
|
1696
1848
|
// src/mobile/expoProject.ts
|
|
1697
1849
|
import {
|
|
1698
1850
|
access,
|
|
@@ -1705,7 +1857,7 @@ import {
|
|
|
1705
1857
|
rm,
|
|
1706
1858
|
writeFile
|
|
1707
1859
|
} from "fs/promises";
|
|
1708
|
-
import { createHash } from "crypto";
|
|
1860
|
+
import { createHash as createHash2 } from "crypto";
|
|
1709
1861
|
import { basename as basename2, dirname as dirname4, join as join8, relative as relative2, resolve as resolve5, sep } from "path";
|
|
1710
1862
|
var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
|
|
1711
1863
|
`, EXPO_ASSET_EXTENSION = ".absasset", EXPO_PROJECT_MARKER = ".absolutejs-expo-project", exists = async (path) => {
|
|
@@ -1772,7 +1924,7 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
|
|
|
1772
1924
|
}, routeFile = (project, route) => join8(project, "app", ...routeSegments(route), "index.tsx"), packageDependencies = (plan) => Object.fromEntries(plan.requiredPackages.map((spec) => {
|
|
1773
1925
|
const separator = spec.lastIndexOf("@");
|
|
1774
1926
|
return [spec.slice(0, separator), spec.slice(separator + 1)];
|
|
1775
|
-
})), expoPackage = (auth, sync, devices) => ({
|
|
1927
|
+
})), expoPackage = (auth, sync, devices, updates) => ({
|
|
1776
1928
|
dependencies: {
|
|
1777
1929
|
"@absolutejs/devices": "0.7.0",
|
|
1778
1930
|
"@absolutejs/devices-expo": "0.0.2",
|
|
@@ -1787,6 +1939,7 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
|
|
|
1787
1939
|
expo: "~57.0.9",
|
|
1788
1940
|
"expo-asset": "~57.0.15",
|
|
1789
1941
|
"expo-constants": "~57.0.16",
|
|
1942
|
+
...updates ? { "expo-crypto": "~57.0.1" } : {},
|
|
1790
1943
|
"expo-dev-client": "~57.0.16",
|
|
1791
1944
|
"expo-file-system": "~57.0.6",
|
|
1792
1945
|
"expo-haptics": "~57.0.2",
|
|
@@ -1795,13 +1948,13 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
|
|
|
1795
1948
|
"expo-background-task": "~57.0.14",
|
|
1796
1949
|
"expo-network": "~57.0.1",
|
|
1797
1950
|
"expo-sqlite": "~57.0.2",
|
|
1798
|
-
"expo-task-manager": "~57.0.14"
|
|
1799
|
-
"expo-updates": "~57.0.19"
|
|
1951
|
+
"expo-task-manager": "~57.0.14"
|
|
1800
1952
|
} : {},
|
|
1953
|
+
...updates || sync ? { "expo-updates": "~57.0.19" } : {},
|
|
1801
1954
|
"expo-router": "~57.0.17",
|
|
1802
|
-
...auth ? {
|
|
1955
|
+
...auth || updates ? {
|
|
1803
1956
|
"expo-secure-store": "~57.0.2",
|
|
1804
|
-
"expo-web-browser": "~57.0.2"
|
|
1957
|
+
...auth ? { "expo-web-browser": "~57.0.2" } : {}
|
|
1805
1958
|
} : {},
|
|
1806
1959
|
react: "19.2.3",
|
|
1807
1960
|
"react-native": "0.86.3",
|
|
@@ -1823,7 +1976,7 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
|
|
|
1823
1976
|
start: "expo start --dev-client"
|
|
1824
1977
|
},
|
|
1825
1978
|
version: "0.0.0"
|
|
1826
|
-
}), expoAppConfig = (config, auth, sync, devices) => {
|
|
1979
|
+
}), expoAppConfig = (config, auth, sync, devices, runtimeFingerprint) => {
|
|
1827
1980
|
const requirements = absoluteDeviceNativeRequirements(devices);
|
|
1828
1981
|
const usageKey = (purpose) => {
|
|
1829
1982
|
if (purpose === "camera")
|
|
@@ -1909,7 +2062,7 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
|
|
|
1909
2062
|
plugins: [
|
|
1910
2063
|
"expo-router",
|
|
1911
2064
|
["expo-dev-client", { launchMode: "most-recent" }],
|
|
1912
|
-
...auth ? ["expo-secure-store"] : [],
|
|
2065
|
+
...auth || config.updates ? ["expo-secure-store"] : [],
|
|
1913
2066
|
...sync ? [
|
|
1914
2067
|
"expo-sqlite",
|
|
1915
2068
|
"expo-background-task",
|
|
@@ -1917,9 +2070,20 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
|
|
|
1917
2070
|
] : [],
|
|
1918
2071
|
...configuredPlugins
|
|
1919
2072
|
],
|
|
1920
|
-
runtimeVersion:
|
|
2073
|
+
runtimeVersion: runtimeFingerprint,
|
|
1921
2074
|
scheme: config.deepLinkScheme,
|
|
1922
2075
|
slug: config.appId.toLowerCase().replaceAll(".", "-"),
|
|
2076
|
+
...config.updates ? {
|
|
2077
|
+
updates: {
|
|
2078
|
+
checkAutomatically: "NEVER",
|
|
2079
|
+
fallbackToCacheTimeout: config.updates.bootTimeoutMs,
|
|
2080
|
+
requestHeaders: {
|
|
2081
|
+
"x-absolute-mobile-app": config.appId,
|
|
2082
|
+
"x-absolute-mobile-channel": config.updates.channel
|
|
2083
|
+
},
|
|
2084
|
+
url: config.updates.manifestUrl
|
|
2085
|
+
}
|
|
2086
|
+
} : {},
|
|
1923
2087
|
version: config.iosVersion ?? "0.1.0"
|
|
1924
2088
|
}
|
|
1925
2089
|
};
|
|
@@ -1937,13 +2101,16 @@ config.resolver.nodeModulesPaths = [
|
|
|
1937
2101
|
config.watchFolders = [appRoot];
|
|
1938
2102
|
|
|
1939
2103
|
module.exports = config;
|
|
1940
|
-
`, layoutSource = (auth, sync) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
|
|
2104
|
+
`, layoutSource = (auth, sync, updates) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
|
|
1941
2105
|
import '../src/generated/AbsoluteDevices';
|
|
1942
2106
|
${auth ? `import { useEffect, useState } from 'react';
|
|
1943
2107
|
import { startAbsoluteExpoAuth } from '../src/generated/AbsoluteAuth';` : ""}
|
|
1944
2108
|
${sync ? "import { startAbsoluteExpoSync } from '../src/generated/AbsoluteSync';" : ""}
|
|
2109
|
+
${updates ? `import { useEffect as useUpdateEffect } from 'react';
|
|
2110
|
+
import { startAbsoluteExpoUpdates } from '../src/generated/AbsoluteUpdates';` : ""}
|
|
1945
2111
|
|
|
1946
2112
|
export default function AbsoluteLayout() {
|
|
2113
|
+
${updates ? "useUpdateEffect(() => { void startAbsoluteExpoUpdates(); }, []);" : ""}
|
|
1947
2114
|
${auth ? `const [ready, setReady] = useState(false);
|
|
1948
2115
|
useEffect(() => {
|
|
1949
2116
|
let active = true;
|
|
@@ -1953,6 +2120,39 @@ export default function AbsoluteLayout() {
|
|
|
1953
2120
|
if (!ready) return null;` : ""}
|
|
1954
2121
|
return <Stack screenOptions={{ headerShown: false }} />;
|
|
1955
2122
|
}
|
|
2123
|
+
`, updatesRuntimeSource = (config) => `${EXPO_GENERATED_HEADER}import { randomUUID } from 'expo-crypto';
|
|
2124
|
+
import * as SecureStore from 'expo-secure-store';
|
|
2125
|
+
import * as Updates from 'expo-updates';
|
|
2126
|
+
|
|
2127
|
+
const INSTALLATION_KEY = 'absolutejs.mobile.update.installation.v1';
|
|
2128
|
+
const REQUEST_HEADERS = ${JSON.stringify({
|
|
2129
|
+
"x-absolute-mobile-app": config.appId,
|
|
2130
|
+
"x-absolute-mobile-channel": config.updates?.channel
|
|
2131
|
+
})};
|
|
2132
|
+
|
|
2133
|
+
let startPromise: Promise<void> | undefined;
|
|
2134
|
+
export const startAbsoluteExpoUpdates = () => {
|
|
2135
|
+
startPromise ??= (async () => {
|
|
2136
|
+
if (!Updates.isEnabled || __DEV__) return;
|
|
2137
|
+
let installationId = await SecureStore.getItemAsync(INSTALLATION_KEY);
|
|
2138
|
+
if (!installationId) {
|
|
2139
|
+
installationId = randomUUID();
|
|
2140
|
+
await SecureStore.setItemAsync(INSTALLATION_KEY, installationId);
|
|
2141
|
+
}
|
|
2142
|
+
await Updates.setUpdateRequestHeadersOverride({
|
|
2143
|
+
...REQUEST_HEADERS,
|
|
2144
|
+
'x-absolute-mobile-installation': installationId
|
|
2145
|
+
});
|
|
2146
|
+
const result = await Updates.checkForUpdateAsync();
|
|
2147
|
+
if (result.isAvailable || result.isRollBackToEmbedded) {
|
|
2148
|
+
await Updates.fetchUpdateAsync();
|
|
2149
|
+
}
|
|
2150
|
+
})().catch(error => {
|
|
2151
|
+
console.warn('[absolute:mobile-update] Expo update check failed', error instanceof Error ? error.message : 'unknown error');
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
return startPromise;
|
|
2155
|
+
};
|
|
1956
2156
|
`, nativeDiagnosticSource, devicesRuntimeSource = (config, plan, auth) => {
|
|
1957
2157
|
const imports = plan.capabilities.map((name, index) => {
|
|
1958
2158
|
const provider = plan.providers[name];
|
|
@@ -2023,7 +2223,7 @@ ${push ? `absoluteExpoAuth.onPrincipalChange(principal => {
|
|
|
2023
2223
|
});` : ""}
|
|
2024
2224
|
`;
|
|
2025
2225
|
}, authRuntimeSource = (auth, appId) => {
|
|
2026
|
-
const storageIdentity =
|
|
2226
|
+
const storageIdentity = createHash2("sha256").update(appId).digest("hex").slice(0, 24);
|
|
2027
2227
|
return `${EXPO_GENERATED_HEADER}import { createAbsoluteExpoAuthClient } from '@absolutejs/auth-expo';
|
|
2028
2228
|
import { createMobileAuthTransport, installAuthClientRuntimeTransport } from '@absolutejs/auth/client/mobile';
|
|
2029
2229
|
|
|
@@ -2054,7 +2254,7 @@ export const startAbsoluteExpoAuth = () => {
|
|
|
2054
2254
|
};
|
|
2055
2255
|
`;
|
|
2056
2256
|
}, syncRuntimeSource = (config, schema) => {
|
|
2057
|
-
const storageIdentity =
|
|
2257
|
+
const storageIdentity = createHash2("sha256").update(config.appId).digest("hex").slice(0, 24);
|
|
2058
2258
|
const backgroundEndpoint = new URL("/__absolute/sync/background", config.productionOrigin).href;
|
|
2059
2259
|
const backgroundTask = `${config.appId}.absolutejs.sync`;
|
|
2060
2260
|
return `${EXPO_GENERATED_HEADER}import { runHeadlessSync } from '@absolutejs/sync/client/headless';
|
|
@@ -2669,6 +2869,7 @@ export default createAbsoluteNativeRoute(ApplicationNativeRoute, ${JSON.stringif
|
|
|
2669
2869
|
const devices = resolveAbsoluteDeviceCapabilityPlan(projectRoot, "expo");
|
|
2670
2870
|
const syncEnabled = Boolean(auth && projectUsesAbsoluteSync(projectRoot));
|
|
2671
2871
|
const syncSchema = syncEnabled ? { components: discoverAbsoluteSyncSchema(projectRoot).components } : undefined;
|
|
2872
|
+
const runtimeFingerprint = resolveAbsoluteMobileUpdateRuntime(config, projectRoot).fingerprint;
|
|
2672
2873
|
const routeModules = Object.entries(config.expoNativeRoutes);
|
|
2673
2874
|
const moduleChecks = await Promise.all(routeModules.map(async ([route, module]) => ({
|
|
2674
2875
|
exists: await exists(module),
|
|
@@ -2701,12 +2902,12 @@ node_modules/
|
|
|
2701
2902
|
],
|
|
2702
2903
|
[
|
|
2703
2904
|
join8(project, "app.json"),
|
|
2704
|
-
jsonSource(expoAppConfig(config, authEnabled, syncEnabled, devices))
|
|
2905
|
+
jsonSource(expoAppConfig(config, authEnabled, syncEnabled, devices, runtimeFingerprint))
|
|
2705
2906
|
],
|
|
2706
2907
|
[join8(project, "app.config.js"), expoDynamicAppConfig],
|
|
2707
2908
|
[
|
|
2708
2909
|
join8(project, "package.json"),
|
|
2709
|
-
jsonSource(expoPackage(authEnabled, syncEnabled, devices))
|
|
2910
|
+
jsonSource(expoPackage(authEnabled, syncEnabled, devices, config.updates !== undefined))
|
|
2710
2911
|
],
|
|
2711
2912
|
[join8(project, "metro.config.js"), metroConfig(projectRoot)],
|
|
2712
2913
|
[
|
|
@@ -2719,7 +2920,7 @@ node_modules/
|
|
|
2719
2920
|
],
|
|
2720
2921
|
[
|
|
2721
2922
|
join8(project, "app", "_layout.tsx"),
|
|
2722
|
-
layoutSource(authEnabled, syncEnabled)
|
|
2923
|
+
layoutSource(authEnabled, syncEnabled, config.updates !== undefined)
|
|
2723
2924
|
],
|
|
2724
2925
|
[
|
|
2725
2926
|
join8(project, "app", "__absolute", "native", "index.tsx"),
|
|
@@ -2738,6 +2939,9 @@ node_modules/
|
|
|
2738
2939
|
webHostSource(config, auth, syncEnabled)
|
|
2739
2940
|
]
|
|
2740
2941
|
]);
|
|
2942
|
+
if (config.updates) {
|
|
2943
|
+
files.set(join8(project, "src", "generated", "AbsoluteUpdates.ts"), updatesRuntimeSource(config));
|
|
2944
|
+
}
|
|
2741
2945
|
if (auth) {
|
|
2742
2946
|
files.set(join8(project, "src", "generated", "AbsoluteAuth.ts"), authRuntimeSource(auth, config.appId));
|
|
2743
2947
|
}
|
|
@@ -2836,7 +3040,7 @@ export const materializeAbsoluteWebBundle = async () => {
|
|
|
2836
3040
|
const nativeDataManifest = expoNativeDataManifest(manifest);
|
|
2837
3041
|
const { appBuild } = nativeDataManifest;
|
|
2838
3042
|
const files = await walkFiles(config.bundleDirectory);
|
|
2839
|
-
const bundleHash =
|
|
3043
|
+
const bundleHash = createHash2("sha256");
|
|
2840
3044
|
const filesWithContents = await Promise.all(files.map(async (file) => ({ contents: await readFile(file), file })));
|
|
2841
3045
|
filesWithContents.forEach(({ contents, file }) => {
|
|
2842
3046
|
bundleHash.update(relative2(config.bundleDirectory, file).replaceAll("\\", "/"));
|
|
@@ -2871,6 +3075,7 @@ var init_expoProject = __esm(() => {
|
|
|
2871
3075
|
init_nativeAuth();
|
|
2872
3076
|
init_syncSchema();
|
|
2873
3077
|
init_deviceCapabilities();
|
|
3078
|
+
init_updateRuntime();
|
|
2874
3079
|
expoDynamicAppConfig = `${EXPO_GENERATED_HEADER}const config = require('./app.json');
|
|
2875
3080
|
|
|
2876
3081
|
if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH) {
|
|
@@ -3471,7 +3676,7 @@ import {
|
|
|
3471
3676
|
rm as rm2,
|
|
3472
3677
|
writeFile as writeFile3
|
|
3473
3678
|
} from "fs/promises";
|
|
3474
|
-
import { createHash as
|
|
3679
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
3475
3680
|
import {
|
|
3476
3681
|
dirname as dirname5,
|
|
3477
3682
|
isAbsolute,
|
|
@@ -3727,7 +3932,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
|
|
|
3727
3932
|
lstat(path),
|
|
3728
3933
|
readFile4(path)
|
|
3729
3934
|
]);
|
|
3730
|
-
const contentDigest =
|
|
3935
|
+
const contentDigest = createHash3("sha256").update(contents).digest("hex");
|
|
3731
3936
|
return [
|
|
3732
3937
|
`file\x00${identity}${metadata.mode & EXECUTABLE_MODE_MASK}\x00${contentDigest}\x00`
|
|
3733
3938
|
];
|
|
@@ -3739,7 +3944,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
|
|
|
3739
3944
|
}, hashNativeTree = async (root, label, ignorePublicBundle) => {
|
|
3740
3945
|
const resolvedRoot = await realpath(root);
|
|
3741
3946
|
const records = await collectNativeDirectory(resolvedRoot, label, resolvedRoot, ignorePublicBundle);
|
|
3742
|
-
return
|
|
3947
|
+
return createHash3("sha256").update(records.join("")).digest("hex");
|
|
3743
3948
|
}, fingerprintAbsoluteAndroidNativeProject = async (project, options = {}) => {
|
|
3744
3949
|
const { dependencies } = await nativeDependencySources(project.nativeDirectory);
|
|
3745
3950
|
const roots = [
|
|
@@ -3755,7 +3960,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
|
|
|
3755
3960
|
}))
|
|
3756
3961
|
];
|
|
3757
3962
|
const treeDigests = await Promise.all(roots.map((root) => hashNativeTree(root.source, root.label, root.ignorePublicBundle)));
|
|
3758
|
-
return
|
|
3963
|
+
return createHash3("sha256").update(treeDigests.join("\x00")).digest("hex");
|
|
3759
3964
|
}, parseJournal = (value) => {
|
|
3760
3965
|
if (typeof value !== "object" || value === null)
|
|
3761
3966
|
return null;
|
|
@@ -4476,7 +4681,7 @@ var init_androidEmulatorController = __esm(() => {
|
|
|
4476
4681
|
});
|
|
4477
4682
|
|
|
4478
4683
|
// src/mobile/emulatorInstaller.ts
|
|
4479
|
-
import { createHash as
|
|
4684
|
+
import { createHash as createHash4 } from "crypto";
|
|
4480
4685
|
import { cp as cp2, mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile5, rm as rm3 } from "fs/promises";
|
|
4481
4686
|
import { tmpdir } from "os";
|
|
4482
4687
|
import { basename as basename3, join as join12 } from "path";
|
|
@@ -4581,7 +4786,7 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
|
|
|
4581
4786
|
const release = releaseFor(plan.host, input.arch);
|
|
4582
4787
|
input.log(`Downloading ${basename3(release.url)}...`);
|
|
4583
4788
|
const bytes = await input.download(release.url);
|
|
4584
|
-
const digest =
|
|
4789
|
+
const digest = createHash4("sha256").update(bytes).digest("hex");
|
|
4585
4790
|
if (digest !== release.sha256) {
|
|
4586
4791
|
throw new Error(`Android command-line-tools checksum mismatch: expected ${release.sha256}, received ${digest}.`);
|
|
4587
4792
|
}
|
|
@@ -4782,7 +4987,7 @@ var init_emulatorInstaller = __esm(() => {
|
|
|
4782
4987
|
});
|
|
4783
4988
|
|
|
4784
4989
|
// src/mobile/iosRelease.ts
|
|
4785
|
-
import { createHash as
|
|
4990
|
+
import { createHash as createHash5 } from "crypto";
|
|
4786
4991
|
import {
|
|
4787
4992
|
access as access6,
|
|
4788
4993
|
copyFile as copyFile2,
|
|
@@ -4863,7 +5068,7 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
|
|
|
4863
5068
|
}));
|
|
4864
5069
|
return nested.flat();
|
|
4865
5070
|
}, fingerprintAbsoluteIosNativeProject = async (nativeDirectory, options = {}) => {
|
|
4866
|
-
const hasher =
|
|
5071
|
+
const hasher = createHash5("sha256");
|
|
4867
5072
|
const files = await fingerprintFiles(nativeDirectory, nativeDirectory, options);
|
|
4868
5073
|
const contents = await Promise.all(files.map((file) => readFile6(file)));
|
|
4869
5074
|
files.forEach((file, index) => {
|
|
@@ -4881,7 +5086,7 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
|
|
|
4881
5086
|
throw new TypeError("mobile build --outdir must remain inside the project.");
|
|
4882
5087
|
}
|
|
4883
5088
|
return output;
|
|
4884
|
-
}, sha256File = async (path) =>
|
|
5089
|
+
}, sha256File = async (path) => createHash5("sha256").update(await readFile6(path)).digest("hex"), findByExtension = async (root, extension) => {
|
|
4885
5090
|
if (!await pathExists3(root))
|
|
4886
5091
|
return;
|
|
4887
5092
|
const entries = await readdir3(root, { withFileTypes: true });
|
|
@@ -5026,7 +5231,7 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
|
|
|
5026
5231
|
let buildNumber = requireBuildNumber(options.buildNumber);
|
|
5027
5232
|
if (options.prepareBuildNumber) {
|
|
5028
5233
|
const nativeFingerprint = await fingerprintAbsoluteIosNativeProject(nativeDirectory);
|
|
5029
|
-
const buildIdentity =
|
|
5234
|
+
const buildIdentity = createHash5("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}\x00${marketingVersion}`).digest("hex");
|
|
5030
5235
|
buildNumber = requireBuildNumber(await options.prepareBuildNumber(buildIdentity));
|
|
5031
5236
|
}
|
|
5032
5237
|
const stagingParent = resolve9(options.projectRoot, ".absolutejs/mobile");
|
|
@@ -5135,7 +5340,7 @@ var init_iosRelease = __esm(() => {
|
|
|
5135
5340
|
});
|
|
5136
5341
|
|
|
5137
5342
|
// src/mobile/iosSimulatorController.ts
|
|
5138
|
-
import { createHash as
|
|
5343
|
+
import { createHash as createHash6, randomUUID as randomUUID3 } from "crypto";
|
|
5139
5344
|
import {
|
|
5140
5345
|
access as access7,
|
|
5141
5346
|
copyFile as copyFile3,
|
|
@@ -5590,7 +5795,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
5590
5795
|
]);
|
|
5591
5796
|
return result.exitCode === 0 && result.stdout.includes(project.config.appId);
|
|
5592
5797
|
}, buildIosDebugApp = async (project, udid, fingerprint, run, signal) => {
|
|
5593
|
-
const derivedDataPath = join14(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data",
|
|
5798
|
+
const derivedDataPath = join14(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash6("sha256").update(project.config.appId).digest("hex").slice(0, 16));
|
|
5594
5799
|
await mkdir5(derivedDataPath, { recursive: true });
|
|
5595
5800
|
await requireSuccess2([
|
|
5596
5801
|
project.xcodebuild,
|
|
@@ -5611,7 +5816,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
|
|
|
5611
5816
|
throw new Error(`Xcode did not produce the simulator app at ${appPath}.`);
|
|
5612
5817
|
return appPath;
|
|
5613
5818
|
}, buildPhysicalIosDebugApp = async (project, identifier, run, signal) => {
|
|
5614
|
-
const derivedDataPath = join14(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data",
|
|
5819
|
+
const derivedDataPath = join14(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash6("sha256").update(project.config.appId).digest("hex").slice(0, 16));
|
|
5615
5820
|
await mkdir5(derivedDataPath, { recursive: true });
|
|
5616
5821
|
await requireSuccess2([
|
|
5617
5822
|
project.xcodebuild,
|
|
@@ -6039,7 +6244,7 @@ var init_iosSimulatorController = __esm(() => {
|
|
|
6039
6244
|
var ABSOLUTE_REMOTE_MAC_EVENT_PREFIX = "ABSOLUTE_REMOTE_MAC\t", ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION = 3;
|
|
6040
6245
|
|
|
6041
6246
|
// src/mobile/remoteMacProtocol.ts
|
|
6042
|
-
import { createHash as
|
|
6247
|
+
import { createHash as createHash7, randomUUID as randomUUID4 } from "crypto";
|
|
6043
6248
|
import {
|
|
6044
6249
|
chmod,
|
|
6045
6250
|
mkdir as mkdir6,
|
|
@@ -6231,7 +6436,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
|
|
|
6231
6436
|
}
|
|
6232
6437
|
await saveStore(store, profilePath);
|
|
6233
6438
|
return true;
|
|
6234
|
-
}, projectIdentity = (projectRoot, appId) =>
|
|
6439
|
+
}, projectIdentity = (projectRoot, appId) => createHash7("sha256").update(`${resolvePath(projectRoot)}\x00${appId}`).digest("hex").slice(0, 20), createAbsoluteRemoteExpoIosDevProject = (config, projectRoot, profile) => {
|
|
6235
6440
|
if (config.engine !== "expo")
|
|
6236
6441
|
throw new TypeError("Remote Expo execution requires mobile.engine: expo.");
|
|
6237
6442
|
return createAbsoluteRemoteIosDevProject(config, projectRoot, profile);
|
|
@@ -6420,7 +6625,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
|
|
|
6420
6625
|
path = join15(outdir, "remoteMacAgentEntry.js");
|
|
6421
6626
|
}
|
|
6422
6627
|
const bytes = await Bun.file(path).arrayBuffer();
|
|
6423
|
-
const sha256 =
|
|
6628
|
+
const sha256 = createHash7("sha256").update(new Uint8Array(bytes)).digest("hex");
|
|
6424
6629
|
return { bytes: bytes.byteLength, path, sha256 };
|
|
6425
6630
|
}, portableRelativePath = (root, path) => relative7(root, path).split(sep5).join(posix.sep), portableMobileConfig = (project) => ({
|
|
6426
6631
|
appId: project.config.appId,
|
|
@@ -7657,7 +7862,7 @@ var init_eslintChunked = __esm(() => {
|
|
|
7657
7862
|
});
|
|
7658
7863
|
|
|
7659
7864
|
// src/cli/scripts/eslint.ts
|
|
7660
|
-
import { createHash as
|
|
7865
|
+
import { createHash as createHash8 } from "crypto";
|
|
7661
7866
|
import {
|
|
7662
7867
|
existsSync as existsSync8,
|
|
7663
7868
|
mkdirSync as mkdirSync5,
|
|
@@ -7756,7 +7961,7 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
|
|
|
7756
7961
|
directory = parent;
|
|
7757
7962
|
}
|
|
7758
7963
|
}, createEslintCacheFingerprint = (cwd = process.cwd()) => {
|
|
7759
|
-
const hash =
|
|
7964
|
+
const hash = createHash8("sha256");
|
|
7760
7965
|
hash.update(`absolute-eslint-cache:${CACHE_CONTRACT_VERSION}\x00`);
|
|
7761
7966
|
const configPath2 = findEslintConfigPath(cwd);
|
|
7762
7967
|
for (const dependency of lintDependencyNames(cwd, configPath2).sort()) {
|
|
@@ -7766,7 +7971,7 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
|
|
|
7766
7971
|
}
|
|
7767
7972
|
return hash.digest("hex");
|
|
7768
7973
|
}, createEslintConfigDigest = (cwd = process.cwd()) => {
|
|
7769
|
-
const hash =
|
|
7974
|
+
const hash = createHash8("sha256");
|
|
7770
7975
|
hash.update(`absolute-eslint-config:${CACHE_CONTRACT_VERSION}\x00`);
|
|
7771
7976
|
const configPath2 = findEslintConfigPath(cwd);
|
|
7772
7977
|
if (configPath2)
|
|
@@ -8630,7 +8835,7 @@ var init_buildMetadata = __esm(() => {
|
|
|
8630
8835
|
});
|
|
8631
8836
|
|
|
8632
8837
|
// src/mobile/releaseArtifact.ts
|
|
8633
|
-
import { createHash as
|
|
8838
|
+
import { createHash as createHash9 } from "crypto";
|
|
8634
8839
|
var ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT = 1, ABSOLUTE_MOBILE_RETAINED_GENERATIONS = 3, SHA_256 = "sha256", RELEASE_ID_PREFIX = "amc_", MODULE_SEGMENT_PARENT = "..", EXPORT_NAME_PATTERN, frameworks3, isCanonicalRecord = (value) => {
|
|
8635
8840
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
8636
8841
|
return false;
|
|
@@ -8667,7 +8872,7 @@ var ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT = 1, ABSOLUTE_MOBILE_RETAINED_GENERATIO
|
|
|
8667
8872
|
return normalizeCanonicalRecord(value, ancestors);
|
|
8668
8873
|
}
|
|
8669
8874
|
throw new TypeError("Compatibility metadata must contain only finite JSON values.");
|
|
8670
|
-
}, canonicalJson = (value) => JSON.stringify(normalizeCanonicalValue(value, new Set)), hashCanonicalValue = (value) =>
|
|
8875
|
+
}, canonicalJson = (value) => JSON.stringify(normalizeCanonicalValue(value, new Set)), hashCanonicalValue = (value) => createHash9(SHA_256).update(canonicalJson(value)).digest("hex"), requireNonEmpty = (value, field) => {
|
|
8671
8876
|
if (!value.trim()) {
|
|
8672
8877
|
throw new TypeError(`${field} must not be empty.`);
|
|
8673
8878
|
}
|
|
@@ -8839,10 +9044,10 @@ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-
|
|
|
8839
9044
|
};
|
|
8840
9045
|
|
|
8841
9046
|
// src/mobile/buildRelease.ts
|
|
8842
|
-
import { createHash as
|
|
9047
|
+
import { createHash as createHash10 } from "crypto";
|
|
8843
9048
|
import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
|
|
8844
9049
|
import { basename as basename7, dirname as dirname11, extname as extname4, join as join20, relative as relative11, resolve as resolve15 } from "path";
|
|
8845
|
-
var sha256 = (bytes) =>
|
|
9050
|
+
var sha256 = (bytes) => createHash10("sha256").update(bytes).digest("hex"), STATIC_SCRIPT_PATTERN, rewriteStaticScriptPaths = (source, manifest) => source.replace(STATIC_SCRIPT_PATTERN, (match, prefix, path, suffix) => {
|
|
8846
9051
|
if (path.endsWith("/htmx.min.js"))
|
|
8847
9052
|
return match;
|
|
8848
9053
|
const key = toPascal(basename7(path, extname4(path)));
|
|
@@ -9408,10 +9613,10 @@ var init_capacitorBundle = __esm(() => {
|
|
|
9408
9613
|
});
|
|
9409
9614
|
|
|
9410
9615
|
// src/mobile/artifactStore.ts
|
|
9411
|
-
import { createHash as
|
|
9616
|
+
import { createHash as createHash11 } from "crypto";
|
|
9412
9617
|
var DEFAULT_MAX_PRODUCER_BYTES = 134217728, SHA_2562 = "sha256", hashBlob = async (blob) => {
|
|
9413
9618
|
const bytes = new Uint8Array(await blob.arrayBuffer());
|
|
9414
|
-
return
|
|
9619
|
+
return createHash11(SHA_2562).update(bytes).digest("hex");
|
|
9415
9620
|
}, verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes = DEFAULT_MAX_PRODUCER_BYTES) => {
|
|
9416
9621
|
const { artifact, producer } = release;
|
|
9417
9622
|
if (producer.size !== artifact.producer.bytes || producer.size > maxProducerBytes) {
|
|
@@ -9427,7 +9632,7 @@ var init_artifactStore = __esm(() => {
|
|
|
9427
9632
|
});
|
|
9428
9633
|
|
|
9429
9634
|
// src/mobile/materializedBundle.ts
|
|
9430
|
-
import { createHash as
|
|
9635
|
+
import { createHash as createHash12 } from "crypto";
|
|
9431
9636
|
import {
|
|
9432
9637
|
access as access8,
|
|
9433
9638
|
mkdir as mkdir10,
|
|
@@ -9443,7 +9648,7 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
|
|
|
9443
9648
|
currentReleaseId,
|
|
9444
9649
|
releases: releases.map(({ releaseId }) => releaseId)
|
|
9445
9650
|
});
|
|
9446
|
-
return `amb_${
|
|
9651
|
+
return `amb_${createHash12("sha256").update(identity).digest("hex")}`;
|
|
9447
9652
|
}, parseBundleIndex = (value) => {
|
|
9448
9653
|
if (!isRecord8(value) || value.format !== ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT || typeof value.bundleId !== "string" || !BUNDLE_ID_PATTERN.test(value.bundleId) || typeof value.currentReleaseId !== "string" || !Array.isArray(value.releases)) {
|
|
9449
9654
|
throw new TypeError("Invalid materialized mobile compatibility bundle.");
|
|
@@ -9556,136 +9761,6 @@ var init_materializedBundle = __esm(() => {
|
|
|
9556
9761
|
BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
|
|
9557
9762
|
});
|
|
9558
9763
|
|
|
9559
|
-
// src/mobile/updateProtocol.ts
|
|
9560
|
-
var ABSOLUTE_MOBILE_UPDATE_FORMAT = 1, ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES, ABSOLUTE_MOBILE_UPDATE_MAX_FILES = 1e4, ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES, ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM = "ecdsa-p256-sha256", HASH_PATTERN, RELEASE_PATTERN, KEY_ID_PATTERN, CHANNEL_PATTERN, isClassification = (value) => value === "bug-fix" || value === "content" || value === "security", object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), canonicalValue = (value) => {
|
|
9561
|
-
if (Array.isArray(value))
|
|
9562
|
-
return value.map(canonicalValue);
|
|
9563
|
-
if (!object3(value))
|
|
9564
|
-
return value;
|
|
9565
|
-
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalValue(value[key])]));
|
|
9566
|
-
}, absoluteMobileUpdateSigningPayload = (manifest) => new TextEncoder().encode(canonicalizeAbsoluteMobileUpdate(manifest)), canonicalizeAbsoluteMobileUpdate = (value) => JSON.stringify(canonicalValue(value)), requireText2 = (value, field) => {
|
|
9567
|
-
if (typeof value !== "string" || value.length === 0)
|
|
9568
|
-
throw new TypeError(`${field} must be a non-empty string.`);
|
|
9569
|
-
return value;
|
|
9570
|
-
}, normalizeAbsoluteMobileUpdatePath = (value) => {
|
|
9571
|
-
const path = requireText2(value, "Update file path").replaceAll("\\", "/");
|
|
9572
|
-
if (path.startsWith("/") || path.includes("\x00") || path.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
9573
|
-
throw new TypeError("Update file paths must be normalized relative paths.");
|
|
9574
|
-
}
|
|
9575
|
-
return path;
|
|
9576
|
-
}, parseFile = (value) => {
|
|
9577
|
-
if (!object3(value))
|
|
9578
|
-
throw new TypeError("Update files must be objects.");
|
|
9579
|
-
const path = normalizeAbsoluteMobileUpdatePath(value.path);
|
|
9580
|
-
if (typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes < 0 || value.bytes > ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES) {
|
|
9581
|
-
throw new TypeError(`Update file ${path} has an invalid byte length.`);
|
|
9582
|
-
}
|
|
9583
|
-
if (typeof value.sha256 !== "string" || !HASH_PATTERN.test(value.sha256))
|
|
9584
|
-
throw new TypeError(`Update file ${path} has an invalid SHA-256 digest.`);
|
|
9585
|
-
return { bytes: value.bytes, path, sha256: value.sha256 };
|
|
9586
|
-
}, parseAbsoluteMobileUnsignedUpdateManifest = (value) => {
|
|
9587
|
-
if (!object3(value) || value.format !== ABSOLUTE_MOBILE_UPDATE_FORMAT)
|
|
9588
|
-
throw new TypeError("Invalid AbsoluteJS mobile update manifest.");
|
|
9589
|
-
const appId = requireText2(value.appId, "Update appId");
|
|
9590
|
-
const channel = requireText2(value.channel, "Update channel");
|
|
9591
|
-
if (!CHANNEL_PATTERN.test(channel))
|
|
9592
|
-
throw new TypeError("Update channel contains unsupported characters.");
|
|
9593
|
-
if (!isClassification(value.classification))
|
|
9594
|
-
throw new TypeError("Update classification is invalid.");
|
|
9595
|
-
const createdAt = requireText2(value.createdAt, "Update createdAt");
|
|
9596
|
-
if (!Number.isFinite(Date.parse(createdAt)) || new Date(createdAt).toISOString() !== createdAt)
|
|
9597
|
-
throw new TypeError("Update createdAt must be a canonical ISO timestamp.");
|
|
9598
|
-
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
9599
|
-
throw new TypeError("An update must contain at least one file.");
|
|
9600
|
-
if (value.files.length > ABSOLUTE_MOBILE_UPDATE_MAX_FILES)
|
|
9601
|
-
throw new TypeError("Update contains too many files.");
|
|
9602
|
-
const files = value.files.map(parseFile);
|
|
9603
|
-
const sorted = [...files].sort((left, right) => left.path.localeCompare(right.path));
|
|
9604
|
-
if (files.some((file, index) => file.path !== sorted[index]?.path))
|
|
9605
|
-
throw new TypeError("Update files must be sorted by path.");
|
|
9606
|
-
if (new Set(files.map(({ path }) => path)).size !== files.length)
|
|
9607
|
-
throw new TypeError("Update file paths must be unique.");
|
|
9608
|
-
if (files.reduce((total, file) => total + file.bytes, 0) > ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES)
|
|
9609
|
-
throw new TypeError("Update exceeds the maximum uncompressed size.");
|
|
9610
|
-
if (typeof value.releaseId !== "string" || !RELEASE_PATTERN.test(value.releaseId))
|
|
9611
|
-
throw new TypeError("Update releaseId is invalid.");
|
|
9612
|
-
if (typeof value.runtimeFingerprint !== "string" || !HASH_PATTERN.test(value.runtimeFingerprint))
|
|
9613
|
-
throw new TypeError("Update runtime fingerprint is invalid.");
|
|
9614
|
-
if (value.withinSubmittedPurpose !== true)
|
|
9615
|
-
throw new TypeError("OTA updates must attest that they remain within the submitted app purpose.");
|
|
9616
|
-
return {
|
|
9617
|
-
appId,
|
|
9618
|
-
channel,
|
|
9619
|
-
classification: value.classification,
|
|
9620
|
-
createdAt,
|
|
9621
|
-
files,
|
|
9622
|
-
format: ABSOLUTE_MOBILE_UPDATE_FORMAT,
|
|
9623
|
-
releaseId: value.releaseId,
|
|
9624
|
-
runtimeFingerprint: value.runtimeFingerprint,
|
|
9625
|
-
withinSubmittedPurpose: true
|
|
9626
|
-
};
|
|
9627
|
-
}, parseAbsoluteMobileUpdateManifest = (value) => {
|
|
9628
|
-
if (!object3(value))
|
|
9629
|
-
throw new TypeError("Invalid AbsoluteJS mobile update manifest.");
|
|
9630
|
-
const { signature: signatureValue, ...unsignedValue } = value;
|
|
9631
|
-
const unsigned = parseAbsoluteMobileUnsignedUpdateManifest(unsignedValue);
|
|
9632
|
-
if (!object3(signatureValue))
|
|
9633
|
-
throw new TypeError("Update signature is missing.");
|
|
9634
|
-
if (signatureValue.algorithm !== ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM)
|
|
9635
|
-
throw new TypeError("Update signature algorithm is unsupported.");
|
|
9636
|
-
const keyId = requireText2(signatureValue.keyId, "Update signature keyId");
|
|
9637
|
-
if (!KEY_ID_PATTERN.test(keyId))
|
|
9638
|
-
throw new TypeError("Update signature keyId is invalid.");
|
|
9639
|
-
const signature = requireText2(signatureValue.value, "Update signature value");
|
|
9640
|
-
let signatureBytes;
|
|
9641
|
-
try {
|
|
9642
|
-
signatureBytes = Uint8Array.from(atob(signature), (character) => character.charCodeAt(0));
|
|
9643
|
-
} catch {
|
|
9644
|
-
signatureBytes = new Uint8Array;
|
|
9645
|
-
}
|
|
9646
|
-
if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(signature) || signatureBytes.byteLength !== 64 || btoa(String.fromCharCode(...signatureBytes)) !== signature)
|
|
9647
|
-
throw new TypeError("Update signature is not canonical base64.");
|
|
9648
|
-
return {
|
|
9649
|
-
...unsigned,
|
|
9650
|
-
signature: {
|
|
9651
|
-
algorithm: ABSOLUTE_MOBILE_UPDATE_SIGNATURE_ALGORITHM,
|
|
9652
|
-
keyId,
|
|
9653
|
-
value: signature
|
|
9654
|
-
}
|
|
9655
|
-
};
|
|
9656
|
-
}, unsignedAbsoluteMobileUpdate = (manifest) => {
|
|
9657
|
-
const { signature: _signature, ...unsigned } = manifest;
|
|
9658
|
-
return unsigned;
|
|
9659
|
-
};
|
|
9660
|
-
var init_updateProtocol = __esm(() => {
|
|
9661
|
-
ABSOLUTE_MOBILE_UPDATE_MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
9662
|
-
ABSOLUTE_MOBILE_UPDATE_MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
9663
|
-
HASH_PATTERN = /^[a-f0-9]{64}$/u;
|
|
9664
|
-
RELEASE_PATTERN = /^amu_[a-f0-9]{64}$/u;
|
|
9665
|
-
KEY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
9666
|
-
CHANNEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
9667
|
-
});
|
|
9668
|
-
|
|
9669
|
-
// src/mobile/updateRuntime.ts
|
|
9670
|
-
import { createHash as createHash12 } from "crypto";
|
|
9671
|
-
var ABSOLUTE_MOBILE_SHELL_ABI = 2, ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT = 1, createAbsoluteMobileUpdateRuntimeDescriptor = (options) => ({
|
|
9672
|
-
appId: options.config.appId,
|
|
9673
|
-
auth: options.auth ?? null,
|
|
9674
|
-
deepLinks: {
|
|
9675
|
-
hosts: options.config.deepLinkHosts,
|
|
9676
|
-
...options.config.deepLinkScheme ? { scheme: options.config.deepLinkScheme } : {}
|
|
9677
|
-
},
|
|
9678
|
-
deviceCapabilities: options.deviceCapabilities,
|
|
9679
|
-
engine: options.config.engine,
|
|
9680
|
-
format: ABSOLUTE_MOBILE_UPDATE_RUNTIME_FORMAT,
|
|
9681
|
-
shellAbi: ABSOLUTE_MOBILE_SHELL_ABI,
|
|
9682
|
-
syncSchema: options.syncSchema ?? null,
|
|
9683
|
-
updates: options.config.updates ?? null
|
|
9684
|
-
}), fingerprintAbsoluteMobileUpdateRuntime = (descriptor) => createHash12("sha256").update(canonicalizeAbsoluteMobileUpdate(descriptor)).digest("hex");
|
|
9685
|
-
var init_updateRuntime = __esm(() => {
|
|
9686
|
-
init_updateProtocol();
|
|
9687
|
-
});
|
|
9688
|
-
|
|
9689
9764
|
// src/mobile/buildPipeline.ts
|
|
9690
9765
|
import { readFile as readFile12 } from "fs/promises";
|
|
9691
9766
|
import { join as join23, resolve as resolve17 } from "path";
|
|
@@ -9754,16 +9829,13 @@ var isElysiaApp = (value) => typeof value === "object" && value !== null && type
|
|
|
9754
9829
|
producerPath: resolve17(options.producerPath),
|
|
9755
9830
|
runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
|
|
9756
9831
|
});
|
|
9757
|
-
const
|
|
9758
|
-
|
|
9759
|
-
const syncSchema = sync ? discoverAbsoluteSyncSchema(options.projectRoot) : undefined;
|
|
9760
|
-
const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot, mobile.engine);
|
|
9761
|
-
const runtimeFingerprint = fingerprintAbsoluteMobileUpdateRuntime(createAbsoluteMobileUpdateRuntimeDescriptor({
|
|
9762
|
-
...auth ? { auth } : {},
|
|
9763
|
-
config: mobile,
|
|
9832
|
+
const {
|
|
9833
|
+
auth,
|
|
9764
9834
|
deviceCapabilities,
|
|
9765
|
-
|
|
9766
|
-
|
|
9835
|
+
fingerprint: runtimeFingerprint,
|
|
9836
|
+
sync,
|
|
9837
|
+
syncSchema
|
|
9838
|
+
} = resolveAbsoluteMobileUpdateRuntime(mobile, options.projectRoot);
|
|
9767
9839
|
const usesPush = deviceCapabilities.capabilities.includes("pushNotifications");
|
|
9768
9840
|
if (usesPush && !auth)
|
|
9769
9841
|
throw new TypeError("Portable push notifications require @absolutejs/auth so provider tokens can be registered without exposing identity controls to page code.");
|
|
@@ -9811,8 +9883,6 @@ var init_buildPipeline = __esm(() => {
|
|
|
9811
9883
|
init_materializedBundle();
|
|
9812
9884
|
init_pageProtocol();
|
|
9813
9885
|
init_releaseArtifact();
|
|
9814
|
-
init_nativeAuth();
|
|
9815
|
-
init_syncSchema();
|
|
9816
9886
|
init_deviceCapabilities();
|
|
9817
9887
|
init_expoProject();
|
|
9818
9888
|
init_updateRuntime();
|
|
@@ -21099,7 +21169,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
21099
21169
|
} catch (error) {
|
|
21100
21170
|
return fail5(`${platform6}.content-security-policy`, error instanceof Error ? error.message : "Packaged shell CSP could not be validated.", path, "Rebuild the production mobile bundle with the AbsoluteJS-generated shell.");
|
|
21101
21171
|
}
|
|
21102
|
-
}, expoApplicationConfigCheck = async (config) => {
|
|
21172
|
+
}, expoApplicationConfigCheck = async (config, projectRoot) => {
|
|
21103
21173
|
const path = join53(config.nativeProjectDirectory, "app.json");
|
|
21104
21174
|
try {
|
|
21105
21175
|
const root = await readJsonObject(path);
|
|
@@ -21109,13 +21179,14 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
21109
21179
|
if (expo.name !== config.appName || config.platforms.includes("android") && (!isRecord15(expo.android) || expo.android.package !== config.appId) || config.platforms.includes("ios") && (!isRecord15(expo.ios) || expo.ios.bundleIdentifier !== config.appId)) {
|
|
21110
21180
|
throw new TypeError("Generated Expo application identity does not match mobile config.");
|
|
21111
21181
|
}
|
|
21112
|
-
|
|
21113
|
-
|
|
21182
|
+
const expectedRuntime = resolveAbsoluteMobileUpdateRuntime(config, projectRoot).fingerprint;
|
|
21183
|
+
if (expo.runtimeVersion !== expectedRuntime) {
|
|
21184
|
+
throw new TypeError("Generated Expo runtimeVersion does not match the AbsoluteJS native contract.");
|
|
21114
21185
|
}
|
|
21115
21186
|
if (Array.isArray(expo.plugins) && expo.plugins.some((plugin) => typeof plugin === "string" && plugin.includes("withAbsoluteDevelopmentCa"))) {
|
|
21116
21187
|
throw new TypeError("Generated Expo production config includes the development CA plugin.");
|
|
21117
21188
|
}
|
|
21118
|
-
return pass("expo.app-config", "Expo application identity and runtime
|
|
21189
|
+
return pass("expo.app-config", "Expo application identity and generated native runtime match the production mobile config.", path);
|
|
21119
21190
|
} catch (error) {
|
|
21120
21191
|
return fail5("expo.app-config", error instanceof Error ? error.message : "Generated Expo application config could not be validated.", path, "Run `absolute mobile build <platform>`; do not edit the generated Expo project.");
|
|
21121
21192
|
}
|
|
@@ -21405,7 +21476,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
21405
21476
|
const journalPath = join53(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
|
|
21406
21477
|
const checks = await Promise.all([
|
|
21407
21478
|
journalReleaseCheck(journalPath, "android"),
|
|
21408
|
-
expoApplicationConfigCheck(config),
|
|
21479
|
+
expoApplicationConfigCheck(config, projectRoot),
|
|
21409
21480
|
expoEmbeddedAssetsCheck(config),
|
|
21410
21481
|
manifestReleaseCheck(manifestPath),
|
|
21411
21482
|
hmrAssetsReleaseCheck(config.bundleDirectory),
|
|
@@ -21459,7 +21530,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
|
|
|
21459
21530
|
];
|
|
21460
21531
|
checks.push(config.iosVersion ? pass("ios.marketing-version", `The iOS marketing version is ${config.iosVersion}.`) : 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."));
|
|
21461
21532
|
checks.push(...await Promise.all([
|
|
21462
|
-
expoApplicationConfigCheck(config),
|
|
21533
|
+
expoApplicationConfigCheck(config, projectRoot),
|
|
21463
21534
|
expoEmbeddedAssetsCheck(config),
|
|
21464
21535
|
hmrAssetsReleaseCheck(config.bundleDirectory),
|
|
21465
21536
|
embeddedBundleReleaseCheck(config, projectRoot, "ios", config.bundleDirectory),
|
|
@@ -21565,6 +21636,7 @@ var init_releaseDoctor = __esm(() => {
|
|
|
21565
21636
|
init_nativeAuth();
|
|
21566
21637
|
init_syncSchema();
|
|
21567
21638
|
init_deviceCapabilities();
|
|
21639
|
+
init_updateRuntime();
|
|
21568
21640
|
HMR_ASSET_PATTERN = /(?:__HMR_WS__|hmr-timing|__absolute_target|absolutejs-error-overlay)/u;
|
|
21569
21641
|
RELEASE_ASSET_EXTENSIONS = new Set([".html", ".js", ".mjs"]);
|
|
21570
21642
|
EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
|
|
@@ -23136,11 +23208,93 @@ var init_updateSigning = __esm(() => {
|
|
|
23136
23208
|
init_updateProtocol();
|
|
23137
23209
|
});
|
|
23138
23210
|
|
|
23211
|
+
// src/mobile/expoUpdate.ts
|
|
23212
|
+
import { access as access16, mkdir as mkdir18, readFile as readFile26, writeFile as writeFile20 } from "fs/promises";
|
|
23213
|
+
import { dirname as dirname35, extname as extname10, join as join58 } from "path";
|
|
23214
|
+
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json", ABSOLUTE_EXPO_UPDATE_FORMAT = 1, object4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), safePath = (value, field) => {
|
|
23215
|
+
if (typeof value !== "string" || value.length === 0)
|
|
23216
|
+
throw new TypeError(`Expo update ${field} is invalid.`);
|
|
23217
|
+
const path = value.replaceAll("\\", "/");
|
|
23218
|
+
if (path.startsWith("/") || path.split("/").some((segment) => !segment || segment === "." || segment === ".."))
|
|
23219
|
+
throw new TypeError(`Expo update ${field} must be a relative path.`);
|
|
23220
|
+
return path;
|
|
23221
|
+
}, extension = (value, path) => {
|
|
23222
|
+
if (value === null || value === undefined) {
|
|
23223
|
+
const inferred = extname10(path);
|
|
23224
|
+
return inferred ? inferred.slice(1) : undefined;
|
|
23225
|
+
}
|
|
23226
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9]+$/u.test(value))
|
|
23227
|
+
throw new TypeError("Expo update asset extension is invalid.");
|
|
23228
|
+
return value;
|
|
23229
|
+
}, asset = (value) => {
|
|
23230
|
+
if (!object4(value))
|
|
23231
|
+
throw new TypeError("Expo update asset is invalid.");
|
|
23232
|
+
const path = safePath(value.path, "asset path");
|
|
23233
|
+
const normalizedExtension = extension(value.extension ?? value.ext, path);
|
|
23234
|
+
return {
|
|
23235
|
+
...normalizedExtension ? { extension: normalizedExtension } : {},
|
|
23236
|
+
path
|
|
23237
|
+
};
|
|
23238
|
+
}, platform6 = (value) => {
|
|
23239
|
+
if (!object4(value) || !Array.isArray(value.assets))
|
|
23240
|
+
throw new TypeError("Expo update platform metadata is invalid.");
|
|
23241
|
+
const launchValue = object4(value.launchAsset) ? value.launchAsset : { path: value.bundle };
|
|
23242
|
+
const launchPath = safePath(launchValue.path, "launch bundle");
|
|
23243
|
+
const launchExtension = extension(launchValue.extension ?? null, launchPath);
|
|
23244
|
+
const assets = value.assets.map(asset);
|
|
23245
|
+
return {
|
|
23246
|
+
assets,
|
|
23247
|
+
launchAsset: {
|
|
23248
|
+
...launchExtension ? { extension: launchExtension } : {},
|
|
23249
|
+
path: launchPath
|
|
23250
|
+
}
|
|
23251
|
+
};
|
|
23252
|
+
}, parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
23253
|
+
if (!object4(value) || value.engine !== "expo" || value.format !== ABSOLUTE_EXPO_UPDATE_FORMAT || !object4(value.expoConfig) || !object4(value.platforms) || typeof value.runtimeVersion !== "string" || !/^[a-f0-9]{64}$/u.test(value.runtimeVersion))
|
|
23254
|
+
throw new TypeError("AbsoluteJS Expo update descriptor is invalid.");
|
|
23255
|
+
const platforms = {};
|
|
23256
|
+
for (const name of ["android", "ios"]) {
|
|
23257
|
+
const candidate = value.platforms[name];
|
|
23258
|
+
if (candidate !== undefined)
|
|
23259
|
+
platforms[name] = platform6(candidate);
|
|
23260
|
+
}
|
|
23261
|
+
if (Object.keys(platforms).length === 0)
|
|
23262
|
+
throw new TypeError("Expo update descriptor has no native platforms.");
|
|
23263
|
+
return {
|
|
23264
|
+
engine: "expo",
|
|
23265
|
+
expoConfig: value.expoConfig,
|
|
23266
|
+
format: ABSOLUTE_EXPO_UPDATE_FORMAT,
|
|
23267
|
+
platforms,
|
|
23268
|
+
runtimeVersion: value.runtimeVersion
|
|
23269
|
+
};
|
|
23270
|
+
}, finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
23271
|
+
const metadata = JSON.parse(await readFile26(join58(options.exportDirectory, "metadata.json"), "utf8"));
|
|
23272
|
+
if (!object4(metadata) || !object4(metadata.fileMetadata))
|
|
23273
|
+
throw new TypeError("Expo export metadata.json is invalid.");
|
|
23274
|
+
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
23275
|
+
engine: "expo",
|
|
23276
|
+
expoConfig: options.expoConfig,
|
|
23277
|
+
format: ABSOLUTE_EXPO_UPDATE_FORMAT,
|
|
23278
|
+
platforms: metadata.fileMetadata,
|
|
23279
|
+
runtimeVersion: options.runtimeVersion
|
|
23280
|
+
});
|
|
23281
|
+
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
23282
|
+
await Promise.all(referenced.map(({ path }) => access16(join58(options.exportDirectory, path)).catch(() => {
|
|
23283
|
+
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
23284
|
+
})));
|
|
23285
|
+
const destination = join58(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
23286
|
+
await mkdir18(dirname35(destination), { recursive: true });
|
|
23287
|
+
await writeFile20(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
23288
|
+
`);
|
|
23289
|
+
return { descriptor, path: destination };
|
|
23290
|
+
};
|
|
23291
|
+
var init_expoUpdate = () => {};
|
|
23292
|
+
|
|
23139
23293
|
// src/mobile/updatePublisher.ts
|
|
23140
|
-
import { access as
|
|
23294
|
+
import { access as access17 } from "fs/promises";
|
|
23141
23295
|
import { isAbsolute as isAbsolute9, relative as relative32, resolve as resolve45, sep as sep10 } from "path";
|
|
23142
23296
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
23143
|
-
var
|
|
23297
|
+
var object5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPublisher2 = (value) => object5(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function", projectPath2 = (projectRoot, requested, label) => {
|
|
23144
23298
|
const root = resolve45(projectRoot);
|
|
23145
23299
|
const path = resolve45(root, requested);
|
|
23146
23300
|
const projectRelative = relative32(root, path);
|
|
@@ -23149,11 +23303,11 @@ var object4 = (value) => typeof value === "object" && value !== null && !Array.i
|
|
|
23149
23303
|
return path;
|
|
23150
23304
|
}, loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
|
|
23151
23305
|
const modulePath = projectPath2(projectRoot, requestedModulePath, "mobile update registry");
|
|
23152
|
-
await
|
|
23306
|
+
await access17(modulePath).catch(() => {
|
|
23153
23307
|
throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
|
|
23154
23308
|
});
|
|
23155
23309
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
23156
|
-
const publisher =
|
|
23310
|
+
const publisher = object5(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
23157
23311
|
if (!isPublisher2(publisher))
|
|
23158
23312
|
throw new TypeError("Mobile update registry must implement publishUpdate, promoteUpdate, and rollbackUpdate.");
|
|
23159
23313
|
return publisher;
|
|
@@ -23200,12 +23354,19 @@ var exports_mobile = {};
|
|
|
23200
23354
|
__export(exports_mobile, {
|
|
23201
23355
|
runMobile: () => runMobile
|
|
23202
23356
|
});
|
|
23203
|
-
import {
|
|
23357
|
+
import {
|
|
23358
|
+
access as access18,
|
|
23359
|
+
mkdir as mkdir19,
|
|
23360
|
+
mkdtemp as mkdtemp9,
|
|
23361
|
+
readFile as readFile27,
|
|
23362
|
+
rm as rm13,
|
|
23363
|
+
writeFile as writeFile21
|
|
23364
|
+
} from "fs/promises";
|
|
23204
23365
|
import { createPublicKey as createPublicKey3 } from "crypto";
|
|
23205
|
-
import { join as
|
|
23366
|
+
import { join as join59, relative as relative33, resolve as resolve46 } from "path";
|
|
23206
23367
|
import { createInterface } from "readline/promises";
|
|
23207
23368
|
var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
|
|
23208
|
-
const manifest = JSON.parse(await
|
|
23369
|
+
const manifest = JSON.parse(await readFile27(join59(projectRoot, "package.json"), "utf8"));
|
|
23209
23370
|
if (!isRecord18(manifest))
|
|
23210
23371
|
throw new TypeError("Application package.json must contain an object.");
|
|
23211
23372
|
const names = new Set;
|
|
@@ -23218,7 +23379,7 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
|
|
|
23218
23379
|
return names;
|
|
23219
23380
|
}, resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
23220
23381
|
try {
|
|
23221
|
-
const manifest = JSON.parse(await
|
|
23382
|
+
const manifest = JSON.parse(await readFile27(join59(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
23222
23383
|
return isRecord18(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
23223
23384
|
} catch {
|
|
23224
23385
|
return;
|
|
@@ -23260,9 +23421,9 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
|
|
|
23260
23421
|
}
|
|
23261
23422
|
return value;
|
|
23262
23423
|
}, capacitorExecutable = async (projectRoot) => {
|
|
23263
|
-
const executable =
|
|
23424
|
+
const executable = join59(projectRoot, "node_modules", ".bin", "cap");
|
|
23264
23425
|
try {
|
|
23265
|
-
await
|
|
23426
|
+
await access18(executable);
|
|
23266
23427
|
return executable;
|
|
23267
23428
|
} catch {
|
|
23268
23429
|
throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
|
|
@@ -23279,10 +23440,10 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
|
|
|
23279
23440
|
if (exitCode !== 0) {
|
|
23280
23441
|
throw new TypeError(`Capacitor exited with status ${exitCode}.`);
|
|
23281
23442
|
}
|
|
23282
|
-
}, runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending,
|
|
23283
|
-
const executable =
|
|
23443
|
+
}, runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform7) => pending.then(() => runCapacitor(projectRoot, [command, platform7])), Promise.resolve()), expoExecutable = async (project) => {
|
|
23444
|
+
const executable = join59(project, "node_modules", ".bin", "expo");
|
|
23284
23445
|
try {
|
|
23285
|
-
await
|
|
23446
|
+
await access18(executable);
|
|
23286
23447
|
return executable;
|
|
23287
23448
|
} catch {
|
|
23288
23449
|
throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
|
|
@@ -23311,14 +23472,14 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
|
|
|
23311
23472
|
throw new TypeError(`Expo exited with status ${exitCode}.`);
|
|
23312
23473
|
}, ensureExpoPackages = async (project, args) => {
|
|
23313
23474
|
try {
|
|
23314
|
-
const manifest = JSON.parse(await
|
|
23475
|
+
const manifest = JSON.parse(await readFile27(join59(project, "package.json"), "utf8"));
|
|
23315
23476
|
const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
|
|
23316
23477
|
if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
|
|
23317
23478
|
throw new TypeError("Generated Expo dependencies are invalid.");
|
|
23318
23479
|
await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
|
|
23319
23480
|
if (typeof expected !== "string")
|
|
23320
23481
|
throw new TypeError("Generated Expo dependency version is invalid.");
|
|
23321
|
-
const installed = JSON.parse(await
|
|
23482
|
+
const installed = JSON.parse(await readFile27(join59(project, "node_modules", name, "package.json"), "utf8"));
|
|
23322
23483
|
const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
|
|
23323
23484
|
if (typeof actual !== "string")
|
|
23324
23485
|
throw new TypeError("Installed Expo dependency version is invalid.");
|
|
@@ -23478,8 +23639,8 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
|
|
|
23478
23639
|
await ensureExpoPackages(mobile.nativeProjectDirectory, args);
|
|
23479
23640
|
const assets = await syncAbsoluteExpoWebAssets(mobile);
|
|
23480
23641
|
console.log(`Synced ${assets.assets} embedded AbsoluteJS assets for ${assets.appBuild}.`);
|
|
23481
|
-
const
|
|
23482
|
-
const platforms2 =
|
|
23642
|
+
const platform8 = args.find((value) => value === "android" || value === "ios");
|
|
23643
|
+
const platforms2 = platform8 ? [platform8] : mobile.platforms;
|
|
23483
23644
|
await runExpo(mobile.nativeProjectDirectory, [
|
|
23484
23645
|
"prebuild",
|
|
23485
23646
|
"--no-install",
|
|
@@ -23489,8 +23650,8 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
|
|
|
23489
23650
|
return;
|
|
23490
23651
|
}
|
|
23491
23652
|
await ensureCapacitorPackages(projectRoot, args, mobile);
|
|
23492
|
-
const
|
|
23493
|
-
const platforms =
|
|
23653
|
+
const platform7 = args.find((value) => value === "android" || value === "ios");
|
|
23654
|
+
const platforms = platform7 ? [platform7] : mobile.platforms;
|
|
23494
23655
|
if (platforms.includes("android"))
|
|
23495
23656
|
await repairAbsoluteAndroidDevSession(projectRoot);
|
|
23496
23657
|
if (platforms.includes("ios"))
|
|
@@ -23598,8 +23759,8 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
|
|
|
23598
23759
|
}
|
|
23599
23760
|
}, runReleaseDoctor = async (args) => {
|
|
23600
23761
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
23601
|
-
const
|
|
23602
|
-
const effectiveMobile =
|
|
23762
|
+
const platform7 = args.find((value) => value === "android" || value === "ios");
|
|
23763
|
+
const effectiveMobile = platform7 ? { ...mobile, platforms: [platform7] } : mobile;
|
|
23603
23764
|
const result = await inspectAbsoluteMobileRelease(effectiveMobile, projectRoot);
|
|
23604
23765
|
if (args.includes("--json")) {
|
|
23605
23766
|
console.log(JSON.stringify(createAbsoluteMobileComplianceReport(effectiveMobile, result), null, 2));
|
|
@@ -23648,10 +23809,51 @@ Mobile release security and compliance checks failed.`);
|
|
|
23648
23809
|
if (value === "bug-fix" || value === "content" || value === "security")
|
|
23649
23810
|
return value;
|
|
23650
23811
|
throw new TypeError("mobile update build requires --classification bug-fix|content|security.");
|
|
23812
|
+
}, prepareExpoMobileUpdateExport = async (options) => {
|
|
23813
|
+
const { mobile } = options;
|
|
23814
|
+
if (mobile.engine !== "expo")
|
|
23815
|
+
return {
|
|
23816
|
+
bundleDirectory: mobile.bundleDirectory,
|
|
23817
|
+
temporaryDirectory: undefined
|
|
23818
|
+
};
|
|
23819
|
+
await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
|
|
23820
|
+
const temporaryParent = join59(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
|
|
23821
|
+
await mkdir19(temporaryParent, { recursive: true });
|
|
23822
|
+
const temporaryDirectory = await mkdtemp9(join59(temporaryParent, ".stage-"));
|
|
23823
|
+
try {
|
|
23824
|
+
await runExpo(mobile.nativeProjectDirectory, [
|
|
23825
|
+
"export",
|
|
23826
|
+
"--platform",
|
|
23827
|
+
"ios",
|
|
23828
|
+
"--platform",
|
|
23829
|
+
"android",
|
|
23830
|
+
"--output-dir",
|
|
23831
|
+
temporaryDirectory,
|
|
23832
|
+
"--clear"
|
|
23833
|
+
], { production: true });
|
|
23834
|
+
const generatedApp = JSON.parse(await readFile27(join59(mobile.nativeProjectDirectory, "app.json"), "utf8"));
|
|
23835
|
+
const expoConfig = isRecord18(generatedApp) ? generatedApp.expo : undefined;
|
|
23836
|
+
if (!isRecord18(expoConfig))
|
|
23837
|
+
throw new TypeError("Generated Expo app configuration is invalid.");
|
|
23838
|
+
const computedRuntime = resolveAbsoluteMobileUpdateRuntime(mobile, options.projectRoot).fingerprint;
|
|
23839
|
+
if (computedRuntime !== options.runtimeFingerprint)
|
|
23840
|
+
throw new TypeError("Expo native runtime changed while exporting the update.");
|
|
23841
|
+
await finalizeAbsoluteExpoUpdateExport({
|
|
23842
|
+
expoConfig,
|
|
23843
|
+
exportDirectory: temporaryDirectory,
|
|
23844
|
+
runtimeVersion: options.runtimeFingerprint
|
|
23845
|
+
});
|
|
23846
|
+
return {
|
|
23847
|
+
bundleDirectory: temporaryDirectory,
|
|
23848
|
+
temporaryDirectory
|
|
23849
|
+
};
|
|
23850
|
+
} catch (error) {
|
|
23851
|
+
await rm13(temporaryDirectory, { force: true, recursive: true });
|
|
23852
|
+
throw error;
|
|
23853
|
+
}
|
|
23651
23854
|
}, buildMobileUpdate = async (args) => {
|
|
23652
23855
|
const configPath2 = valueAfter(args, "--config");
|
|
23653
23856
|
const { mobile, projectRoot } = await loadMobile(configPath2);
|
|
23654
|
-
requireCapacitorEngine(mobile, "mobile update build");
|
|
23655
23857
|
if (!mobile.updates)
|
|
23656
23858
|
throw new TypeError("mobile update build requires mobile.updates.publicKeys in absolute.config.ts.");
|
|
23657
23859
|
if (!args.includes("--within-submitted-purpose"))
|
|
@@ -23665,13 +23867,14 @@ Mobile release security and compliance checks failed.`);
|
|
|
23665
23867
|
throw new TypeError("mobile update build requires --signing-key <private-key.pem>.");
|
|
23666
23868
|
const startedAt = performance.now();
|
|
23667
23869
|
let success = false;
|
|
23870
|
+
let expoExportDirectory;
|
|
23668
23871
|
try {
|
|
23669
23872
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath2, { prepareOnly: true });
|
|
23670
|
-
const embedded = JSON.parse(await
|
|
23873
|
+
const embedded = JSON.parse(await readFile27(join59(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
23671
23874
|
const runtimeFingerprint = isRecord18(embedded) ? embedded.nativeRuntime : undefined;
|
|
23672
23875
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
23673
23876
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
23674
|
-
const privateKey = await
|
|
23877
|
+
const privateKey = await readFile27(resolve46(projectRoot, signingKeyPath));
|
|
23675
23878
|
const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
|
|
23676
23879
|
const derivedPublicKey = createPublicKey3(privateKey).export({
|
|
23677
23880
|
format: "der",
|
|
@@ -23679,13 +23882,20 @@ Mobile release security and compliance checks failed.`);
|
|
|
23679
23882
|
});
|
|
23680
23883
|
if (!configuredPublicKey.equals(derivedPublicKey))
|
|
23681
23884
|
throw new TypeError(`The private key does not match mobile.updates.publicKeys.${keyId}.`);
|
|
23885
|
+
const expoExport = await prepareExpoMobileUpdateExport({
|
|
23886
|
+
args,
|
|
23887
|
+
mobile,
|
|
23888
|
+
projectRoot,
|
|
23889
|
+
runtimeFingerprint
|
|
23890
|
+
});
|
|
23891
|
+
expoExportDirectory = expoExport.temporaryDirectory;
|
|
23682
23892
|
const result = await buildAbsoluteMobileUpdate({
|
|
23683
23893
|
appId: mobile.appId,
|
|
23684
|
-
bundleDirectory:
|
|
23894
|
+
bundleDirectory: expoExport.bundleDirectory,
|
|
23685
23895
|
channel: mobile.updates.channel,
|
|
23686
23896
|
classification,
|
|
23687
23897
|
keyId,
|
|
23688
|
-
outputDirectory: valueAfter(args, "--outdir") ??
|
|
23898
|
+
outputDirectory: valueAfter(args, "--outdir") ?? join59(projectRoot, ".absolutejs", "mobile", "updates"),
|
|
23689
23899
|
privateKey,
|
|
23690
23900
|
runtimeFingerprint
|
|
23691
23901
|
});
|
|
@@ -23696,6 +23906,8 @@ Mobile release security and compliance checks failed.`);
|
|
|
23696
23906
|
console.log(`Manifest: ${result.manifestPath}`);
|
|
23697
23907
|
return result;
|
|
23698
23908
|
} finally {
|
|
23909
|
+
if (expoExportDirectory)
|
|
23910
|
+
await rm13(expoExportDirectory, { force: true, recursive: true });
|
|
23699
23911
|
sendTelemetryEvent("mobile:update-build", {
|
|
23700
23912
|
classification: classification ?? "invalid",
|
|
23701
23913
|
durationMs: Math.round(performance.now() - startedAt),
|
|
@@ -23962,7 +24174,7 @@ Mobile release security and compliance checks failed.`);
|
|
|
23962
24174
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
23963
24175
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
23964
24176
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
23965
|
-
console.log(`Metadata: ${
|
|
24177
|
+
console.log(`Metadata: ${join59(release.releaseRoot, "release.json")}`);
|
|
23966
24178
|
return release;
|
|
23967
24179
|
} finally {
|
|
23968
24180
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -24110,7 +24322,7 @@ Mobile release security and compliance checks failed.`);
|
|
|
24110
24322
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
24111
24323
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
|
|
24112
24324
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
24113
|
-
console.log(`Metadata: ${
|
|
24325
|
+
console.log(`Metadata: ${join59(release.releaseRoot, "release.json")}`);
|
|
24114
24326
|
return release;
|
|
24115
24327
|
} finally {
|
|
24116
24328
|
stopListeningForCancellation();
|
|
@@ -24219,7 +24431,7 @@ Mobile release security and compliance checks failed.`);
|
|
|
24219
24431
|
checks.push({
|
|
24220
24432
|
id: "sync.storage-schema",
|
|
24221
24433
|
label: `Offline schema ${schema.components.map((component2) => `${component2.id}@${component2.version}`).join(", ")}`,
|
|
24222
|
-
path:
|
|
24434
|
+
path: join59(projectRoot, "package.json"),
|
|
24223
24435
|
platform: "host",
|
|
24224
24436
|
status: "pass"
|
|
24225
24437
|
});
|
|
@@ -24227,7 +24439,7 @@ Mobile release security and compliance checks failed.`);
|
|
|
24227
24439
|
checks.push({
|
|
24228
24440
|
id: "sync.storage-schema",
|
|
24229
24441
|
label: "Offline schema metadata is invalid",
|
|
24230
|
-
path:
|
|
24442
|
+
path: join59(projectRoot, "package.json"),
|
|
24231
24443
|
platform: "host",
|
|
24232
24444
|
remediation: error instanceof Error ? error.message : String(error),
|
|
24233
24445
|
status: "fail"
|
|
@@ -24238,9 +24450,9 @@ Mobile release security and compliance checks failed.`);
|
|
|
24238
24450
|
await runReleaseDoctor(args);
|
|
24239
24451
|
return;
|
|
24240
24452
|
}
|
|
24241
|
-
const
|
|
24453
|
+
const platform7 = args.find((value) => value === "android" || value === "ios");
|
|
24242
24454
|
const requestedRemote = valueAfter(args, "--remote");
|
|
24243
|
-
const remoteProfile =
|
|
24455
|
+
const remoteProfile = platform7 === "ios" && (requestedRemote !== undefined || process.platform !== "darwin") ? await getAbsoluteRemoteMacProfile(requestedRemote, remoteProfilePath()) : undefined;
|
|
24244
24456
|
if (remoteProfile) {
|
|
24245
24457
|
if (args.includes("--fix"))
|
|
24246
24458
|
throw new TypeError("Remote Mac doctor is read-only. Configure Xcode or Bun on the Mac, then rerun doctor.");
|
|
@@ -24253,7 +24465,7 @@ Mobile release security and compliance checks failed.`);
|
|
|
24253
24465
|
}
|
|
24254
24466
|
const checks = await inspectAbsoluteMobileToolchain();
|
|
24255
24467
|
appendSyncSchemaDoctorCheck(checks, process.cwd());
|
|
24256
|
-
const selected =
|
|
24468
|
+
const selected = platform7 ? checks.filter((check2) => check2.platform === "host" || check2.platform === platform7) : checks;
|
|
24257
24469
|
if (args.includes("--json")) {
|
|
24258
24470
|
if (args.includes("--fix")) {
|
|
24259
24471
|
throw new TypeError("mobile doctor --json cannot be combined with --fix.");
|
|
@@ -24264,7 +24476,7 @@ Mobile release security and compliance checks failed.`);
|
|
|
24264
24476
|
printDoctorChecks(selected);
|
|
24265
24477
|
if (!args.includes("--fix"))
|
|
24266
24478
|
return;
|
|
24267
|
-
const installPlatform =
|
|
24479
|
+
const installPlatform = platform7 ?? "android";
|
|
24268
24480
|
const relevantFailures = selected.filter((check2) => check2.platform === installPlatform && (check2.status === "fail" || check2.status === "warn"));
|
|
24269
24481
|
if (relevantFailures.length === 0) {
|
|
24270
24482
|
console.log(`
|
|
@@ -24380,12 +24592,12 @@ Emulator setup verification:`);
|
|
|
24380
24592
|
timeoutMs
|
|
24381
24593
|
});
|
|
24382
24594
|
}, writeAndroidFailureArtifacts = async (options) => {
|
|
24383
|
-
await
|
|
24384
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
24595
|
+
await mkdir19(options.artifactRoot, { recursive: true });
|
|
24596
|
+
const screenshot = options.session ? await options.session.screenshot(join59(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
24385
24597
|
return;
|
|
24386
24598
|
}) : undefined;
|
|
24387
|
-
const diagnosticPath =
|
|
24388
|
-
await
|
|
24599
|
+
const diagnosticPath = join59(options.artifactRoot, "android-failure.json");
|
|
24600
|
+
await writeFile21(diagnosticPath, `${JSON.stringify({
|
|
24389
24601
|
diagnostics: options.session?.diagnostics ?? [],
|
|
24390
24602
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
24391
24603
|
platform: "android",
|
|
@@ -24449,7 +24661,7 @@ Emulator setup verification:`);
|
|
|
24449
24661
|
console.log(JSON.stringify(report, null, 2));
|
|
24450
24662
|
else
|
|
24451
24663
|
printAndroidTestReport(report);
|
|
24452
|
-
const screenshot = reportRoot ? await session.screenshot(
|
|
24664
|
+
const screenshot = reportRoot ? await session.screenshot(join59(artifactRoot, "android-emulator.png")) : undefined;
|
|
24453
24665
|
await writeRequestedAndroidReport({
|
|
24454
24666
|
adb,
|
|
24455
24667
|
args,
|
|
@@ -24638,8 +24850,8 @@ Emulator setup verification:`);
|
|
|
24638
24850
|
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
24639
24851
|
return result;
|
|
24640
24852
|
}, writeIosFailureArtifacts = async (options) => {
|
|
24641
|
-
await
|
|
24642
|
-
const screenshot =
|
|
24853
|
+
await mkdir19(options.artifactRoot, { recursive: true });
|
|
24854
|
+
const screenshot = join59(options.artifactRoot, "ios-failure.png");
|
|
24643
24855
|
const screenshotResult = captureCommand4([
|
|
24644
24856
|
options.xcrun,
|
|
24645
24857
|
"simctl",
|
|
@@ -24648,8 +24860,8 @@ Emulator setup verification:`);
|
|
|
24648
24860
|
"screenshot",
|
|
24649
24861
|
screenshot
|
|
24650
24862
|
]);
|
|
24651
|
-
const diagnosticPath =
|
|
24652
|
-
await
|
|
24863
|
+
const diagnosticPath = join59(options.artifactRoot, "ios-failure.json");
|
|
24864
|
+
await writeFile21(diagnosticPath, `${JSON.stringify({
|
|
24653
24865
|
appId: options.appId,
|
|
24654
24866
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
24655
24867
|
platform: "ios",
|
|
@@ -24663,14 +24875,14 @@ Emulator setup verification:`);
|
|
|
24663
24875
|
diagnosticPath,
|
|
24664
24876
|
screenshot: screenshotResult.exitCode === 0 ? screenshot : undefined
|
|
24665
24877
|
};
|
|
24666
|
-
}, nativeReportRoot = (args, projectRoot,
|
|
24878
|
+
}, nativeReportRoot = (args, projectRoot, platform7) => {
|
|
24667
24879
|
const index = args.indexOf("--report");
|
|
24668
24880
|
if (index === NOT_FOUND4)
|
|
24669
24881
|
return;
|
|
24670
24882
|
const candidate = args[index + 1];
|
|
24671
24883
|
const explicit = candidate?.startsWith("--") ? undefined : candidate;
|
|
24672
24884
|
const timestamp = new Date().toISOString().replaceAll(":", "-");
|
|
24673
|
-
return safeArtifactRoot(projectRoot, explicit ?? `.absolutejs/mobile/test-reports/${
|
|
24885
|
+
return safeArtifactRoot(projectRoot, explicit ?? `.absolutejs/mobile/test-reports/${platform7}-${timestamp}`);
|
|
24674
24886
|
}, absolutejsVersionForReport = async () => {
|
|
24675
24887
|
let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
|
|
24676
24888
|
const versions = await Promise.all([
|
|
@@ -24894,8 +25106,8 @@ Emulator setup verification:`);
|
|
|
24894
25106
|
mobile.appId
|
|
24895
25107
|
], "iOS app launch");
|
|
24896
25108
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
24897
|
-
await
|
|
24898
|
-
const screenshot =
|
|
25109
|
+
await mkdir19(artifactRoot, { recursive: true });
|
|
25110
|
+
const screenshot = join59(artifactRoot, "ios-simulator.png");
|
|
24899
25111
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
24900
25112
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
24901
25113
|
const report = {
|
|
@@ -25095,6 +25307,8 @@ var init_mobile = __esm(() => {
|
|
|
25095
25307
|
init_mobileInspect();
|
|
25096
25308
|
init_ciWorkflow();
|
|
25097
25309
|
init_updateSigning();
|
|
25310
|
+
init_expoUpdate();
|
|
25311
|
+
init_updateRuntime();
|
|
25098
25312
|
init_updatePublisher();
|
|
25099
25313
|
CAPACITOR_PACKAGES = [
|
|
25100
25314
|
"@capacitor/core",
|
|
@@ -25131,9 +25345,9 @@ var exports_typecheck = {};
|
|
|
25131
25345
|
__export(exports_typecheck, {
|
|
25132
25346
|
typecheck: () => typecheck
|
|
25133
25347
|
});
|
|
25134
|
-
import { resolve as resolve47, join as
|
|
25348
|
+
import { resolve as resolve47, join as join60 } from "path";
|
|
25135
25349
|
import { existsSync as existsSync44, readFileSync as readFileSync40 } from "fs";
|
|
25136
|
-
import { mkdir as
|
|
25350
|
+
import { mkdir as mkdir20, writeFile as writeFile22 } from "fs/promises";
|
|
25137
25351
|
var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve47(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
|
|
25138
25352
|
if (!existsSync44(resolveConfigPath(configPath2))) {
|
|
25139
25353
|
const defaultService = {};
|
|
@@ -25240,8 +25454,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
25240
25454
|
console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
|
|
25241
25455
|
process.exit(1);
|
|
25242
25456
|
}
|
|
25243
|
-
const vueTsconfigPath =
|
|
25244
|
-
await
|
|
25457
|
+
const vueTsconfigPath = join60(cacheDir, "tsconfig.vue-check.json");
|
|
25458
|
+
await writeFile22(vueTsconfigPath, JSON.stringify({
|
|
25245
25459
|
compilerOptions: {
|
|
25246
25460
|
rootDir: ".."
|
|
25247
25461
|
},
|
|
@@ -25260,7 +25474,7 @@ Found ${errorCount} error${suffix}.`;
|
|
|
25260
25474
|
...base,
|
|
25261
25475
|
"--incremental",
|
|
25262
25476
|
"--tsBuildInfoFile",
|
|
25263
|
-
|
|
25477
|
+
join60(cacheDir, "vue-tsc.tsbuildinfo")
|
|
25264
25478
|
]);
|
|
25265
25479
|
if (cached.exitCode === 0 || cached.output.length > 0)
|
|
25266
25480
|
return cached;
|
|
@@ -25271,8 +25485,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
25271
25485
|
console.error("\x1B[31m\u2717\x1B[0m @angular/compiler-cli is required for Angular type checking. Install it: bun add -d @angular/compiler-cli");
|
|
25272
25486
|
process.exit(1);
|
|
25273
25487
|
}
|
|
25274
|
-
const angularTsconfigPath =
|
|
25275
|
-
await
|
|
25488
|
+
const angularTsconfigPath = join60(cacheDir, "tsconfig.angular-check.json");
|
|
25489
|
+
await writeFile22(angularTsconfigPath, JSON.stringify({
|
|
25276
25490
|
angularCompilerOptions: {
|
|
25277
25491
|
strictTemplates: true
|
|
25278
25492
|
},
|
|
@@ -25291,8 +25505,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
25291
25505
|
console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
|
|
25292
25506
|
process.exit(1);
|
|
25293
25507
|
}
|
|
25294
|
-
const tscConfigPath =
|
|
25295
|
-
return
|
|
25508
|
+
const tscConfigPath = join60(cacheDir, "tsconfig.typecheck.json");
|
|
25509
|
+
return writeFile22(tscConfigPath, JSON.stringify({
|
|
25296
25510
|
compilerOptions: {
|
|
25297
25511
|
rootDir: ".."
|
|
25298
25512
|
},
|
|
@@ -25306,7 +25520,7 @@ Found ${errorCount} error${suffix}.`;
|
|
|
25306
25520
|
resolve47(tscConfigPath),
|
|
25307
25521
|
"--incremental",
|
|
25308
25522
|
"--tsBuildInfoFile",
|
|
25309
|
-
|
|
25523
|
+
join60(cacheDir, "tsc.tsbuildinfo"),
|
|
25310
25524
|
"--pretty"
|
|
25311
25525
|
]));
|
|
25312
25526
|
}, buildSvelteCheck = async (cacheDir, svelteDir) => {
|
|
@@ -25315,8 +25529,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
25315
25529
|
console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
|
|
25316
25530
|
process.exit(1);
|
|
25317
25531
|
}
|
|
25318
|
-
const svelteTsconfigPath =
|
|
25319
|
-
await
|
|
25532
|
+
const svelteTsconfigPath = join60(cacheDir, "tsconfig.svelte-check.json");
|
|
25533
|
+
await writeFile22(svelteTsconfigPath, JSON.stringify({
|
|
25320
25534
|
extends: resolve47("tsconfig.json"),
|
|
25321
25535
|
files: ABSOLUTE_TYPECHECK_FILES,
|
|
25322
25536
|
include: [`../${svelteDir}/**/*`]
|
|
@@ -25345,7 +25559,7 @@ Found ${errorCount} error${suffix}.`;
|
|
|
25345
25559
|
...new Set(targets.map((config) => config.angularDirectory).filter((dir) => typeof dir === "string" && dir.length > 0))
|
|
25346
25560
|
];
|
|
25347
25561
|
const cacheDir = ".absolutejs";
|
|
25348
|
-
await
|
|
25562
|
+
await mkdir20(cacheDir, { recursive: true });
|
|
25349
25563
|
const checks = [];
|
|
25350
25564
|
checks.push(hasVue ? buildVueTscCheck(cacheDir) : buildTscCheck(cacheDir));
|
|
25351
25565
|
for (const svelteDir of hasSvelte ? svelteDirs : []) {
|