@absolutejs/absolute 0.20.0-beta.39 → 0.20.0-beta.40
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/README.md +6 -0
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/index.js +1274 -620
- package/dist/index.js +25 -3
- package/dist/index.js.map +3 -3
- package/dist/mobile/index.js +833 -97
- package/dist/mobile/index.js.map +9 -7
- package/dist/mobile/remoteMacAgentEntry.js +7 -7
- package/dist/mobile/shellBootstrap.js +36 -5
- package/dist/mobile/shellExpoDevices.js +99 -0
- package/dist/src/mobile/config.d.ts +3 -1
- package/dist/src/mobile/expoBridge.d.ts +91 -0
- package/dist/src/mobile/expoProject.d.ts +16 -0
- package/dist/src/mobile/index.d.ts +2 -0
- package/dist/src/mobile/mobileInspect.d.ts +2 -2
- package/dist/src/mobile/releaseDoctor.d.ts +1 -1
- package/dist/src/mobile/shellBootstrap.d.ts +1 -0
- package/dist/src/mobile/shellExpoDevices.d.ts +3 -0
- package/dist/types/build.d.ts +21 -3
- package/package.json +1 -1
package/dist/mobile/index.js
CHANGED
|
@@ -862,7 +862,7 @@ var init_deviceCapabilities = __esm(() => {
|
|
|
862
862
|
// src/cli/scripts/telemetry.ts
|
|
863
863
|
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
864
864
|
import { homedir as homedir3 } from "os";
|
|
865
|
-
import { join as
|
|
865
|
+
import { join as join16 } from "path";
|
|
866
866
|
var configDir, configPath, getTelemetryConfig = () => {
|
|
867
867
|
try {
|
|
868
868
|
if (!existsSync4(configPath))
|
|
@@ -875,14 +875,14 @@ var configDir, configPath, getTelemetryConfig = () => {
|
|
|
875
875
|
}
|
|
876
876
|
};
|
|
877
877
|
var init_telemetry = __esm(() => {
|
|
878
|
-
configDir =
|
|
879
|
-
configPath =
|
|
878
|
+
configDir = join16(homedir3(), ".absolutejs");
|
|
879
|
+
configPath = join16(configDir, "telemetry.json");
|
|
880
880
|
});
|
|
881
881
|
|
|
882
882
|
// src/cli/telemetryEvent.ts
|
|
883
883
|
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
884
884
|
import { arch, platform } from "os";
|
|
885
|
-
import { dirname as
|
|
885
|
+
import { dirname as dirname12, join as join17, parse } from "path";
|
|
886
886
|
var checkCandidate = (candidate) => {
|
|
887
887
|
if (!existsSync5(candidate)) {
|
|
888
888
|
return null;
|
|
@@ -902,12 +902,12 @@ var checkCandidate = (candidate) => {
|
|
|
902
902
|
}, findPackageVersion = () => {
|
|
903
903
|
let { dir } = import.meta;
|
|
904
904
|
while (dir !== parse(dir).root) {
|
|
905
|
-
const candidate =
|
|
905
|
+
const candidate = join17(dir, "package.json");
|
|
906
906
|
const version = checkCandidate(candidate);
|
|
907
907
|
if (version) {
|
|
908
908
|
return version;
|
|
909
909
|
}
|
|
910
|
-
dir =
|
|
910
|
+
dir = dirname12(dir);
|
|
911
911
|
}
|
|
912
912
|
return "unknown";
|
|
913
913
|
}, sendTelemetryEvent = (event, payload) => {
|
|
@@ -4535,6 +4535,26 @@ var normalizeCertificateFingerprints = (values) => [
|
|
|
4535
4535
|
return value.match(/.{2}/g)?.join(":") ?? value;
|
|
4536
4536
|
}))
|
|
4537
4537
|
].sort();
|
|
4538
|
+
var normalizeExpoNativeRoutes = (config, projectRoot) => {
|
|
4539
|
+
if (config.engine !== "expo")
|
|
4540
|
+
return {};
|
|
4541
|
+
const routes = config.routes?.native ?? {};
|
|
4542
|
+
const normalized = {};
|
|
4543
|
+
for (const [route, module] of Object.entries(routes)) {
|
|
4544
|
+
const path = normalizeEntry(route);
|
|
4545
|
+
if (path.includes("?") || path.includes("#") || path !== "/" && path.endsWith("/")) {
|
|
4546
|
+
throw new TypeError(`mobile.routes.native route ${path} must be a canonical path without a query, fragment, or trailing slash.`);
|
|
4547
|
+
}
|
|
4548
|
+
if (path === "/__absolute/native") {
|
|
4549
|
+
throw new TypeError("mobile.routes.native reserves /__absolute/native for the Expo diagnostic screen.");
|
|
4550
|
+
}
|
|
4551
|
+
if (path.includes("*") || path.includes(":")) {
|
|
4552
|
+
throw new TypeError(`mobile.routes.native route ${path} must be static during the Expo experiment; parameters and wildcards are not supported yet.`);
|
|
4553
|
+
}
|
|
4554
|
+
normalized[path] = resolveProjectPath(projectRoot, requireText(module, `mobile.routes.native[${path}]`), `mobile.routes.native[${path}]`);
|
|
4555
|
+
}
|
|
4556
|
+
return Object.fromEntries(Object.entries(normalized).sort(([left], [right]) => left.localeCompare(right)));
|
|
4557
|
+
};
|
|
4538
4558
|
var normalizeAbsoluteMobileConfig = (config, projectRoot) => {
|
|
4539
4559
|
const appId = requireText(config.appId, "mobile.appId");
|
|
4540
4560
|
if (!APP_ID_PATTERN.test(appId)) {
|
|
@@ -4553,10 +4573,12 @@ var normalizeAbsoluteMobileConfig = (config, projectRoot) => {
|
|
|
4553
4573
|
bundleDirectory: resolveProjectPath(projectRoot, config.bundleDirectory ?? ".absolutejs/mobile/web", "mobile.bundleDirectory"),
|
|
4554
4574
|
deepLinkHosts: normalizeHosts(config.deepLinks?.hosts, productionOrigin),
|
|
4555
4575
|
deepLinkScheme,
|
|
4556
|
-
engine: "capacitor",
|
|
4576
|
+
engine: config.engine ?? "capacitor",
|
|
4557
4577
|
entry: normalizeEntry(config.entry),
|
|
4578
|
+
expoNativeRoutes: normalizeExpoNativeRoutes(config, projectRoot),
|
|
4579
|
+
...config.engine === "expo" ? { expoSdkVersion: config.expo?.sdkVersion ?? 57 } : {},
|
|
4558
4580
|
iosVersion: normalizeIosVersion(config.ios?.version),
|
|
4559
|
-
nativeProjectDirectory: resolveProjectPath(projectRoot, config.nativeProject?.directory ?? "mobile", "mobile.nativeProject.directory"),
|
|
4581
|
+
nativeProjectDirectory: resolveProjectPath(projectRoot, config.nativeProject?.directory ?? (config.engine === "expo" ? ".absolutejs/mobile/expo" : "mobile"), "mobile.nativeProject.directory"),
|
|
4560
4582
|
platforms: normalizePlatforms(config.platforms),
|
|
4561
4583
|
productionOrigin,
|
|
4562
4584
|
pushAndroidGoogleServicesFile: resolveProjectPath(projectRoot, config.pushNotifications?.android?.googleServicesFile ?? "google-services.json", "mobile.pushNotifications.android.googleServicesFile")
|
|
@@ -4805,8 +4827,8 @@ var parseAbsoluteMobileBuildPageMetadata = (value) => {
|
|
|
4805
4827
|
};
|
|
4806
4828
|
};
|
|
4807
4829
|
// src/mobile/buildPipeline.ts
|
|
4808
|
-
import { readFile as
|
|
4809
|
-
import { join as
|
|
4830
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
4831
|
+
import { join as join15, resolve as resolve13 } from "path";
|
|
4810
4832
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
4811
4833
|
|
|
4812
4834
|
// src/mobile/buildRelease.ts
|
|
@@ -5460,6 +5482,12 @@ var shellPushModule = () => {
|
|
|
5460
5482
|
return candidate;
|
|
5461
5483
|
throw new TypeError("AbsoluteJS mobile push shell module is missing.");
|
|
5462
5484
|
};
|
|
5485
|
+
var shellExpoDevicesModule = () => {
|
|
5486
|
+
const candidate = ["js", "ts"].map((extension) => join9(import.meta.dir, `shellExpoDevices.${extension}`)).find(existsSync2);
|
|
5487
|
+
if (candidate)
|
|
5488
|
+
return candidate;
|
|
5489
|
+
throw new TypeError("AbsoluteJS Expo device bridge module is missing.");
|
|
5490
|
+
};
|
|
5463
5491
|
var escapeHtml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
5464
5492
|
var contentSecurityPolicy = (productionOrigin) => {
|
|
5465
5493
|
const backend = new URL(productionOrigin);
|
|
@@ -5520,18 +5548,20 @@ var resolveProjectImport = async (projectRoot, specifier) => {
|
|
|
5520
5548
|
throw new TypeError(`${specifier} has an unsafe import entry.`);
|
|
5521
5549
|
return resolved;
|
|
5522
5550
|
};
|
|
5523
|
-
var buildShellBootstrap = async (staging, auth, sync, storagePrefix, deviceCapabilities, projectRoot) => {
|
|
5551
|
+
var buildShellBootstrap = async (staging, auth, sync, storagePrefix, engine, deviceCapabilities, projectRoot) => {
|
|
5552
|
+
const capacitor = engine !== "expo";
|
|
5553
|
+
const shellCapabilities = capacitor ? deviceCapabilities.capabilities : [];
|
|
5524
5554
|
const modulePath = shellBootstrapModule();
|
|
5525
5555
|
const authImport = auth ? `import { createAbsoluteMobileShellAuth } from ${JSON.stringify(shellAuthModule())};
|
|
5526
5556
|
` : "";
|
|
5527
5557
|
const options = auth ? `{ createAuth: createAbsoluteMobileShellAuth${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }` : "";
|
|
5528
5558
|
const syncImport = sync ? `import { installAbsoluteMobileShellSync } from ${JSON.stringify(shellSyncModule())};
|
|
5529
5559
|
` : "";
|
|
5530
|
-
const pushIndex =
|
|
5560
|
+
const pushIndex = shellCapabilities.indexOf("pushNotifications");
|
|
5531
5561
|
const push = pushIndex !== -1;
|
|
5532
5562
|
const pushImport = push ? `import { createAbsoluteMobileShellPush } from ${JSON.stringify(shellPushModule())};
|
|
5533
5563
|
` : "";
|
|
5534
|
-
const capabilityImports = (await Promise.all(
|
|
5564
|
+
const capabilityImports = (await Promise.all(shellCapabilities.map(async (name, index) => {
|
|
5535
5565
|
const provider = deviceCapabilities.providers[name];
|
|
5536
5566
|
if (!provider)
|
|
5537
5567
|
throw new TypeError(`Missing device capability provider ${name}.`);
|
|
@@ -5541,14 +5571,22 @@ var buildShellBootstrap = async (staging, auth, sync, storagePrefix, deviceCapab
|
|
|
5541
5571
|
const pushSetup = push ? `const absoluteMobilePush = createAbsoluteMobileShellPush();
|
|
5542
5572
|
const absoluteMobilePushCapability = absoluteDeviceCapability${pushIndex}(absoluteMobilePush.capabilityOptions);
|
|
5543
5573
|
` : "";
|
|
5544
|
-
const capabilityOptions =
|
|
5574
|
+
const capabilityOptions = shellCapabilities.map((name, index) => `${JSON.stringify(name)}: ${name === "pushNotifications" ? "absoluteMobilePushCapability" : `absoluteDeviceCapability${index}()`}`).join(", ");
|
|
5545
5575
|
const entryPath = join9(staging, ".absolute-mobile-entry.ts");
|
|
5546
|
-
const baseAdapterModule = await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor");
|
|
5576
|
+
const baseAdapterModule = capacitor ? await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor") : shellExpoDevicesModule();
|
|
5577
|
+
const adapterImport = capacitor ? `import { installCapacitorDeviceAdapterIfNative } from ${JSON.stringify(baseAdapterModule)};` : `import { createAbsoluteExpoBridgeFetch, installAbsoluteExpoWebDeviceAdapter } from ${JSON.stringify(baseAdapterModule)};`;
|
|
5578
|
+
const adapterInstall = capacitor ? `installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });` : "installAbsoluteExpoWebDeviceAdapter();";
|
|
5579
|
+
let shellOptions = "{ createFetch: createAbsoluteExpoBridgeFetch }";
|
|
5580
|
+
if (capacitor)
|
|
5581
|
+
shellOptions = options;
|
|
5582
|
+
if (push) {
|
|
5583
|
+
shellOptions = `{ createAuth: (config, options) => createAbsoluteMobileShellAuth(config, options), beforeSignOut: absoluteMobilePush.beforeSignOut, connectPush: (auth) => absoluteMobilePush.connect(auth, absoluteMobilePushCapability)${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }`;
|
|
5584
|
+
}
|
|
5547
5585
|
await writeFile10(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
|
|
5548
|
-
|
|
5586
|
+
${adapterImport}
|
|
5549
5587
|
${authImport}${syncImport}${pushImport}${capabilityImports}
|
|
5550
|
-
${pushSetup}
|
|
5551
|
-
void startAbsoluteMobileShell(${
|
|
5588
|
+
${pushSetup}${adapterInstall}
|
|
5589
|
+
void startAbsoluteMobileShell(${shellOptions});
|
|
5552
5590
|
`);
|
|
5553
5591
|
const build = await Bun.build({
|
|
5554
5592
|
entrypoints: [entryPath],
|
|
@@ -5712,7 +5750,7 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
|
5712
5750
|
writeFile10(join9(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
5713
5751
|
`),
|
|
5714
5752
|
writeFile10(join9(staging, INDEX_FILE), indexHtml(options.config.appName, options.config.productionOrigin)),
|
|
5715
|
-
buildShellBootstrap(staging, options.auth !== undefined, options.auth !== undefined && options.sync === true, `absolutejs.${options.auth?.clientId ?? options.config.appId}.`, options.deviceCapabilities, options.projectRoot)
|
|
5753
|
+
buildShellBootstrap(staging, options.auth !== undefined, options.auth !== undefined && options.sync === true, `absolutejs.${options.auth?.clientId ?? options.config.appId}.`, options.config.engine, options.deviceCapabilities, options.projectRoot)
|
|
5716
5754
|
]);
|
|
5717
5755
|
await installBundle(staging, destination);
|
|
5718
5756
|
return manifest;
|
|
@@ -5954,6 +5992,540 @@ var serializeAbsoluteMobileAuthEnvironment = (config, auth) => auth === undefine
|
|
|
5954
5992
|
// src/mobile/buildPipeline.ts
|
|
5955
5993
|
init_syncSchema();
|
|
5956
5994
|
init_deviceCapabilities();
|
|
5995
|
+
|
|
5996
|
+
// src/mobile/expoProject.ts
|
|
5997
|
+
import {
|
|
5998
|
+
access as access9,
|
|
5999
|
+
cp as cp2,
|
|
6000
|
+
mkdir as mkdir11,
|
|
6001
|
+
mkdtemp as mkdtemp6,
|
|
6002
|
+
readdir as readdir4,
|
|
6003
|
+
readFile as readFile14,
|
|
6004
|
+
rename as rename11,
|
|
6005
|
+
rm as rm9,
|
|
6006
|
+
writeFile as writeFile12
|
|
6007
|
+
} from "fs/promises";
|
|
6008
|
+
import { createHash as createHash10 } from "crypto";
|
|
6009
|
+
import { basename as basename4, dirname as dirname10, join as join14, relative as relative10, resolve as resolve12 } from "path";
|
|
6010
|
+
var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
|
|
6011
|
+
`;
|
|
6012
|
+
var EXPO_ASSET_EXTENSION = ".absasset";
|
|
6013
|
+
var EXPO_PROJECT_MARKER = ".absolutejs-expo-project";
|
|
6014
|
+
var exists3 = async (path) => {
|
|
6015
|
+
try {
|
|
6016
|
+
await access9(path);
|
|
6017
|
+
return true;
|
|
6018
|
+
} catch {
|
|
6019
|
+
return false;
|
|
6020
|
+
}
|
|
6021
|
+
};
|
|
6022
|
+
var portableRelative2 = (from, destination) => {
|
|
6023
|
+
const value = relative10(from, destination).replaceAll("\\", "/");
|
|
6024
|
+
return value.startsWith(".") ? value : `./${value}`;
|
|
6025
|
+
};
|
|
6026
|
+
var routeSegments = (route) => route.split("/").filter(Boolean).map((segment) => {
|
|
6027
|
+
if (segment.startsWith(":"))
|
|
6028
|
+
return `[${segment.slice(1)}]`;
|
|
6029
|
+
if (segment === "." || segment === ".." || !/^[A-Za-z0-9._~-]+$/u.test(segment)) {
|
|
6030
|
+
throw new TypeError(`Expo native route ${route} contains an unsupported segment ${segment}.`);
|
|
6031
|
+
}
|
|
6032
|
+
return segment;
|
|
6033
|
+
});
|
|
6034
|
+
var routeFile = (project, route) => join14(project, "app", ...routeSegments(route), "index.tsx");
|
|
6035
|
+
var expoPackage = () => ({
|
|
6036
|
+
dependencies: {
|
|
6037
|
+
expo: "~57.0.9",
|
|
6038
|
+
"expo-asset": "~57.0.15",
|
|
6039
|
+
"expo-constants": "~57.0.16",
|
|
6040
|
+
"expo-file-system": "~57.0.6",
|
|
6041
|
+
"expo-haptics": "~57.0.2",
|
|
6042
|
+
"expo-linking": "~57.0.8",
|
|
6043
|
+
"expo-router": "~57.0.17",
|
|
6044
|
+
react: "19.2.3",
|
|
6045
|
+
"react-native": "0.86.3",
|
|
6046
|
+
"react-native-safe-area-context": "~5.7.0",
|
|
6047
|
+
"react-native-screens": "4.26.0",
|
|
6048
|
+
"react-native-webview": "13.16.1"
|
|
6049
|
+
},
|
|
6050
|
+
devDependencies: {
|
|
6051
|
+
"@types/react": "~19.2.2",
|
|
6052
|
+
typescript: "~6.0.3"
|
|
6053
|
+
},
|
|
6054
|
+
main: "expo-router/entry",
|
|
6055
|
+
name: "absolutejs-expo-shell",
|
|
6056
|
+
private: true,
|
|
6057
|
+
scripts: {
|
|
6058
|
+
android: "expo run:android",
|
|
6059
|
+
ios: "expo run:ios",
|
|
6060
|
+
start: "expo start --dev-client"
|
|
6061
|
+
},
|
|
6062
|
+
version: "0.0.0"
|
|
6063
|
+
});
|
|
6064
|
+
var expoAppConfig = (config) => ({
|
|
6065
|
+
expo: {
|
|
6066
|
+
android: {
|
|
6067
|
+
intentFilters: config.deepLinkHosts.map((host2) => ({
|
|
6068
|
+
action: "VIEW",
|
|
6069
|
+
autoVerify: true,
|
|
6070
|
+
category: ["BROWSABLE", "DEFAULT"],
|
|
6071
|
+
data: [{ host: host2, pathPrefix: "/", scheme: "https" }]
|
|
6072
|
+
})),
|
|
6073
|
+
package: config.appId
|
|
6074
|
+
},
|
|
6075
|
+
experiments: { typedRoutes: true },
|
|
6076
|
+
ios: {
|
|
6077
|
+
associatedDomains: config.deepLinkHosts.map((host2) => `applinks:${host2}`),
|
|
6078
|
+
bundleIdentifier: config.appId,
|
|
6079
|
+
...config.iosVersion ? { buildNumber: config.iosVersion } : {}
|
|
6080
|
+
},
|
|
6081
|
+
name: config.appName,
|
|
6082
|
+
plugins: ["expo-router"],
|
|
6083
|
+
runtimeVersion: { policy: "appVersion" },
|
|
6084
|
+
scheme: config.deepLinkScheme,
|
|
6085
|
+
slug: config.appId.toLowerCase().replaceAll(".", "-"),
|
|
6086
|
+
version: config.iosVersion ?? "0.1.0"
|
|
6087
|
+
}
|
|
6088
|
+
});
|
|
6089
|
+
var metroConfig = (projectRoot) => `${EXPO_GENERATED_HEADER}const { getDefaultConfig } = require('expo/metro-config');
|
|
6090
|
+
const path = require('node:path');
|
|
6091
|
+
|
|
6092
|
+
const projectRoot = __dirname;
|
|
6093
|
+
const appRoot = ${JSON.stringify(projectRoot)};
|
|
6094
|
+
const config = getDefaultConfig(projectRoot);
|
|
6095
|
+
config.resolver.assetExts.push('absasset');
|
|
6096
|
+
config.resolver.nodeModulesPaths = [
|
|
6097
|
+
path.join(projectRoot, 'node_modules'),
|
|
6098
|
+
path.join(appRoot, 'node_modules')
|
|
6099
|
+
];
|
|
6100
|
+
config.watchFolders = [appRoot];
|
|
6101
|
+
|
|
6102
|
+
module.exports = config;
|
|
6103
|
+
`;
|
|
6104
|
+
var layoutSource = `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
|
|
6105
|
+
|
|
6106
|
+
export default function AbsoluteLayout() {
|
|
6107
|
+
return <Stack screenOptions={{ headerShown: false }} />;
|
|
6108
|
+
}
|
|
6109
|
+
`;
|
|
6110
|
+
var nativeDiagnosticSource = `${EXPO_GENERATED_HEADER}import * as Haptics from 'expo-haptics';
|
|
6111
|
+
import { Link } from 'expo-router';
|
|
6112
|
+
import { Pressable, SafeAreaView, StyleSheet, Text, View } from 'react-native';
|
|
6113
|
+
|
|
6114
|
+
export default function AbsoluteNativeDiagnostic() {
|
|
6115
|
+
return (
|
|
6116
|
+
<SafeAreaView style={styles.page}>
|
|
6117
|
+
<View style={styles.card}>
|
|
6118
|
+
<Text style={styles.eyebrow}>ABSOLUTEJS \xB7 EXPO EXPERIMENT</Text>
|
|
6119
|
+
<Text style={styles.title}>This screen is native React UI.</Text>
|
|
6120
|
+
<Text style={styles.body}>Ordinary AbsoluteJS routes remain embedded web routes. Only explicitly owned routes use React Native.</Text>
|
|
6121
|
+
<Pressable style={styles.button} onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)}>
|
|
6122
|
+
<Text style={styles.buttonText}>Test native haptics</Text>
|
|
6123
|
+
</Pressable>
|
|
6124
|
+
<Link href="/" style={styles.link}>Open the AbsoluteJS app</Link>
|
|
6125
|
+
</View>
|
|
6126
|
+
</SafeAreaView>
|
|
6127
|
+
);
|
|
6128
|
+
}
|
|
6129
|
+
|
|
6130
|
+
const styles = StyleSheet.create({
|
|
6131
|
+
body: { color: '#cbd5e1', fontSize: 16, lineHeight: 24 },
|
|
6132
|
+
button: { backgroundColor: '#f8fafc', borderRadius: 12, padding: 14 },
|
|
6133
|
+
buttonText: { color: '#020617', fontSize: 16, fontWeight: '700', textAlign: 'center' },
|
|
6134
|
+
card: { backgroundColor: '#0f172a', borderRadius: 24, gap: 20, maxWidth: 560, padding: 28, width: '100%' },
|
|
6135
|
+
eyebrow: { color: '#38bdf8', fontSize: 12, fontWeight: '800', letterSpacing: 1.5 },
|
|
6136
|
+
link: { color: '#7dd3fc', fontSize: 16, textAlign: 'center' },
|
|
6137
|
+
page: { alignItems: 'center', backgroundColor: '#020617', flex: 1, justifyContent: 'center', padding: 20 },
|
|
6138
|
+
title: { color: '#f8fafc', fontSize: 32, fontWeight: '800' }
|
|
6139
|
+
});
|
|
6140
|
+
`;
|
|
6141
|
+
var webHostSource = (config) => {
|
|
6142
|
+
const nativeRoutes = [
|
|
6143
|
+
"/__absolute/native",
|
|
6144
|
+
...Object.keys(config.expoNativeRoutes)
|
|
6145
|
+
];
|
|
6146
|
+
return `${EXPO_GENERATED_HEADER}import * as Haptics from 'expo-haptics';
|
|
6147
|
+
import * as Linking from 'expo-linking';
|
|
6148
|
+
import { router, usePathname } from 'expo-router';
|
|
6149
|
+
import { useEffect, useRef, useState } from 'react';
|
|
6150
|
+
import { ActivityIndicator, BackHandler, StyleSheet, View } from 'react-native';
|
|
6151
|
+
import { WebView, type WebViewMessageEvent } from 'react-native-webview';
|
|
6152
|
+
import { materializeAbsoluteWebBundle } from './webAssets';
|
|
6153
|
+
|
|
6154
|
+
const BRIDGE_FORMAT = 1;
|
|
6155
|
+
const MAX_MESSAGE_BYTES = 64 * 1024;
|
|
6156
|
+
const MAX_HTTP_BODY_BYTES = 48 * 1024;
|
|
6157
|
+
const NATIVE_ROUTES = new Set(${JSON.stringify(nativeRoutes)});
|
|
6158
|
+
const PRODUCTION_ORIGIN = ${JSON.stringify(config.productionOrigin)};
|
|
6159
|
+
|
|
6160
|
+
const bridgeBootstrap = (path: string) => \`(() => {
|
|
6161
|
+
const pending = new Map();
|
|
6162
|
+
let sequence = 0;
|
|
6163
|
+
let currentPath = \${JSON.stringify(path)};
|
|
6164
|
+
const send = value => {
|
|
6165
|
+
const source = JSON.stringify(value);
|
|
6166
|
+
if (new TextEncoder().encode(source).byteLength > 65536) throw new Error('Expo bridge message exceeds 64 KiB.');
|
|
6167
|
+
window.ReactNativeWebView.postMessage(source);
|
|
6168
|
+
};
|
|
6169
|
+
globalThis.__absoluteExpoReceive = source => {
|
|
6170
|
+
const message = JSON.parse(source);
|
|
6171
|
+
const operation = pending.get(message.id);
|
|
6172
|
+
if (!operation) return;
|
|
6173
|
+
pending.delete(message.id);
|
|
6174
|
+
clearTimeout(operation.timer);
|
|
6175
|
+
message.error ? operation.reject(new Error(message.error.message)) : operation.resolve(message.result);
|
|
6176
|
+
};
|
|
6177
|
+
globalThis.__absoluteExpoBridge = {
|
|
6178
|
+
request(method, params) {
|
|
6179
|
+
const id = 'web_' + Date.now().toString(36) + '_' + (++sequence).toString(36);
|
|
6180
|
+
send({ format: 1, id, kind: 'request', method, params, path: currentPath });
|
|
6181
|
+
return new Promise((resolve, reject) => {
|
|
6182
|
+
const timer = setTimeout(() => {
|
|
6183
|
+
pending.delete(id);
|
|
6184
|
+
reject(new Error('Expo bridge request timed out.'));
|
|
6185
|
+
}, 10000);
|
|
6186
|
+
pending.set(id, { reject, resolve, timer });
|
|
6187
|
+
});
|
|
6188
|
+
},
|
|
6189
|
+
setPath(path) {
|
|
6190
|
+
currentPath = path;
|
|
6191
|
+
send({ format: 1, kind: 'event', event: 'navigation', path });
|
|
6192
|
+
}
|
|
6193
|
+
};
|
|
6194
|
+
document.addEventListener('click', event => {
|
|
6195
|
+
const anchor = event.target instanceof Element ? event.target.closest('a[href]') : null;
|
|
6196
|
+
if (!anchor) return;
|
|
6197
|
+
const url = new URL(anchor.href, location.href);
|
|
6198
|
+
if (!${JSON.stringify(nativeRoutes)}.includes(url.pathname)) return;
|
|
6199
|
+
event.preventDefault();
|
|
6200
|
+
send({ format: 1, kind: 'event', event: 'navigation', path: url.pathname + url.search + url.hash });
|
|
6201
|
+
}, true);
|
|
6202
|
+
send({ format: 1, kind: 'event', event: 'ready', path: \${JSON.stringify(path)} });
|
|
6203
|
+
})(); true;\`;
|
|
6204
|
+
|
|
6205
|
+
const impact = async (params: Record<string, unknown>) => {
|
|
6206
|
+
const style = params.style;
|
|
6207
|
+
if (style === 'selection') return Haptics.selectionAsync();
|
|
6208
|
+
if (style === 'success' || style === 'warning' || style === 'error') {
|
|
6209
|
+
const value = style === 'success' ? Haptics.NotificationFeedbackType.Success : style === 'warning' ? Haptics.NotificationFeedbackType.Warning : Haptics.NotificationFeedbackType.Error;
|
|
6210
|
+
return Haptics.notificationAsync(value);
|
|
6211
|
+
}
|
|
6212
|
+
const value = style === 'light' ? Haptics.ImpactFeedbackStyle.Light : style === 'heavy' ? Haptics.ImpactFeedbackStyle.Heavy : Haptics.ImpactFeedbackStyle.Medium;
|
|
6213
|
+
return Haptics.impactAsync(value);
|
|
6214
|
+
};
|
|
6215
|
+
|
|
6216
|
+
const bridgeFetch = async (params: Record<string, unknown>) => {
|
|
6217
|
+
if (params.method !== 'GET' || typeof params.url !== 'string' || typeof params.headers !== 'object' || params.headers === null || Array.isArray(params.headers)) throw new Error('Expo bridge HTTP request is invalid.');
|
|
6218
|
+
const url = new URL(params.url);
|
|
6219
|
+
if (url.origin !== PRODUCTION_ORIGIN || url.username || url.password) throw new Error('Expo bridge HTTP request left productionOrigin.');
|
|
6220
|
+
const headers = new Headers();
|
|
6221
|
+
for (const [name, value] of Object.entries(params.headers as Record<string, unknown>)) {
|
|
6222
|
+
const normalized = name.toLowerCase();
|
|
6223
|
+
if (typeof value !== 'string' || (normalized !== 'accept' && !normalized.startsWith('x-absolute-mobile-'))) throw new Error('Expo bridge HTTP header is not allowed.');
|
|
6224
|
+
headers.set(normalized, value);
|
|
6225
|
+
}
|
|
6226
|
+
const response = await fetch(url, { headers, method: 'GET', redirect: 'manual' });
|
|
6227
|
+
if (new URL(response.url || url.href).origin !== PRODUCTION_ORIGIN || response.status >= 300 && response.status < 400) throw new Error('Expo bridge HTTP redirects are not allowed.');
|
|
6228
|
+
const body = await response.text();
|
|
6229
|
+
if (new TextEncoder().encode(body).byteLength > MAX_HTTP_BODY_BYTES) throw new Error('Expo bridge HTTP response exceeds 48 KiB.');
|
|
6230
|
+
const responseHeaders: Record<string, string> = {};
|
|
6231
|
+
for (const name of ['cache-control', 'content-type']) {
|
|
6232
|
+
const value = response.headers.get(name);
|
|
6233
|
+
if (value) responseHeaders[name] = value;
|
|
6234
|
+
}
|
|
6235
|
+
return { body, headers: responseHeaders, status: response.status };
|
|
6236
|
+
};
|
|
6237
|
+
|
|
6238
|
+
export function AbsoluteWebHost() {
|
|
6239
|
+
const pathname = usePathname() || '/';
|
|
6240
|
+
const webView = useRef<WebView>(null);
|
|
6241
|
+
const [indexUri, setIndexUri] = useState<string>();
|
|
6242
|
+
const [canGoBack, setCanGoBack] = useState(false);
|
|
6243
|
+
const activeWebPath = useRef(pathname);
|
|
6244
|
+
|
|
6245
|
+
useEffect(() => { void materializeAbsoluteWebBundle().then(setIndexUri); }, []);
|
|
6246
|
+
useEffect(() => {
|
|
6247
|
+
const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
|
|
6248
|
+
if (!canGoBack) return false;
|
|
6249
|
+
webView.current?.goBack();
|
|
6250
|
+
return true;
|
|
6251
|
+
});
|
|
6252
|
+
return () => subscription.remove();
|
|
6253
|
+
}, [canGoBack]);
|
|
6254
|
+
|
|
6255
|
+
const respond = (message: Record<string, unknown>) => {
|
|
6256
|
+
const source = JSON.stringify(message).replaceAll('\\u2028', '\\\\u2028').replaceAll('\\u2029', '\\\\u2029');
|
|
6257
|
+
if (new TextEncoder().encode(source).byteLength > MAX_MESSAGE_BYTES) throw new Error('Expo bridge response exceeds 64 KiB.');
|
|
6258
|
+
webView.current?.injectJavaScript(\`globalThis.__absoluteExpoReceive(\${JSON.stringify(source)}); true;\`);
|
|
6259
|
+
};
|
|
6260
|
+
const onMessage = async (event: WebViewMessageEvent) => {
|
|
6261
|
+
const source = event.nativeEvent.data;
|
|
6262
|
+
if (new TextEncoder().encode(source).byteLength > MAX_MESSAGE_BYTES) return;
|
|
6263
|
+
let message: Record<string, unknown>;
|
|
6264
|
+
try { message = JSON.parse(source); } catch { return; }
|
|
6265
|
+
if (message.format !== BRIDGE_FORMAT || typeof message.path !== 'string' || !message.path.startsWith('/') || message.path.startsWith('//')) return;
|
|
6266
|
+
if (message.kind === 'event' && (message.event === 'navigation' || message.event === 'ready')) {
|
|
6267
|
+
const target = new URL(message.path, PRODUCTION_ORIGIN);
|
|
6268
|
+
if (target.origin !== PRODUCTION_ORIGIN) return;
|
|
6269
|
+
if (NATIVE_ROUTES.has(target.pathname)) router.push(message.path as never);
|
|
6270
|
+
else activeWebPath.current = message.path;
|
|
6271
|
+
return;
|
|
6272
|
+
}
|
|
6273
|
+
if (message.kind !== 'request' || typeof message.id !== 'string' || message.path !== activeWebPath.current) return;
|
|
6274
|
+
try {
|
|
6275
|
+
if (typeof message.params !== 'object' || message.params === null || Array.isArray(message.params)) throw new Error('Expo bridge method params are invalid.');
|
|
6276
|
+
if (message.method === 'devices.haptics.impact') {
|
|
6277
|
+
const style = (message.params as Record<string, unknown>).style;
|
|
6278
|
+
if (typeof style !== 'string' || !['error', 'heavy', 'light', 'medium', 'selection', 'success', 'vibrate', 'warning'].includes(style)) throw new Error('Expo bridge haptics style is invalid.');
|
|
6279
|
+
await impact(message.params as Record<string, unknown>);
|
|
6280
|
+
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: null });
|
|
6281
|
+
} else if (message.method === 'http.fetch') {
|
|
6282
|
+
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await bridgeFetch(message.params as Record<string, unknown>) });
|
|
6283
|
+
} else {
|
|
6284
|
+
throw new Error('Expo bridge method is not allowed.');
|
|
6285
|
+
}
|
|
6286
|
+
} catch (error) {
|
|
6287
|
+
respond({ error: { code: 'failed', message: error instanceof Error ? error.message : 'Native operation failed.' }, format: BRIDGE_FORMAT, id: message.id, kind: 'response' });
|
|
6288
|
+
}
|
|
6289
|
+
};
|
|
6290
|
+
|
|
6291
|
+
if (!indexUri) return <View style={styles.loading}><ActivityIndicator /></View>;
|
|
6292
|
+
return <WebView
|
|
6293
|
+
allowFileAccess
|
|
6294
|
+
allowFileAccessFromFileURLs
|
|
6295
|
+
allowUniversalAccessFromFileURLs={false}
|
|
6296
|
+
allowingReadAccessToURL={indexUri.slice(0, indexUri.lastIndexOf('/') + 1)}
|
|
6297
|
+
injectedJavaScriptBeforeContentLoaded={bridgeBootstrap(pathname)}
|
|
6298
|
+
onMessage={onMessage}
|
|
6299
|
+
onNavigationStateChange={state => setCanGoBack(state.canGoBack)}
|
|
6300
|
+
onShouldStartLoadWithRequest={request => {
|
|
6301
|
+
if (request.url.startsWith('file:') || request.url.startsWith(PRODUCTION_ORIGIN)) return true;
|
|
6302
|
+
void Linking.openURL(request.url);
|
|
6303
|
+
return false;
|
|
6304
|
+
}}
|
|
6305
|
+
ref={webView}
|
|
6306
|
+
source={{ uri: indexUri + '?absolutePath=' + encodeURIComponent(pathname) }}
|
|
6307
|
+
style={styles.web}
|
|
6308
|
+
/>;
|
|
6309
|
+
}
|
|
6310
|
+
|
|
6311
|
+
const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, justifyContent: 'center' }, web: { flex: 1 } });
|
|
6312
|
+
`;
|
|
6313
|
+
};
|
|
6314
|
+
var webRouteSource = `${EXPO_GENERATED_HEADER}import { AbsoluteWebHost } from '../src/generated/AbsoluteWebHost';
|
|
6315
|
+
|
|
6316
|
+
export default AbsoluteWebHost;
|
|
6317
|
+
`;
|
|
6318
|
+
var catchAllRouteSource = `${EXPO_GENERATED_HEADER}import { AbsoluteWebHost } from '../src/generated/AbsoluteWebHost';
|
|
6319
|
+
|
|
6320
|
+
export default AbsoluteWebHost;
|
|
6321
|
+
`;
|
|
6322
|
+
var nativeWrapperSource = (wrapper, module) => `${EXPO_GENERATED_HEADER}export { default } from ${JSON.stringify(portableRelative2(dirname10(wrapper), module).replace(/\.(?:[cm]?[jt]sx?)$/u, ""))};
|
|
6323
|
+
`;
|
|
6324
|
+
var expoTsConfig = (projectRoot, project) => ({
|
|
6325
|
+
compilerOptions: {
|
|
6326
|
+
paths: {
|
|
6327
|
+
"*": [
|
|
6328
|
+
"./node_modules/*",
|
|
6329
|
+
`${portableRelative2(project, projectRoot)}/node_modules/*`
|
|
6330
|
+
],
|
|
6331
|
+
react: ["./node_modules/@types/react/index.d.ts"],
|
|
6332
|
+
"react/*": ["./node_modules/@types/react/*"]
|
|
6333
|
+
},
|
|
6334
|
+
strict: true
|
|
6335
|
+
},
|
|
6336
|
+
extends: "expo/tsconfig.base"
|
|
6337
|
+
});
|
|
6338
|
+
var writeManagedFile = async (path, source, force) => {
|
|
6339
|
+
await mkdir11(dirname10(path), { recursive: true });
|
|
6340
|
+
if (await exists3(path)) {
|
|
6341
|
+
const current = await readFile14(path, "utf8");
|
|
6342
|
+
if (current === source)
|
|
6343
|
+
return false;
|
|
6344
|
+
if (!force && !current.startsWith(EXPO_GENERATED_HEADER)) {
|
|
6345
|
+
throw new TypeError(`Expo project file ${path} is not AbsoluteJS-managed; rerun with --force only after reviewing it.`);
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
6349
|
+
await writeFile12(temporary, source, { flag: "wx" });
|
|
6350
|
+
await rename11(temporary, path);
|
|
6351
|
+
return true;
|
|
6352
|
+
};
|
|
6353
|
+
var jsonSource = (value) => `${JSON.stringify(value, null, "\t")}
|
|
6354
|
+
`;
|
|
6355
|
+
var writeAbsoluteExpoProject = async (config, options) => {
|
|
6356
|
+
if (config.engine !== "expo")
|
|
6357
|
+
throw new TypeError("Expo project generation requires mobile.engine: expo.");
|
|
6358
|
+
const projectRoot = resolve12(options.projectRoot);
|
|
6359
|
+
const project = config.nativeProjectDirectory;
|
|
6360
|
+
const routeModules = Object.entries(config.expoNativeRoutes);
|
|
6361
|
+
const moduleChecks = await Promise.all(routeModules.map(async ([route, module]) => ({
|
|
6362
|
+
exists: await exists3(module),
|
|
6363
|
+
module,
|
|
6364
|
+
route
|
|
6365
|
+
})));
|
|
6366
|
+
const missing = moduleChecks.find((entry) => !entry.exists);
|
|
6367
|
+
if (missing) {
|
|
6368
|
+
throw new TypeError(`Expo native route ${missing.route} references missing module ${missing.module}.`);
|
|
6369
|
+
}
|
|
6370
|
+
const marker = join14(project, EXPO_PROJECT_MARKER);
|
|
6371
|
+
if (await exists3(project) && !await exists3(marker)) {
|
|
6372
|
+
const entries = await readdir4(project);
|
|
6373
|
+
if (entries.length > 0 && !options.force) {
|
|
6374
|
+
throw new TypeError(`Expo project directory ${project} is not AbsoluteJS-managed; rerun with --force only after reviewing it.`);
|
|
6375
|
+
}
|
|
6376
|
+
}
|
|
6377
|
+
await mkdir11(project, { recursive: true });
|
|
6378
|
+
await writeFile12(marker, `format=1
|
|
6379
|
+
`);
|
|
6380
|
+
const files = new Map([
|
|
6381
|
+
[
|
|
6382
|
+
join14(project, ".gitignore"),
|
|
6383
|
+
`.expo/
|
|
6384
|
+
android/
|
|
6385
|
+
ios/
|
|
6386
|
+
node_modules/
|
|
6387
|
+
`
|
|
6388
|
+
],
|
|
6389
|
+
[join14(project, "app.json"), jsonSource(expoAppConfig(config))],
|
|
6390
|
+
[join14(project, "package.json"), jsonSource(expoPackage())],
|
|
6391
|
+
[join14(project, "metro.config.js"), metroConfig(projectRoot)],
|
|
6392
|
+
[
|
|
6393
|
+
join14(project, "tsconfig.json"),
|
|
6394
|
+
jsonSource(expoTsConfig(projectRoot, project))
|
|
6395
|
+
],
|
|
6396
|
+
[join14(project, "app", "_layout.tsx"), layoutSource],
|
|
6397
|
+
[
|
|
6398
|
+
join14(project, "app", "__absolute", "native", "index.tsx"),
|
|
6399
|
+
nativeDiagnosticSource
|
|
6400
|
+
],
|
|
6401
|
+
[
|
|
6402
|
+
join14(project, "src", "generated", "AbsoluteWebHost.tsx"),
|
|
6403
|
+
webHostSource(config)
|
|
6404
|
+
]
|
|
6405
|
+
]);
|
|
6406
|
+
if (!config.expoNativeRoutes["/"]) {
|
|
6407
|
+
files.set(join14(project, "app", "index.tsx"), webRouteSource);
|
|
6408
|
+
}
|
|
6409
|
+
files.set(join14(project, "app", "[...absolute].tsx"), catchAllRouteSource);
|
|
6410
|
+
for (const [route, module] of routeModules) {
|
|
6411
|
+
const wrapper = route === "/" ? join14(project, "app", "index.tsx") : routeFile(project, route);
|
|
6412
|
+
files.set(wrapper, nativeWrapperSource(wrapper, module));
|
|
6413
|
+
}
|
|
6414
|
+
const changes = await Promise.all([...files].map(([path, source]) => writeManagedFile(path, source, true)));
|
|
6415
|
+
const changed = changes.filter(Boolean).length;
|
|
6416
|
+
return { changed, path: project, written: [...files.keys()] };
|
|
6417
|
+
};
|
|
6418
|
+
var walkFiles = async (root, directory = root) => {
|
|
6419
|
+
const entries = await readdir4(directory, { withFileTypes: true });
|
|
6420
|
+
const nested = await Promise.all(entries.map((entry) => {
|
|
6421
|
+
const path = join14(directory, entry.name);
|
|
6422
|
+
if (entry.isDirectory())
|
|
6423
|
+
return walkFiles(root, path);
|
|
6424
|
+
if (entry.isFile())
|
|
6425
|
+
return [path];
|
|
6426
|
+
throw new TypeError(`Expo embedded bundle cannot contain a symbolic link or special file: ${path}.`);
|
|
6427
|
+
}));
|
|
6428
|
+
return nested.flat().sort();
|
|
6429
|
+
};
|
|
6430
|
+
var renameIfPresent = async (source, destination) => {
|
|
6431
|
+
try {
|
|
6432
|
+
await rename11(source, destination);
|
|
6433
|
+
return true;
|
|
6434
|
+
} catch (error) {
|
|
6435
|
+
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT") {
|
|
6436
|
+
return false;
|
|
6437
|
+
}
|
|
6438
|
+
throw error;
|
|
6439
|
+
}
|
|
6440
|
+
};
|
|
6441
|
+
var installStagedDirectory = async (staging, destination) => {
|
|
6442
|
+
const backup = `${destination}.previous-${crypto.randomUUID()}`;
|
|
6443
|
+
const moved = await renameIfPresent(destination, backup);
|
|
6444
|
+
try {
|
|
6445
|
+
await rename11(staging, destination);
|
|
6446
|
+
if (moved)
|
|
6447
|
+
await rm9(backup, { force: true, recursive: true });
|
|
6448
|
+
} catch (error) {
|
|
6449
|
+
if (moved)
|
|
6450
|
+
await rename11(backup, destination);
|
|
6451
|
+
throw error;
|
|
6452
|
+
}
|
|
6453
|
+
};
|
|
6454
|
+
var assetModuleSource = (assets, bundleId) => `${EXPO_GENERATED_HEADER}import { Asset } from 'expo-asset';
|
|
6455
|
+
import { Directory, File, Paths } from 'expo-file-system';
|
|
6456
|
+
|
|
6457
|
+
declare const require: (path: string) => number;
|
|
6458
|
+
const BUNDLE_ID = ${JSON.stringify(bundleId)};
|
|
6459
|
+
const ASSETS = [
|
|
6460
|
+
${assets.map(({ asset, path }) => ` { module: require(${JSON.stringify(asset)}), path: ${JSON.stringify(path)} }`).join(`,
|
|
6461
|
+
`)}
|
|
6462
|
+
] as const;
|
|
6463
|
+
|
|
6464
|
+
export const materializeAbsoluteWebBundle = async () => {
|
|
6465
|
+
const root = new Directory(Paths.document, 'absolutejs-web', BUNDLE_ID);
|
|
6466
|
+
root.create({ idempotent: true, intermediates: true });
|
|
6467
|
+
for (const entry of ASSETS) {
|
|
6468
|
+
const parts = entry.path.split('/');
|
|
6469
|
+
const name = parts.pop();
|
|
6470
|
+
if (!name) throw new Error('AbsoluteJS embedded asset path is invalid.');
|
|
6471
|
+
const directory = new Directory(root, ...parts);
|
|
6472
|
+
directory.create({ idempotent: true, intermediates: true });
|
|
6473
|
+
const destination = new File(directory, name);
|
|
6474
|
+
if (destination.exists) continue;
|
|
6475
|
+
const asset = await Asset.fromModule(entry.module).downloadAsync();
|
|
6476
|
+
if (!asset.localUri) throw new Error('Expo did not materialize an embedded AbsoluteJS asset.');
|
|
6477
|
+
new File(asset.localUri).copy(destination);
|
|
6478
|
+
}
|
|
6479
|
+
|
|
6480
|
+
return new File(root, 'index.html').uri;
|
|
6481
|
+
};
|
|
6482
|
+
`;
|
|
6483
|
+
var syncAbsoluteExpoWebAssets = async (config) => {
|
|
6484
|
+
if (config.engine !== "expo")
|
|
6485
|
+
throw new TypeError("Expo asset sync requires mobile.engine: expo.");
|
|
6486
|
+
const marker = join14(config.nativeProjectDirectory, EXPO_PROJECT_MARKER);
|
|
6487
|
+
if (!await exists3(marker)) {
|
|
6488
|
+
throw new TypeError("Expo asset sync requires an AbsoluteJS-managed Expo project. Run mobile init first.");
|
|
6489
|
+
}
|
|
6490
|
+
const manifestPath = join14(config.bundleDirectory, "absolute-mobile-manifest.json");
|
|
6491
|
+
const manifest = JSON.parse(await readFile14(manifestPath, "utf8"));
|
|
6492
|
+
const appBuild = typeof manifest === "object" && manifest !== null && typeof Reflect.get(manifest, "appBuild") === "string" ? String(Reflect.get(manifest, "appBuild")) : undefined;
|
|
6493
|
+
if (!appBuild)
|
|
6494
|
+
throw new TypeError("AbsoluteJS mobile manifest has no appBuild.");
|
|
6495
|
+
const files = await walkFiles(config.bundleDirectory);
|
|
6496
|
+
const bundleHash = createHash10("sha256");
|
|
6497
|
+
const filesWithContents = await Promise.all(files.map(async (file) => ({ contents: await readFile14(file), file })));
|
|
6498
|
+
filesWithContents.forEach(({ contents, file }) => {
|
|
6499
|
+
bundleHash.update(relative10(config.bundleDirectory, file).replaceAll("\\", "/"));
|
|
6500
|
+
bundleHash.update("\x00");
|
|
6501
|
+
bundleHash.update(contents);
|
|
6502
|
+
bundleHash.update("\x00");
|
|
6503
|
+
});
|
|
6504
|
+
const bundleId = `amexpo_${bundleHash.digest("hex")}`;
|
|
6505
|
+
const destination = join14(config.nativeProjectDirectory, "assets", "absolute");
|
|
6506
|
+
await mkdir11(dirname10(destination), { recursive: true });
|
|
6507
|
+
const staging = await mkdtemp6(join14(dirname10(destination), `.${basename4(destination)}.stage-`));
|
|
6508
|
+
let assets;
|
|
6509
|
+
try {
|
|
6510
|
+
assets = await Promise.all(files.map(async (source, index) => {
|
|
6511
|
+
const name = `${String(index).padStart(6, "0")}${EXPO_ASSET_EXTENSION}`;
|
|
6512
|
+
await cp2(source, join14(staging, name));
|
|
6513
|
+
return {
|
|
6514
|
+
asset: portableRelative2(join14(config.nativeProjectDirectory, "src", "generated"), join14(destination, name)),
|
|
6515
|
+
path: relative10(config.bundleDirectory, source).replaceAll("\\", "/")
|
|
6516
|
+
};
|
|
6517
|
+
}));
|
|
6518
|
+
await installStagedDirectory(staging, destination);
|
|
6519
|
+
} catch (error) {
|
|
6520
|
+
await rm9(staging, { force: true, recursive: true });
|
|
6521
|
+
throw error;
|
|
6522
|
+
}
|
|
6523
|
+
const generated = join14(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
|
|
6524
|
+
await writeManagedFile(generated, assetModuleSource(assets, bundleId), true);
|
|
6525
|
+
return { appBuild, assets: assets.length, bundleId, path: destination };
|
|
6526
|
+
};
|
|
6527
|
+
|
|
6528
|
+
// src/mobile/buildPipeline.ts
|
|
5957
6529
|
var isElysiaApp = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "compile") === "function" && Array.isArray(Reflect.get(value, "routes"));
|
|
5958
6530
|
var isStringRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
5959
6531
|
var serverExportName = (loaded, app) => {
|
|
@@ -5987,11 +6559,11 @@ var loadServerApp = async (producerPath) => {
|
|
|
5987
6559
|
return { app, exportName };
|
|
5988
6560
|
};
|
|
5989
6561
|
var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
5990
|
-
const buildDirectory =
|
|
6562
|
+
const buildDirectory = resolve13(options.buildDirectory);
|
|
5991
6563
|
const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
|
|
5992
|
-
const root =
|
|
6564
|
+
const root = join15(buildDirectory, ".absolutejs", "mobile-compatibility");
|
|
5993
6565
|
const [manifestSource, previous] = await Promise.all([
|
|
5994
|
-
|
|
6566
|
+
readFile15(join15(buildDirectory, "manifest.json"), "utf8"),
|
|
5995
6567
|
readAbsoluteMobileMaterializedReleases(root)
|
|
5996
6568
|
]);
|
|
5997
6569
|
const manifest = JSON.parse(manifestSource);
|
|
@@ -6004,11 +6576,11 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
6004
6576
|
process.env.ABSOLUTE_BUILD_DIR = buildDirectory;
|
|
6005
6577
|
process.env.ABSOLUTE_COMPILED_RUNTIME = "1";
|
|
6006
6578
|
if (options.configPath) {
|
|
6007
|
-
process.env.ABSOLUTE_CONFIG =
|
|
6579
|
+
process.env.ABSOLUTE_CONFIG = resolve13(options.projectRoot, options.configPath);
|
|
6008
6580
|
}
|
|
6009
6581
|
let loaded;
|
|
6010
6582
|
try {
|
|
6011
|
-
loaded = await loadServerApp(
|
|
6583
|
+
loaded = await loadServerApp(resolve13(options.producerPath));
|
|
6012
6584
|
} finally {
|
|
6013
6585
|
restoreEnvironmentVariable("ABSOLUTE_BUILD_DIR", previousBuildDirectory);
|
|
6014
6586
|
restoreEnvironmentVariable("ABSOLUTE_COMPILED_RUNTIME", previousCompiledRuntime);
|
|
@@ -6021,19 +6593,27 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
6021
6593
|
manifest,
|
|
6022
6594
|
previousArtifacts: previous.map(({ artifact }) => artifact),
|
|
6023
6595
|
producerExport: loaded.exportName,
|
|
6024
|
-
producerPath:
|
|
6596
|
+
producerPath: resolve13(options.producerPath),
|
|
6025
6597
|
runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
|
|
6026
6598
|
});
|
|
6027
6599
|
const auth = resolveAbsoluteMobileAuthManifest(options.projectRoot, mobile);
|
|
6600
|
+
if (mobile.engine === "expo" && auth) {
|
|
6601
|
+
throw new TypeError("Expo mobile Auth is not released yet. The experimental Expo shell cannot safely substitute Capacitor credentials; remove engine: 'expo' or wait for @absolutejs/auth-expo.");
|
|
6602
|
+
}
|
|
6028
6603
|
const sync = auth !== undefined && projectUsesAbsoluteSync(options.projectRoot);
|
|
6029
6604
|
const syncSchema = sync ? discoverAbsoluteSyncSchema(options.projectRoot) : undefined;
|
|
6030
6605
|
const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot);
|
|
6031
6606
|
const usesPush = deviceCapabilities.capabilities.includes("pushNotifications");
|
|
6607
|
+
if (mobile.engine === "expo" && deviceCapabilities.capabilities.some((capability) => capability !== "haptics")) {
|
|
6608
|
+
throw new TypeError("Experimental Expo builds currently bridge only @absolutejs/devices haptics. Other detected device capabilities require their Expo adapters.");
|
|
6609
|
+
}
|
|
6032
6610
|
if (usesPush && !auth)
|
|
6033
6611
|
throw new TypeError("Portable push notifications require @absolutejs/auth so provider tokens can be registered without exposing identity controls to page code.");
|
|
6034
6612
|
if (usesPush && !loaded.app.routes.some((route) => route.path === "/auth/push" || route.path === "/auth/mobile/push"))
|
|
6035
6613
|
throw new TypeError("@absolutejs/devices pushNotifications is used, but Auth push is not configured. Pass a trusted server-side registrar to auth({ push: ... }).");
|
|
6036
|
-
|
|
6614
|
+
if (mobile.engine === "capacitor") {
|
|
6615
|
+
assertAbsoluteDeviceCapabilityPackages(options.projectRoot, deviceCapabilities);
|
|
6616
|
+
}
|
|
6037
6617
|
if (auth && !loaded.app.routes.some((route) => route.path === "/.well-known/openid-configuration")) {
|
|
6038
6618
|
throw new TypeError("@absolutejs/auth is installed, but its OIDC provider is not mounted. Native authentication requires the auth oidc configuration so AbsoluteJS can provision a public PKCE client.");
|
|
6039
6619
|
}
|
|
@@ -6057,6 +6637,12 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
6057
6637
|
...sync ? { sync: true } : {},
|
|
6058
6638
|
...syncSchema ? { syncSchema: { components: syncSchema.components } } : {}
|
|
6059
6639
|
});
|
|
6640
|
+
if (mobile.engine === "expo") {
|
|
6641
|
+
await writeAbsoluteExpoProject(mobile, {
|
|
6642
|
+
projectRoot: options.projectRoot
|
|
6643
|
+
});
|
|
6644
|
+
await syncAbsoluteExpoWebAssets(mobile);
|
|
6645
|
+
}
|
|
6060
6646
|
return current.artifact;
|
|
6061
6647
|
};
|
|
6062
6648
|
|
|
@@ -6174,10 +6760,152 @@ var installAbsoluteMobileSyncRemediation = (bridge = {
|
|
|
6174
6760
|
// src/mobile/index.ts
|
|
6175
6761
|
init_deviceCapabilities();
|
|
6176
6762
|
|
|
6763
|
+
// src/mobile/expoBridge.ts
|
|
6764
|
+
var ABSOLUTE_EXPO_BRIDGE_FORMAT = 1;
|
|
6765
|
+
var ABSOLUTE_EXPO_BRIDGE_MAX_BYTES = 64 * 1024;
|
|
6766
|
+
var ABSOLUTE_EXPO_BRIDGE_METHODS = [
|
|
6767
|
+
"devices.haptics.impact",
|
|
6768
|
+
"http.fetch"
|
|
6769
|
+
];
|
|
6770
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
6771
|
+
var validId = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,80}$/u.test(value);
|
|
6772
|
+
var validPath = (value) => typeof value === "string" && value.startsWith("/") && !value.startsWith("//") && value.length <= 4096;
|
|
6773
|
+
var parseRequest = (value) => {
|
|
6774
|
+
if (typeof value.id !== "string" || !validId(value.id))
|
|
6775
|
+
throw new TypeError("Expo bridge request id is invalid.");
|
|
6776
|
+
const method = ABSOLUTE_EXPO_BRIDGE_METHODS.find((candidate) => candidate === value.method);
|
|
6777
|
+
if (!method) {
|
|
6778
|
+
throw new TypeError("Expo bridge method is not allowed.");
|
|
6779
|
+
}
|
|
6780
|
+
if (!isRecord8(value.params))
|
|
6781
|
+
throw new TypeError("Expo bridge request params must be an object.");
|
|
6782
|
+
if (typeof value.path !== "string" || !validPath(value.path))
|
|
6783
|
+
throw new TypeError("Expo bridge request path is invalid.");
|
|
6784
|
+
if (method === "devices.haptics.impact") {
|
|
6785
|
+
const styles = new Set([
|
|
6786
|
+
"error",
|
|
6787
|
+
"heavy",
|
|
6788
|
+
"light",
|
|
6789
|
+
"medium",
|
|
6790
|
+
"selection",
|
|
6791
|
+
"success",
|
|
6792
|
+
"vibrate",
|
|
6793
|
+
"warning"
|
|
6794
|
+
]);
|
|
6795
|
+
if (typeof value.params.style !== "string" || !styles.has(value.params.style))
|
|
6796
|
+
throw new TypeError("Expo bridge haptics style is invalid.");
|
|
6797
|
+
if (value.params.durationMs !== undefined && (typeof value.params.durationMs !== "number" || !Number.isFinite(value.params.durationMs) || value.params.durationMs < 0 || value.params.durationMs > 1e4)) {
|
|
6798
|
+
throw new TypeError("Expo bridge haptics duration is invalid.");
|
|
6799
|
+
}
|
|
6800
|
+
}
|
|
6801
|
+
if (method === "http.fetch") {
|
|
6802
|
+
if (typeof value.params.url !== "string")
|
|
6803
|
+
throw new TypeError("Expo bridge HTTP URL is invalid.");
|
|
6804
|
+
const url = new URL(value.params.url);
|
|
6805
|
+
const loopbackHttp = url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]");
|
|
6806
|
+
if (url.protocol !== "https:" && !loopbackHttp)
|
|
6807
|
+
throw new TypeError("Expo bridge HTTP URL must use HTTPS.");
|
|
6808
|
+
if (value.params.method !== "GET")
|
|
6809
|
+
throw new TypeError("Expo bridge HTTP method is not allowed.");
|
|
6810
|
+
if (!isRecord8(value.params.headers))
|
|
6811
|
+
throw new TypeError("Expo bridge HTTP headers must be an object.");
|
|
6812
|
+
const headersAllowed = Object.entries(value.params.headers).every(([name, header]) => typeof header === "string" && (name.toLowerCase() === "accept" || name.toLowerCase().startsWith("x-absolute-mobile-")));
|
|
6813
|
+
if (!headersAllowed) {
|
|
6814
|
+
throw new TypeError("Expo bridge HTTP header is not allowed.");
|
|
6815
|
+
}
|
|
6816
|
+
}
|
|
6817
|
+
return {
|
|
6818
|
+
format: ABSOLUTE_EXPO_BRIDGE_FORMAT,
|
|
6819
|
+
id: value.id,
|
|
6820
|
+
kind: "request",
|
|
6821
|
+
method,
|
|
6822
|
+
params: value.params,
|
|
6823
|
+
path: value.path
|
|
6824
|
+
};
|
|
6825
|
+
};
|
|
6826
|
+
var parseResponse = (value) => {
|
|
6827
|
+
if (typeof value.id !== "string" || !validId(value.id))
|
|
6828
|
+
throw new TypeError("Expo bridge response id is invalid.");
|
|
6829
|
+
if (value.error !== undefined) {
|
|
6830
|
+
if (!isRecord8(value.error) || typeof value.error.code !== "string" || typeof value.error.message !== "string") {
|
|
6831
|
+
throw new TypeError("Expo bridge response error is invalid.");
|
|
6832
|
+
}
|
|
6833
|
+
if (value.result !== undefined)
|
|
6834
|
+
throw new TypeError("Expo bridge response cannot contain result and error.");
|
|
6835
|
+
}
|
|
6836
|
+
if (isRecord8(value.error)) {
|
|
6837
|
+
return {
|
|
6838
|
+
error: {
|
|
6839
|
+
code: String(value.error.code),
|
|
6840
|
+
message: String(value.error.message)
|
|
6841
|
+
},
|
|
6842
|
+
format: ABSOLUTE_EXPO_BRIDGE_FORMAT,
|
|
6843
|
+
id: value.id,
|
|
6844
|
+
kind: "response"
|
|
6845
|
+
};
|
|
6846
|
+
}
|
|
6847
|
+
return {
|
|
6848
|
+
format: ABSOLUTE_EXPO_BRIDGE_FORMAT,
|
|
6849
|
+
id: value.id,
|
|
6850
|
+
kind: "response",
|
|
6851
|
+
result: value.result
|
|
6852
|
+
};
|
|
6853
|
+
};
|
|
6854
|
+
var parseEvent = (value) => {
|
|
6855
|
+
if (value.event !== "ready" && value.event !== "navigation") {
|
|
6856
|
+
throw new TypeError("Expo bridge event is not allowed.");
|
|
6857
|
+
}
|
|
6858
|
+
if (typeof value.path !== "string" || !validPath(value.path))
|
|
6859
|
+
throw new TypeError("Expo bridge event path is invalid.");
|
|
6860
|
+
if (value.payload !== undefined && !isRecord8(value.payload)) {
|
|
6861
|
+
throw new TypeError("Expo bridge event payload must be an object.");
|
|
6862
|
+
}
|
|
6863
|
+
return {
|
|
6864
|
+
event: value.event,
|
|
6865
|
+
format: ABSOLUTE_EXPO_BRIDGE_FORMAT,
|
|
6866
|
+
kind: "event",
|
|
6867
|
+
path: value.path,
|
|
6868
|
+
...value.payload ? { payload: value.payload } : {}
|
|
6869
|
+
};
|
|
6870
|
+
};
|
|
6871
|
+
var createAbsoluteExpoBridgeError = (id, code, message) => parseResponse({
|
|
6872
|
+
error: { code, message },
|
|
6873
|
+
format: ABSOLUTE_EXPO_BRIDGE_FORMAT,
|
|
6874
|
+
id,
|
|
6875
|
+
kind: "response"
|
|
6876
|
+
});
|
|
6877
|
+
var createAbsoluteExpoBridgeResponse = (id, result) => parseResponse({
|
|
6878
|
+
format: ABSOLUTE_EXPO_BRIDGE_FORMAT,
|
|
6879
|
+
id,
|
|
6880
|
+
kind: "response",
|
|
6881
|
+
result
|
|
6882
|
+
});
|
|
6883
|
+
var parseAbsoluteExpoBridgeMessage = (source) => {
|
|
6884
|
+
if (new TextEncoder().encode(source).byteLength > ABSOLUTE_EXPO_BRIDGE_MAX_BYTES)
|
|
6885
|
+
throw new TypeError("Expo bridge message exceeds 64 KiB.");
|
|
6886
|
+
let parsed;
|
|
6887
|
+
try {
|
|
6888
|
+
parsed = JSON.parse(source);
|
|
6889
|
+
} catch (cause) {
|
|
6890
|
+
throw new TypeError("Expo bridge message is not valid JSON.", {
|
|
6891
|
+
cause
|
|
6892
|
+
});
|
|
6893
|
+
}
|
|
6894
|
+
if (!isRecord8(parsed) || parsed.format !== ABSOLUTE_EXPO_BRIDGE_FORMAT) {
|
|
6895
|
+
throw new TypeError("Expo bridge message format is unsupported.");
|
|
6896
|
+
}
|
|
6897
|
+
if (parsed.kind === "request")
|
|
6898
|
+
return parseRequest(parsed);
|
|
6899
|
+
if (parsed.kind === "response")
|
|
6900
|
+
return parseResponse(parsed);
|
|
6901
|
+
if (parsed.kind === "event")
|
|
6902
|
+
return parseEvent(parsed);
|
|
6903
|
+
throw new TypeError("Expo bridge message kind is unsupported.");
|
|
6904
|
+
};
|
|
6177
6905
|
// src/mobile/ciWorkflow.ts
|
|
6178
6906
|
import { existsSync as existsSync3 } from "fs";
|
|
6179
|
-
import { access as
|
|
6180
|
-
import { dirname as
|
|
6907
|
+
import { access as access10, mkdir as mkdir12, readFile as readFile16, writeFile as writeFile13 } from "fs/promises";
|
|
6908
|
+
import { dirname as dirname11, extname as extname4, relative as relative11, resolve as resolve14, sep as sep6 } from "path";
|
|
6181
6909
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
|
|
6182
6910
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
6183
6911
|
var CI_ENV_INDENTATION = 6;
|
|
@@ -6196,9 +6924,9 @@ var RESERVED_SECRET_NAMES = new Set([
|
|
|
6196
6924
|
"APP_STORE_CONNECT_KEY_ID",
|
|
6197
6925
|
"APP_STORE_CONNECT_PRIVATE_KEY_BASE64"
|
|
6198
6926
|
]);
|
|
6199
|
-
var
|
|
6927
|
+
var exists4 = async (path) => {
|
|
6200
6928
|
try {
|
|
6201
|
-
await
|
|
6929
|
+
await access10(path);
|
|
6202
6930
|
return true;
|
|
6203
6931
|
} catch {
|
|
6204
6932
|
return false;
|
|
@@ -6206,9 +6934,9 @@ var exists3 = async (path) => {
|
|
|
6206
6934
|
};
|
|
6207
6935
|
var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
|
|
6208
6936
|
var projectPath = (projectRoot, value, field2, options = {}) => {
|
|
6209
|
-
const root =
|
|
6210
|
-
const path =
|
|
6211
|
-
const portable =
|
|
6937
|
+
const root = resolve14(projectRoot);
|
|
6938
|
+
const path = resolve14(root, value);
|
|
6939
|
+
const portable = relative11(root, path).replaceAll("\\", "/");
|
|
6212
6940
|
if (portable === ".." || portable.startsWith(`..${sep6}`) || portable.startsWith("../") || portable === "") {
|
|
6213
6941
|
throw new TypeError(`${field2} must remain inside the project root.`);
|
|
6214
6942
|
}
|
|
@@ -6219,10 +6947,10 @@ var projectPath = (projectRoot, value, field2, options = {}) => {
|
|
|
6219
6947
|
return portable;
|
|
6220
6948
|
};
|
|
6221
6949
|
var workflowOutputPath = (projectRoot, value) => {
|
|
6222
|
-
const root =
|
|
6223
|
-
const workflows =
|
|
6224
|
-
const path =
|
|
6225
|
-
const portable =
|
|
6950
|
+
const root = resolve14(projectRoot);
|
|
6951
|
+
const workflows = resolve14(root, ".github/workflows");
|
|
6952
|
+
const path = resolve14(root, value ?? ".github/workflows/absolute-mobile.yml");
|
|
6953
|
+
const portable = relative11(workflows, path);
|
|
6226
6954
|
if (portable === ".." || portable.startsWith(`..${sep6}`) || extname4(path) !== ".yml" && extname4(path) !== ".yaml") {
|
|
6227
6955
|
throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
|
|
6228
6956
|
}
|
|
@@ -6652,12 +7380,12 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
6652
7380
|
var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
6653
7381
|
const path = workflowOutputPath(options.projectRoot, options.outputPath);
|
|
6654
7382
|
const generated = createAbsoluteMobileGithubWorkflow(options);
|
|
6655
|
-
const previous = await
|
|
7383
|
+
const previous = await exists4(path) ? await readFile16(path, "utf8") : undefined;
|
|
6656
7384
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
6657
|
-
throw new TypeError(`${
|
|
7385
|
+
throw new TypeError(`${relative11(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
6658
7386
|
if (previous !== generated.workflow) {
|
|
6659
|
-
await
|
|
6660
|
-
await
|
|
7387
|
+
await mkdir12(dirname11(path), { recursive: true });
|
|
7388
|
+
await writeFile13(path, generated.workflow);
|
|
6661
7389
|
}
|
|
6662
7390
|
return {
|
|
6663
7391
|
changed: previous !== generated.workflow,
|
|
@@ -6817,7 +7545,7 @@ init_telemetryEvent();
|
|
|
6817
7545
|
import { Elysia as Elysia3 } from "elysia";
|
|
6818
7546
|
var ABSOLUTE_MOBILE_PREVIEW_PATH = "/__absolute/mobile-preview";
|
|
6819
7547
|
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
6820
|
-
var
|
|
7548
|
+
var isRecord9 = (value) => typeof value === "object" && value !== null;
|
|
6821
7549
|
var normalizeEntry2 = (entry) => {
|
|
6822
7550
|
const parsed = new URL(entry ?? "/", "https://absolute.invalid");
|
|
6823
7551
|
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
@@ -6868,7 +7596,7 @@ var createAbsoluteMobilePreviewPlugin = (mobile) => {
|
|
|
6868
7596
|
"X-Robots-Tag": "noindex, nofollow"
|
|
6869
7597
|
}
|
|
6870
7598
|
})).post("/__absolute/mobile-preview-telemetry", ({ body, status }) => {
|
|
6871
|
-
const value =
|
|
7599
|
+
const value = isRecord9(body) ? body : undefined;
|
|
6872
7600
|
const durationMs = value?.durationMs;
|
|
6873
7601
|
const platform2 = value?.platform;
|
|
6874
7602
|
if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || durationMs > 300000 || platform2 !== "android" && platform2 !== "ios") {
|
|
@@ -6884,20 +7612,20 @@ var createAbsoluteMobilePreviewPlugin = (mobile) => {
|
|
|
6884
7612
|
});
|
|
6885
7613
|
};
|
|
6886
7614
|
// src/mobile/nativeDeepLinks.ts
|
|
6887
|
-
import { readFile as
|
|
6888
|
-
import { join as
|
|
7615
|
+
import { readFile as readFile17, rename as rename12, writeFile as writeFile14 } from "fs/promises";
|
|
7616
|
+
import { join as join18 } from "path";
|
|
6889
7617
|
var START_MARKER = "<!-- absolutejs:deep-links:start -->";
|
|
6890
7618
|
var END_MARKER = "<!-- absolutejs:deep-links:end -->";
|
|
6891
7619
|
var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
|
|
6892
7620
|
var NOT_FOUND = -1;
|
|
6893
7621
|
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
6894
7622
|
var writeChangedFile = async (path, source) => {
|
|
6895
|
-
const current = await
|
|
7623
|
+
const current = await readFile17(path, "utf8");
|
|
6896
7624
|
if (current === source)
|
|
6897
7625
|
return false;
|
|
6898
7626
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
6899
|
-
await
|
|
6900
|
-
await
|
|
7627
|
+
await writeFile14(temporary, source, { flag: "wx" });
|
|
7628
|
+
await rename12(temporary, path);
|
|
6901
7629
|
return true;
|
|
6902
7630
|
};
|
|
6903
7631
|
var replaceManagedRegion = (source, region, insertAt) => {
|
|
@@ -6942,8 +7670,8 @@ ${hosts}
|
|
|
6942
7670
|
`;
|
|
6943
7671
|
};
|
|
6944
7672
|
var configureAndroid = async (config) => {
|
|
6945
|
-
const path =
|
|
6946
|
-
const source = await
|
|
7673
|
+
const path = join18(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
7674
|
+
const source = await readFile17(path, "utf8");
|
|
6947
7675
|
const mainActivity = source.indexOf('android:name=".MainActivity"');
|
|
6948
7676
|
if (mainActivity === NOT_FOUND) {
|
|
6949
7677
|
throw new TypeError("Android MainActivity was not found.");
|
|
@@ -6968,8 +7696,8 @@ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
|
|
|
6968
7696
|
${END_MARKER}
|
|
6969
7697
|
`;
|
|
6970
7698
|
var configureIosInfo = async (config) => {
|
|
6971
|
-
const path =
|
|
6972
|
-
const source = await
|
|
7699
|
+
const path = join18(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
7700
|
+
const source = await readFile17(path, "utf8");
|
|
6973
7701
|
const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
|
|
6974
7702
|
${END_MARKER}
|
|
6975
7703
|
`;
|
|
@@ -6992,10 +7720,10 @@ ${domains}
|
|
|
6992
7720
|
`;
|
|
6993
7721
|
};
|
|
6994
7722
|
var configureIosEntitlements = async (config) => {
|
|
6995
|
-
const path =
|
|
7723
|
+
const path = join18(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
6996
7724
|
let current = "";
|
|
6997
7725
|
try {
|
|
6998
|
-
current = await
|
|
7726
|
+
current = await readFile17(path, "utf8");
|
|
6999
7727
|
} catch (error) {
|
|
7000
7728
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
7001
7729
|
throw error;
|
|
@@ -7005,13 +7733,13 @@ var configureIosEntitlements = async (config) => {
|
|
|
7005
7733
|
if (current === source)
|
|
7006
7734
|
return false;
|
|
7007
7735
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
7008
|
-
await
|
|
7009
|
-
await
|
|
7736
|
+
await writeFile14(temporary, source, { flag: "wx" });
|
|
7737
|
+
await rename12(temporary, path);
|
|
7010
7738
|
return true;
|
|
7011
7739
|
};
|
|
7012
7740
|
var configureIosProject = async (config) => {
|
|
7013
|
-
const path =
|
|
7014
|
-
const source = await
|
|
7741
|
+
const path = join18(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
7742
|
+
const source = await readFile17(path, "utf8");
|
|
7015
7743
|
const declarations = [
|
|
7016
7744
|
...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
|
|
7017
7745
|
].map((match) => match[1]);
|
|
@@ -7050,8 +7778,8 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
|
|
|
7050
7778
|
};
|
|
7051
7779
|
// src/mobile/nativeDeviceCapabilities.ts
|
|
7052
7780
|
init_deviceCapabilities();
|
|
7053
|
-
import { readFile as
|
|
7054
|
-
import { join as
|
|
7781
|
+
import { readFile as readFile18, rename as rename13, writeFile as writeFile15 } from "fs/promises";
|
|
7782
|
+
import { join as join19 } from "path";
|
|
7055
7783
|
var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->";
|
|
7056
7784
|
var END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->";
|
|
7057
7785
|
var NOT_FOUND2 = -1;
|
|
@@ -7061,12 +7789,12 @@ var PUSH_START_MARKER = "absolutejs:push-notifications:start";
|
|
|
7061
7789
|
var PUSH_END_MARKER = "absolutejs:push-notifications:end";
|
|
7062
7790
|
var escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
7063
7791
|
var writeChangedFile2 = async (path, source) => {
|
|
7064
|
-
const current = await
|
|
7792
|
+
const current = await readFile18(path, "utf8");
|
|
7065
7793
|
if (current === source)
|
|
7066
7794
|
return false;
|
|
7067
7795
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
7068
|
-
await
|
|
7069
|
-
await
|
|
7796
|
+
await writeFile15(temporary, source, { flag: "wx" });
|
|
7797
|
+
await rename13(temporary, path);
|
|
7070
7798
|
return true;
|
|
7071
7799
|
};
|
|
7072
7800
|
var writeOptionalChangedFile = async (path, source) => {
|
|
@@ -7074,14 +7802,14 @@ var writeOptionalChangedFile = async (path, source) => {
|
|
|
7074
7802
|
if (current === source)
|
|
7075
7803
|
return false;
|
|
7076
7804
|
if (current === null) {
|
|
7077
|
-
await
|
|
7805
|
+
await writeFile15(path, source, { flag: "wx" });
|
|
7078
7806
|
return true;
|
|
7079
7807
|
}
|
|
7080
7808
|
return writeChangedFile2(path, source);
|
|
7081
7809
|
};
|
|
7082
7810
|
var optionalSource = async (path) => {
|
|
7083
7811
|
try {
|
|
7084
|
-
return await
|
|
7812
|
+
return await readFile18(path, "utf8");
|
|
7085
7813
|
} catch (error) {
|
|
7086
7814
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
7087
7815
|
return null;
|
|
@@ -7195,14 +7923,14 @@ var writeIosPrivacyManifest = async (path, current, source) => {
|
|
|
7195
7923
|
return false;
|
|
7196
7924
|
if (current !== null)
|
|
7197
7925
|
return writeChangedFile2(path, source);
|
|
7198
|
-
await
|
|
7926
|
+
await writeFile15(path, source, { flag: "wx" });
|
|
7199
7927
|
return true;
|
|
7200
7928
|
};
|
|
7201
7929
|
var configureIosPrivacyProject = async (config, requirements) => {
|
|
7202
7930
|
if (requirements.iosPrivacyAccessedApis.length === 0)
|
|
7203
7931
|
return false;
|
|
7204
|
-
const projectPath2 =
|
|
7205
|
-
const project = await
|
|
7932
|
+
const projectPath2 = join19(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
7933
|
+
const project = await readFile18(projectPath2, "utf8");
|
|
7206
7934
|
return writeChangedFile2(projectPath2, addIosPrivacyProjectReference(project));
|
|
7207
7935
|
};
|
|
7208
7936
|
var addIosPrivacyProjectReference = (source) => {
|
|
@@ -7254,8 +7982,8 @@ ${next.slice(index)}`;
|
|
|
7254
7982
|
return next;
|
|
7255
7983
|
};
|
|
7256
7984
|
var configureIos2 = async (config, plan) => {
|
|
7257
|
-
const path =
|
|
7258
|
-
const source = await
|
|
7985
|
+
const path = join19(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
7986
|
+
const source = await readFile18(path, "utf8");
|
|
7259
7987
|
const requirements = absoluteDeviceNativeRequirements(plan);
|
|
7260
7988
|
const existingSystemBars = source.match(/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<(true|false)\s*\/>/u);
|
|
7261
7989
|
const ownedStart = source.indexOf(START_MARKER2);
|
|
@@ -7275,7 +8003,7 @@ ${content}
|
|
|
7275
8003
|
${END_MARKER2}
|
|
7276
8004
|
` : "";
|
|
7277
8005
|
const infoChanged = await writeChangedFile2(path, managed(source, region, source.lastIndexOf("</dict>")));
|
|
7278
|
-
const privacyPath =
|
|
8006
|
+
const privacyPath = join19(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
|
|
7279
8007
|
const privacyCurrent = await optionalSource(privacyPath);
|
|
7280
8008
|
const privacySource = privacyManifestSource(privacyCurrent, requirements);
|
|
7281
8009
|
const [privacyChanged, projectChanged, pushChanged] = await Promise.all([
|
|
@@ -7286,7 +8014,7 @@ ${content}
|
|
|
7286
8014
|
return infoChanged || privacyChanged || projectChanged || pushChanged;
|
|
7287
8015
|
};
|
|
7288
8016
|
var configureIosPushNotifications = async (config, enabled) => {
|
|
7289
|
-
const entitlementsPath =
|
|
8017
|
+
const entitlementsPath = join19(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
7290
8018
|
const entitlements = await optionalSource(entitlementsPath);
|
|
7291
8019
|
if (entitlements === null && !enabled)
|
|
7292
8020
|
return false;
|
|
@@ -7298,7 +8026,7 @@ var configureIosPushNotifications = async (config, enabled) => {
|
|
|
7298
8026
|
<!-- ${PUSH_END_MARKER} -->
|
|
7299
8027
|
` : "";
|
|
7300
8028
|
const nextEntitlements = replacePushRegion(entitlements, entitlementRegion, entitlements.lastIndexOf("</dict>"));
|
|
7301
|
-
const delegatePath =
|
|
8029
|
+
const delegatePath = join19(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
7302
8030
|
const delegate = await optionalSource(delegatePath);
|
|
7303
8031
|
if (delegate === null && !enabled)
|
|
7304
8032
|
return false;
|
|
@@ -7347,8 +8075,8 @@ var replacePushRegion = (source, region, insertion) => {
|
|
|
7347
8075
|
return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
|
|
7348
8076
|
};
|
|
7349
8077
|
var configureAndroid2 = async (config, plan) => {
|
|
7350
|
-
const path =
|
|
7351
|
-
const source = await
|
|
8078
|
+
const path = join19(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
8079
|
+
const source = await readFile18(path, "utf8");
|
|
7352
8080
|
const permissions = absoluteDeviceNativeRequirements(plan).androidPermissions;
|
|
7353
8081
|
const content = permissions.map((permission) => ` <uses-permission android:name="${escapeXml2(permission)}" />`).join(`
|
|
7354
8082
|
`);
|
|
@@ -7383,7 +8111,7 @@ ${content}
|
|
|
7383
8111
|
throw new TypeError(`Android google-services.json does not contain package ${config.appId}.`);
|
|
7384
8112
|
const [manifestChanged, firebaseChanged] = await Promise.all([
|
|
7385
8113
|
writeChangedFile2(path, nextManifest),
|
|
7386
|
-
writeOptionalChangedFile(
|
|
8114
|
+
writeOptionalChangedFile(join19(config.nativeProjectDirectory, "android/app/google-services.json"), firebaseSource)
|
|
7387
8115
|
]);
|
|
7388
8116
|
return manifestChanged || firebaseChanged;
|
|
7389
8117
|
};
|
|
@@ -7397,8 +8125,8 @@ var applyAbsoluteNativeDeviceCapabilities = async (projectRoot, config, platform
|
|
|
7397
8125
|
};
|
|
7398
8126
|
};
|
|
7399
8127
|
// src/mobile/releasePublisher.ts
|
|
7400
|
-
import { access as
|
|
7401
|
-
import { isAbsolute as isAbsolute6, relative as
|
|
8128
|
+
import { access as access11 } from "fs/promises";
|
|
8129
|
+
import { isAbsolute as isAbsolute6, relative as relative12, resolve as resolve15, sep as sep7 } from "path";
|
|
7402
8130
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
7403
8131
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
7404
8132
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -7421,12 +8149,12 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
|
7421
8149
|
}
|
|
7422
8150
|
return versionCode;
|
|
7423
8151
|
};
|
|
7424
|
-
var
|
|
7425
|
-
var isPublisher = (value) =>
|
|
8152
|
+
var isRecord10 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8153
|
+
var isPublisher = (value) => isRecord10(value) && typeof value.publish === "function";
|
|
7426
8154
|
var publisherModulePath = (projectRoot, requested) => {
|
|
7427
|
-
const root =
|
|
7428
|
-
const path =
|
|
7429
|
-
const projectRelative =
|
|
8155
|
+
const root = resolve15(projectRoot);
|
|
8156
|
+
const path = resolve15(root, requested);
|
|
8157
|
+
const projectRelative = relative12(root, path);
|
|
7430
8158
|
if (projectRelative === ".." || projectRelative.startsWith(`..${sep7}`) || isAbsolute6(projectRelative)) {
|
|
7431
8159
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
7432
8160
|
}
|
|
@@ -7434,11 +8162,11 @@ var publisherModulePath = (projectRoot, requested) => {
|
|
|
7434
8162
|
};
|
|
7435
8163
|
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
7436
8164
|
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
7437
|
-
await
|
|
8165
|
+
await access11(modulePath).catch(() => {
|
|
7438
8166
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
7439
8167
|
});
|
|
7440
8168
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
7441
|
-
const publisher =
|
|
8169
|
+
const publisher = isRecord10(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
7442
8170
|
if (!isPublisher(publisher)) {
|
|
7443
8171
|
throw new TypeError("Native release registry module must default-export a registry with publish(options).");
|
|
7444
8172
|
}
|
|
@@ -7496,7 +8224,7 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
7496
8224
|
};
|
|
7497
8225
|
// src/mobile/routeMetadataTransform.ts
|
|
7498
8226
|
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
7499
|
-
import { dirname as
|
|
8227
|
+
import { dirname as dirname13, extname as extname5, relative as relative13, resolve as resolve16 } from "path";
|
|
7500
8228
|
import ts2 from "typescript";
|
|
7501
8229
|
var ROUTE_METHODS = new Set(["get", "head"]);
|
|
7502
8230
|
var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
|
|
@@ -7547,7 +8275,7 @@ var PAGE_HANDLERS = new Map([
|
|
|
7547
8275
|
]
|
|
7548
8276
|
]);
|
|
7549
8277
|
var posixPath = (value) => value.replace(/\\/g, "/");
|
|
7550
|
-
var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(
|
|
8278
|
+
var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(dirname13(entry), existsSync6, "tsconfig.json") ?? ts2.findConfigFile(projectRoot, existsSync6, "tsconfig.json");
|
|
7551
8279
|
var createProgram = (entry, projectRoot) => {
|
|
7552
8280
|
const configPath2 = findTsconfig(entry, projectRoot);
|
|
7553
8281
|
if (!configPath2) {
|
|
@@ -7559,7 +8287,7 @@ var createProgram = (entry, projectRoot) => {
|
|
|
7559
8287
|
target: ts2.ScriptTarget.ESNext
|
|
7560
8288
|
});
|
|
7561
8289
|
}
|
|
7562
|
-
const parsed = ts2.parseJsonConfigFileContent(ts2.readConfigFile(configPath2, (path) => readFileSync7(path, "utf8")).config, ts2.sys,
|
|
8290
|
+
const parsed = ts2.parseJsonConfigFileContent(ts2.readConfigFile(configPath2, (path) => readFileSync7(path, "utf8")).config, ts2.sys, dirname13(configPath2));
|
|
7563
8291
|
if (!parsed.fileNames.includes(entry))
|
|
7564
8292
|
parsed.fileNames.push(entry);
|
|
7565
8293
|
return ts2.createProgram(parsed.fileNames, parsed.options);
|
|
@@ -7665,7 +8393,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
|
|
|
7665
8393
|
const declaration = symbol?.declarations?.[0];
|
|
7666
8394
|
const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
|
|
7667
8395
|
const exportedName = symbol?.name ?? expression.getText(sourceFile);
|
|
7668
|
-
const source = posixPath(
|
|
8396
|
+
const source = posixPath(relative13(projectRoot, file));
|
|
7669
8397
|
return `${source}#${exportedName}`;
|
|
7670
8398
|
};
|
|
7671
8399
|
var resolveAlias = (symbol, checker) => {
|
|
@@ -7904,7 +8632,7 @@ var analyzeProgram = (program, projectRoot) => {
|
|
|
7904
8632
|
const checker = program.getTypeChecker();
|
|
7905
8633
|
const analyzed = new Map;
|
|
7906
8634
|
for (const sourceFile of program.getSourceFiles()) {
|
|
7907
|
-
const resolvedFile =
|
|
8635
|
+
const resolvedFile = resolve16(sourceFile.fileName);
|
|
7908
8636
|
if (!isProjectSource(sourceFile, resolvedFile, projectRoot))
|
|
7909
8637
|
continue;
|
|
7910
8638
|
const analysis = analyzeSourceFile(sourceFile, checker, projectRoot);
|
|
@@ -7997,14 +8725,14 @@ var transformFile = (source, fileName, analysis) => {
|
|
|
7997
8725
|
};
|
|
7998
8726
|
var ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL = ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION;
|
|
7999
8727
|
var createAbsoluteMobileRouteMetadataPlugin = (options) => {
|
|
8000
|
-
const projectRoot =
|
|
8001
|
-
const entry =
|
|
8728
|
+
const projectRoot = resolve16(options.projectRoot ?? process.cwd());
|
|
8729
|
+
const entry = resolve16(options.entry);
|
|
8002
8730
|
const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
|
|
8003
8731
|
return {
|
|
8004
8732
|
name: "absolute-mobile-route-metadata",
|
|
8005
8733
|
setup(build) {
|
|
8006
8734
|
build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
|
|
8007
|
-
const analysis = analyzed.get(
|
|
8735
|
+
const analysis = analyzed.get(resolve16(path));
|
|
8008
8736
|
if (!analysis)
|
|
8009
8737
|
return;
|
|
8010
8738
|
const source = await Bun.file(path).text();
|
|
@@ -8017,11 +8745,11 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
|
|
|
8017
8745
|
};
|
|
8018
8746
|
};
|
|
8019
8747
|
var inspectAbsoluteMobileRouteMetadata = (options) => {
|
|
8020
|
-
const projectRoot =
|
|
8021
|
-
const entry =
|
|
8748
|
+
const projectRoot = resolve16(options.projectRoot ?? process.cwd());
|
|
8749
|
+
const entry = resolve16(options.entry);
|
|
8022
8750
|
const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
|
|
8023
8751
|
return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
|
|
8024
|
-
file: posixPath(
|
|
8752
|
+
file: posixPath(relative13(projectRoot, file)),
|
|
8025
8753
|
metadata
|
|
8026
8754
|
})));
|
|
8027
8755
|
};
|
|
@@ -8230,6 +8958,7 @@ var http = createAbsoluteHttpClient();
|
|
|
8230
8958
|
var installAbsoluteMobileShellHttp = (origin, fetch2 = globalThis.fetch) => installAbsoluteHttpTransport(createAbsoluteHttpTransport({ fetch: fetch2, origin, runtime: "native" }));
|
|
8231
8959
|
export {
|
|
8232
8960
|
writeAbsoluteMobileGithubWorkflow,
|
|
8961
|
+
writeAbsoluteExpoProject,
|
|
8233
8962
|
writeAbsoluteCapacitorConfig,
|
|
8234
8963
|
waitForAbsoluteIosHmrLog,
|
|
8235
8964
|
verifyAbsoluteMobileCompatibilityProducer,
|
|
@@ -8238,6 +8967,7 @@ export {
|
|
|
8238
8967
|
validateAbsoluteRemoteMacProfileName,
|
|
8239
8968
|
testAbsoluteIosPhysicalDevice,
|
|
8240
8969
|
syncAbsoluteRemoteMacProject,
|
|
8970
|
+
syncAbsoluteExpoWebAssets,
|
|
8241
8971
|
startAbsoluteRemoteIosDevSession,
|
|
8242
8972
|
startAbsoluteIosTcpRelay,
|
|
8243
8973
|
startAbsoluteIosDevSession,
|
|
@@ -8273,6 +9003,7 @@ export {
|
|
|
8273
9003
|
parseAbsoluteMobileBuildPageMetadata,
|
|
8274
9004
|
parseAbsoluteIosLogLine,
|
|
8275
9005
|
parseAbsoluteIosHmrLog,
|
|
9006
|
+
parseAbsoluteExpoBridgeMessage,
|
|
8276
9007
|
parseAbsoluteAndroidInstalledApp,
|
|
8277
9008
|
pairAbsoluteRemoteMac,
|
|
8278
9009
|
normalizeAbsoluteMobileConfig,
|
|
@@ -8327,6 +9058,8 @@ export {
|
|
|
8327
9058
|
createAbsoluteMobileAssociationPlugin,
|
|
8328
9059
|
createAbsoluteMobileAssociationDocuments,
|
|
8329
9060
|
createAbsoluteIosNativeWatcher,
|
|
9061
|
+
createAbsoluteExpoBridgeResponse,
|
|
9062
|
+
createAbsoluteExpoBridgeError,
|
|
8330
9063
|
carryForwardAbsoluteMobileCompatibilityReleases,
|
|
8331
9064
|
captureAbsoluteRemoteMacCommand,
|
|
8332
9065
|
captureAbsoluteMobileRouteGraph,
|
|
@@ -8364,9 +9097,12 @@ export {
|
|
|
8364
9097
|
ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT,
|
|
8365
9098
|
ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
8366
9099
|
ABSOLUTE_IOS_RELEASE_FORMAT,
|
|
9100
|
+
ABSOLUTE_EXPO_BRIDGE_METHODS,
|
|
9101
|
+
ABSOLUTE_EXPO_BRIDGE_MAX_BYTES,
|
|
9102
|
+
ABSOLUTE_EXPO_BRIDGE_FORMAT,
|
|
8367
9103
|
ABSOLUTE_AUTH_PACKAGE,
|
|
8368
9104
|
ABSOLUTE_ANDROID_RELEASE_FORMAT
|
|
8369
9105
|
};
|
|
8370
9106
|
|
|
8371
|
-
//# debugId=
|
|
9107
|
+
//# debugId=81AD994206FFE2B464756E2164756E21
|
|
8372
9108
|
//# sourceMappingURL=index.js.map
|