@absolutejs/absolute 0.20.0-beta.45 → 0.20.0-beta.47
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/build.js +141 -123
- package/dist/build.js.map +3 -3
- package/dist/cli/index.js +1069 -803
- package/dist/index.js +208 -150
- package/dist/index.js.map +5 -5
- package/dist/mobile/index.js +419 -112
- package/dist/mobile/index.js.map +8 -8
- package/dist/mobile/remoteMacAgentEntry.js +609 -85
- package/dist/mobile/shellExpoAuth.js +20 -41
- package/dist/mobile/shellExpoDevices.js +20 -41
- package/dist/src/mobile/deviceCapabilities.d.ts +4 -2
- package/dist/src/mobile/expoBridge.d.ts +2 -2
- package/dist/src/mobile/shellExpoDevices.d.ts +2 -2
- package/dist/types/build.d.ts +4 -1
- package/package.json +2 -1
package/dist/mobile/index.js
CHANGED
|
@@ -167,7 +167,7 @@ var init_startupBanner = __esm(() => {
|
|
|
167
167
|
|
|
168
168
|
// src/mobile/config.ts
|
|
169
169
|
import { resolve as resolve6 } from "path";
|
|
170
|
-
var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, HOSTNAME_PATTERN, resolveProjectPath = (projectRoot, value, field2) => {
|
|
170
|
+
var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, HOSTNAME_PATTERN, EXPO_RESERVED_ROUTE_PREFIXES, resolveProjectPath = (projectRoot, value, field2) => {
|
|
171
171
|
const root = resolve6(projectRoot);
|
|
172
172
|
const path = resolve6(root, value);
|
|
173
173
|
if (path !== root && !path.startsWith(`${root}/`)) {
|
|
@@ -240,11 +240,31 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
240
240
|
}
|
|
241
241
|
return value.match(/.{2}/g)?.join(":") ?? value;
|
|
242
242
|
}))
|
|
243
|
-
].sort(),
|
|
243
|
+
].sort(), validateExpoNativeRouteSegment = (path, segment, index, count, parameters) => {
|
|
244
|
+
if (segment === "*" && (index !== count - 1 || count === 1)) {
|
|
245
|
+
throw new TypeError(`mobile.routes.native route ${path} must use * once, as the final segment after a static or parameterized prefix.`);
|
|
246
|
+
}
|
|
247
|
+
if (segment === "*")
|
|
248
|
+
return;
|
|
249
|
+
if (!segment.startsWith(":") && (segment.includes("*") || segment.includes(":"))) {
|
|
250
|
+
throw new TypeError(`mobile.routes.native route ${path} contains invalid segment ${segment}.`);
|
|
251
|
+
}
|
|
252
|
+
if (!segment.startsWith(":"))
|
|
253
|
+
return;
|
|
254
|
+
const name = segment.slice(1);
|
|
255
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/u.test(name)) {
|
|
256
|
+
throw new TypeError(`mobile.routes.native route ${path} has invalid parameter ${segment}.`);
|
|
257
|
+
}
|
|
258
|
+
if (parameters.has(name)) {
|
|
259
|
+
throw new TypeError(`mobile.routes.native route ${path} repeats parameter ${segment}.`);
|
|
260
|
+
}
|
|
261
|
+
parameters.add(name);
|
|
262
|
+
}, normalizeExpoNativeRoutes = (config, projectRoot) => {
|
|
244
263
|
if (config.engine !== "expo")
|
|
245
264
|
return {};
|
|
246
265
|
const routes = config.routes?.native ?? {};
|
|
247
266
|
const normalized = {};
|
|
267
|
+
const ownership = new Map;
|
|
248
268
|
for (const [route, module] of Object.entries(routes)) {
|
|
249
269
|
const path = normalizeEntry(route);
|
|
250
270
|
if (path.includes("?") || path.includes("#") || path !== "/" && path.endsWith("/")) {
|
|
@@ -253,9 +273,18 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
|
|
|
253
273
|
if (path === "/__absolute/native") {
|
|
254
274
|
throw new TypeError("mobile.routes.native reserves /__absolute/native for the Expo diagnostic screen.");
|
|
255
275
|
}
|
|
256
|
-
|
|
257
|
-
|
|
276
|
+
const segments = path.split("/").filter(Boolean);
|
|
277
|
+
if (segments[0] && EXPO_RESERVED_ROUTE_PREFIXES.has(segments[0])) {
|
|
278
|
+
throw new TypeError(`mobile.routes.native route ${path} conflicts with an Expo Router or Metro reserved path.`);
|
|
279
|
+
}
|
|
280
|
+
const parameters = new Set;
|
|
281
|
+
segments.forEach((segment, index) => validateExpoNativeRouteSegment(path, segment, index, segments.length, parameters));
|
|
282
|
+
const signature = segments.map((segment) => segment.startsWith(":") ? ":" : segment).join("/");
|
|
283
|
+
const existing = ownership.get(signature);
|
|
284
|
+
if (existing) {
|
|
285
|
+
throw new TypeError(`mobile.routes.native routes ${existing} and ${path} claim the same Expo route pattern.`);
|
|
258
286
|
}
|
|
287
|
+
ownership.set(signature, path);
|
|
259
288
|
normalized[path] = resolveProjectPath(projectRoot, requireText(module, `mobile.routes.native[${path}]`), `mobile.routes.native[${path}]`);
|
|
260
289
|
}
|
|
261
290
|
return Object.fromEntries(Object.entries(normalized).sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -294,6 +323,16 @@ var init_config = __esm(() => {
|
|
|
294
323
|
APPLE_APP_ID_PREFIX_PATTERN = /^[A-Z0-9]{10}$/;
|
|
295
324
|
CERTIFICATE_FINGERPRINT_PATTERN = /^[0-9A-F]{64}$/;
|
|
296
325
|
HOSTNAME_PATTERN = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$/;
|
|
326
|
+
EXPO_RESERVED_ROUTE_PREFIXES = new Set([
|
|
327
|
+
"_expo",
|
|
328
|
+
"_flight",
|
|
329
|
+
"_sitemap",
|
|
330
|
+
"assets",
|
|
331
|
+
"expo-dev-plugins",
|
|
332
|
+
"inspector",
|
|
333
|
+
"manifest",
|
|
334
|
+
"public"
|
|
335
|
+
]);
|
|
297
336
|
});
|
|
298
337
|
|
|
299
338
|
// src/utils/stringModifiers.ts
|
|
@@ -772,10 +811,11 @@ var init_syncSchema = __esm(() => {
|
|
|
772
811
|
});
|
|
773
812
|
|
|
774
813
|
// src/mobile/deviceCapabilities.ts
|
|
775
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
814
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
776
815
|
import { extname as extname3, join as join13, relative as relative9, resolve as resolve11 } from "path";
|
|
816
|
+
import { fileURLToPath } from "url";
|
|
777
817
|
import ts from "typescript";
|
|
778
|
-
var DEVICES_PACKAGE = "@absolutejs/devices",
|
|
818
|
+
var DEVICES_PACKAGE = "@absolutejs/devices", ADAPTERS, SOURCE_GLOB, IGNORED_DIRECTORIES, IDENTIFIER_PATTERN, providerModulePattern = (provider) => new RegExp(`^@absolutejs/devices-${provider}/[a-z][a-z0-9-]*$`, "u"), providerPackagePattern = (provider) => provider === "capacitor" ? /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u : /^(?:expo-[a-z][a-z0-9-]*|@react-native-[a-z0-9-]+\/[a-z][a-z0-9-]*)@\d+\.\d+\.\d+$/u, providerLabel = (provider) => provider === "capacitor" ? "Capacitor" : "Expo", ANDROID_PERMISSION_PATTERN, IOS_USAGE_DESCRIPTIONS, IOS_PRIVACY_ACCESSED_API_REASONS, IOS_PRIVACY_ACCESSED_APIS, object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
|
|
779
819
|
const value = JSON.parse(readFileSync4(path, "utf8"));
|
|
780
820
|
if (!object2(value))
|
|
781
821
|
throw new TypeError(`${path} must contain an object.`);
|
|
@@ -835,7 +875,7 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
835
875
|
...systemBars === true ? { systemBars: true } : {},
|
|
836
876
|
...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
|
|
837
877
|
};
|
|
838
|
-
}, parseProvider = (name, value) => {
|
|
878
|
+
}, parseProvider = (name, value, providerName) => {
|
|
839
879
|
if (!IDENTIFIER_PATTERN.test(name))
|
|
840
880
|
throw new TypeError("Device capability names must be identifiers.");
|
|
841
881
|
if (!object2(value))
|
|
@@ -844,10 +884,13 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
844
884
|
const module = text(value.module, `${name}.module`);
|
|
845
885
|
if (!IDENTIFIER_PATTERN.test(factory))
|
|
846
886
|
throw new TypeError(`${name}.factory must be a JavaScript identifier.`);
|
|
847
|
-
if (!
|
|
848
|
-
throw new TypeError(`${name}.module must be an official devices
|
|
849
|
-
if (!Array.isArray(value.packages) || !value.packages.every((spec) => typeof spec === "string" &&
|
|
850
|
-
throw new TypeError(`${name}.packages must contain exact official
|
|
887
|
+
if (!providerModulePattern(providerName).test(module))
|
|
888
|
+
throw new TypeError(`${name}.module must be an official devices-${providerName} subpath.`);
|
|
889
|
+
if (!Array.isArray(value.packages) || !value.packages.every((spec) => typeof spec === "string" && providerPackagePattern(providerName).test(spec)))
|
|
890
|
+
throw new TypeError(`${name}.packages must contain exact official ${providerLabel(providerName)} package versions.`);
|
|
891
|
+
const { plugins } = value;
|
|
892
|
+
if (plugins !== undefined && (!Array.isArray(plugins) || !plugins.every((plugin) => typeof plugin === "string" && /^expo-[a-z][a-z0-9-]*$/u.test(plugin))))
|
|
893
|
+
throw new TypeError(`${name}.plugins must contain Expo config plugin names.`);
|
|
851
894
|
let native;
|
|
852
895
|
const { native: nativeMetadata } = value;
|
|
853
896
|
if (nativeMetadata !== undefined) {
|
|
@@ -865,6 +908,7 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
865
908
|
factory,
|
|
866
909
|
module,
|
|
867
910
|
...native === undefined ? {} : { native },
|
|
911
|
+
...plugins === undefined ? {} : { plugins: [...plugins] },
|
|
868
912
|
packages: [...value.packages]
|
|
869
913
|
};
|
|
870
914
|
}, absoluteDeviceNativeRequirements = (plan) => {
|
|
@@ -894,18 +938,27 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
894
938
|
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
|
|
895
939
|
].sort()
|
|
896
940
|
};
|
|
897
|
-
}, loadAbsoluteDeviceCapabilityProviders = (projectRoot) => {
|
|
898
|
-
const
|
|
941
|
+
}, loadAbsoluteDeviceCapabilityProviders = (projectRoot, provider = "capacitor") => {
|
|
942
|
+
const adapter = ADAPTERS[provider];
|
|
943
|
+
let path = join13(resolve11(projectRoot), "node_modules", adapter, "package.json");
|
|
944
|
+
try {
|
|
945
|
+
readFileSync4(path, "utf8");
|
|
946
|
+
} catch {
|
|
947
|
+
path = fileURLToPath(import.meta.resolve(`${adapter}/package.json`));
|
|
948
|
+
}
|
|
899
949
|
const manifest = readJson(path);
|
|
900
950
|
const { absolutejs } = manifest;
|
|
901
951
|
const devices = object2(absolutejs) ? absolutejs.devices : undefined;
|
|
902
|
-
if (!object2(devices) || devices.format !== 1 || devices.provider !==
|
|
903
|
-
throw new TypeError(`${
|
|
904
|
-
const entries = Object.entries(devices.capabilities).map(([name,
|
|
952
|
+
if (!object2(devices) || devices.format !== 1 || devices.provider !== provider || !object2(devices.capabilities))
|
|
953
|
+
throw new TypeError(`${adapter} does not publish supported capability metadata.`);
|
|
954
|
+
const entries = Object.entries(devices.capabilities).map(([name, capability]) => ({
|
|
905
955
|
name,
|
|
906
|
-
provider: parseProvider(name, provider)
|
|
956
|
+
provider: parseProvider(name, capability, provider)
|
|
907
957
|
}));
|
|
908
|
-
return Object.fromEntries(entries.sort((left, right) => left.name.localeCompare(right.name)).map(({ name, provider }) => [
|
|
958
|
+
return Object.fromEntries(entries.sort((left, right) => left.name.localeCompare(right.name)).map(({ name, provider: capabilityProvider }) => [
|
|
959
|
+
name,
|
|
960
|
+
capabilityProvider
|
|
961
|
+
]));
|
|
909
962
|
}, isIgnored = (path) => path.split("/").some((segment) => IGNORED_DIRECTORIES.has(segment)), importedCapabilities = (source, file) => {
|
|
910
963
|
const names = new Set;
|
|
911
964
|
const namespaces = new Set;
|
|
@@ -962,6 +1015,8 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
962
1015
|
return packages;
|
|
963
1016
|
}, discoverAbsoluteDeviceCapabilities = (projectRoot, providers = loadAbsoluteDeviceCapabilityProviders(projectRoot)) => {
|
|
964
1017
|
const root = resolve11(projectRoot);
|
|
1018
|
+
if (!existsSync3(root))
|
|
1019
|
+
return [];
|
|
965
1020
|
const known = new Set(Object.keys(providers));
|
|
966
1021
|
const capabilities = new Set;
|
|
967
1022
|
for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
|
|
@@ -988,14 +1043,14 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
988
1043
|
return true;
|
|
989
1044
|
}
|
|
990
1045
|
return false;
|
|
991
|
-
}, resolveAbsoluteDeviceCapabilityPlan = (projectRoot) => {
|
|
992
|
-
const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot);
|
|
1046
|
+
}, resolveAbsoluteDeviceCapabilityPlan = (projectRoot, provider = "capacitor") => {
|
|
1047
|
+
const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot, provider);
|
|
993
1048
|
const capabilities = discoverAbsoluteDeviceCapabilities(projectRoot, allProviders);
|
|
994
1049
|
const providers = {};
|
|
995
1050
|
for (const name of capabilities) {
|
|
996
|
-
const
|
|
997
|
-
if (
|
|
998
|
-
providers[name] =
|
|
1051
|
+
const capabilityProvider = allProviders[name];
|
|
1052
|
+
if (capabilityProvider)
|
|
1053
|
+
providers[name] = capabilityProvider;
|
|
999
1054
|
}
|
|
1000
1055
|
return {
|
|
1001
1056
|
capabilities,
|
|
@@ -1006,6 +1061,10 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
1006
1061
|
};
|
|
1007
1062
|
};
|
|
1008
1063
|
var init_deviceCapabilities = __esm(() => {
|
|
1064
|
+
ADAPTERS = {
|
|
1065
|
+
capacitor: "@absolutejs/devices-capacitor",
|
|
1066
|
+
expo: "@absolutejs/devices-expo"
|
|
1067
|
+
};
|
|
1009
1068
|
SOURCE_GLOB = new Bun.Glob("**/*.{js,jsx,ts,tsx,svelte,vue}");
|
|
1010
1069
|
IGNORED_DIRECTORIES = new Set([
|
|
1011
1070
|
".absolutejs",
|
|
@@ -1019,8 +1078,6 @@ var init_deviceCapabilities = __esm(() => {
|
|
|
1019
1078
|
"tests"
|
|
1020
1079
|
]);
|
|
1021
1080
|
IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/u;
|
|
1022
|
-
CAPACITOR_MODULE_PATTERN = /^@absolutejs\/devices-capacitor\/[a-z][a-z0-9-]*$/u;
|
|
1023
|
-
CAPACITOR_PACKAGE_PATTERN = /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u;
|
|
1024
1081
|
ANDROID_PERMISSION_PATTERN = /^android\.permission\.[A-Z][A-Z0-9_]*$/u;
|
|
1025
1082
|
IOS_USAGE_DESCRIPTIONS = new Set([
|
|
1026
1083
|
"camera",
|
|
@@ -1038,12 +1095,12 @@ var init_deviceCapabilities = __esm(() => {
|
|
|
1038
1095
|
});
|
|
1039
1096
|
|
|
1040
1097
|
// src/cli/scripts/telemetry.ts
|
|
1041
|
-
import { existsSync as
|
|
1098
|
+
import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
1042
1099
|
import { homedir as homedir3 } from "os";
|
|
1043
1100
|
import { join as join17 } from "path";
|
|
1044
1101
|
var configDir, configPath, getTelemetryConfig = () => {
|
|
1045
1102
|
try {
|
|
1046
|
-
if (!
|
|
1103
|
+
if (!existsSync5(configPath))
|
|
1047
1104
|
return null;
|
|
1048
1105
|
const raw = readFileSync5(configPath, "utf-8");
|
|
1049
1106
|
const config = JSON.parse(raw);
|
|
@@ -1058,11 +1115,11 @@ var init_telemetry = __esm(() => {
|
|
|
1058
1115
|
});
|
|
1059
1116
|
|
|
1060
1117
|
// src/cli/telemetryEvent.ts
|
|
1061
|
-
import { existsSync as
|
|
1118
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
1062
1119
|
import { arch, platform } from "os";
|
|
1063
1120
|
import { dirname as dirname12, join as join18, parse } from "path";
|
|
1064
1121
|
var checkCandidate = (candidate) => {
|
|
1065
|
-
if (!
|
|
1122
|
+
if (!existsSync6(candidate)) {
|
|
1066
1123
|
return null;
|
|
1067
1124
|
}
|
|
1068
1125
|
const pkg = JSON.parse(readFileSync6(candidate, "utf-8"));
|
|
@@ -5723,7 +5780,7 @@ const absoluteMobilePushCapability = absoluteDeviceCapability${pushIndex}(absolu
|
|
|
5723
5780
|
const entryPath = join9(staging, ".absolute-mobile-entry.ts");
|
|
5724
5781
|
const baseAdapterModule = capacitor ? await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor") : shellExpoDevicesModule();
|
|
5725
5782
|
const adapterImport = capacitor ? `import { installCapacitorDeviceAdapterIfNative } from ${JSON.stringify(baseAdapterModule)};` : `import { createAbsoluteExpoBridgeFetch, installAbsoluteExpoWebDeviceAdapter } from ${JSON.stringify(baseAdapterModule)};`;
|
|
5726
|
-
const adapterInstall = capacitor ? `installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });` :
|
|
5783
|
+
const adapterInstall = capacitor ? `installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });` : `installAbsoluteExpoWebDeviceAdapter(${JSON.stringify(deviceCapabilities.capabilities)});`;
|
|
5727
5784
|
let shellOptions = auth ? options : "{ createFetch: createAbsoluteExpoBridgeFetch }";
|
|
5728
5785
|
if (capacitor)
|
|
5729
5786
|
shellOptions = options;
|
|
@@ -6096,6 +6153,7 @@ init_deviceCapabilities();
|
|
|
6096
6153
|
// src/mobile/expoProject.ts
|
|
6097
6154
|
init_nativeAuth();
|
|
6098
6155
|
init_syncSchema();
|
|
6156
|
+
init_deviceCapabilities();
|
|
6099
6157
|
import {
|
|
6100
6158
|
access as access9,
|
|
6101
6159
|
cp as cp2,
|
|
@@ -6108,7 +6166,7 @@ import {
|
|
|
6108
6166
|
writeFile as writeFile12
|
|
6109
6167
|
} from "fs/promises";
|
|
6110
6168
|
import { createHash as createHash10 } from "crypto";
|
|
6111
|
-
import { basename as basename4, dirname as dirname10, join as join14, relative as relative10, resolve as resolve12 } from "path";
|
|
6169
|
+
import { basename as basename4, dirname as dirname10, join as join14, relative as relative10, resolve as resolve12, sep as sep6 } from "path";
|
|
6112
6170
|
var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
|
|
6113
6171
|
`;
|
|
6114
6172
|
var EXPO_ASSET_EXTENSION = ".absasset";
|
|
@@ -6125,17 +6183,28 @@ var portableRelative2 = (from, destination) => {
|
|
|
6125
6183
|
const value = relative10(from, destination).replaceAll("\\", "/");
|
|
6126
6184
|
return value.startsWith(".") ? value : `./${value}`;
|
|
6127
6185
|
};
|
|
6128
|
-
var routeSegments = (route) =>
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6186
|
+
var routeSegments = (route) => {
|
|
6187
|
+
const segments = route.split("/").filter(Boolean);
|
|
6188
|
+
return segments.map((segment, index) => {
|
|
6189
|
+
if (segment.startsWith(":"))
|
|
6190
|
+
return `[${segment.slice(1)}]`;
|
|
6191
|
+
if (segment === "*" && index === segments.length - 1)
|
|
6192
|
+
return "[...absoluteWildcard]";
|
|
6193
|
+
if (segment === "." || segment === ".." || !/^[A-Za-z0-9._~-]+$/u.test(segment)) {
|
|
6194
|
+
throw new TypeError(`Expo native route ${route} contains an unsupported segment ${segment}.`);
|
|
6195
|
+
}
|
|
6196
|
+
return segment;
|
|
6197
|
+
});
|
|
6198
|
+
};
|
|
6136
6199
|
var routeFile = (project, route) => join14(project, "app", ...routeSegments(route), "index.tsx");
|
|
6137
|
-
var
|
|
6200
|
+
var packageDependencies = (plan) => Object.fromEntries(plan.requiredPackages.map((spec) => {
|
|
6201
|
+
const separator = spec.lastIndexOf("@");
|
|
6202
|
+
return [spec.slice(0, separator), spec.slice(separator + 1)];
|
|
6203
|
+
}));
|
|
6204
|
+
var expoPackage = (auth, sync, devices) => ({
|
|
6138
6205
|
dependencies: {
|
|
6206
|
+
"@absolutejs/devices": "0.7.0",
|
|
6207
|
+
"@absolutejs/devices-expo": "0.0.2",
|
|
6139
6208
|
...auth ? {
|
|
6140
6209
|
"@absolutejs/auth": ABSOLUTE_EXPO_AUTH_CORE_VERSION,
|
|
6141
6210
|
[ABSOLUTE_EXPO_AUTH_PACKAGE]: ABSOLUTE_EXPO_AUTH_VERSION
|
|
@@ -6167,7 +6236,8 @@ var expoPackage = (auth, sync) => ({
|
|
|
6167
6236
|
"react-native": "0.86.3",
|
|
6168
6237
|
"react-native-safe-area-context": "~5.7.0",
|
|
6169
6238
|
"react-native-screens": "4.26.0",
|
|
6170
|
-
"react-native-webview": "13.16.1"
|
|
6239
|
+
"react-native-webview": "13.16.1",
|
|
6240
|
+
...packageDependencies(devices)
|
|
6171
6241
|
},
|
|
6172
6242
|
devDependencies: {
|
|
6173
6243
|
"@types/react": "~19.2.2",
|
|
@@ -6183,36 +6253,107 @@ var expoPackage = (auth, sync) => ({
|
|
|
6183
6253
|
},
|
|
6184
6254
|
version: "0.0.0"
|
|
6185
6255
|
});
|
|
6186
|
-
var expoAppConfig = (config, auth, sync) =>
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6256
|
+
var expoAppConfig = (config, auth, sync, devices) => {
|
|
6257
|
+
const requirements = absoluteDeviceNativeRequirements(devices);
|
|
6258
|
+
const usageKey = (purpose) => {
|
|
6259
|
+
if (purpose === "camera")
|
|
6260
|
+
return "NSCameraUsageDescription";
|
|
6261
|
+
if (purpose === "photo-library")
|
|
6262
|
+
return "NSPhotoLibraryUsageDescription";
|
|
6263
|
+
if (purpose === "photo-library-add")
|
|
6264
|
+
return "NSPhotoLibraryAddUsageDescription";
|
|
6265
|
+
if (purpose === "location-always")
|
|
6266
|
+
return "NSLocationAlwaysAndWhenInUseUsageDescription";
|
|
6267
|
+
return "NSLocationWhenInUseUsageDescription";
|
|
6268
|
+
};
|
|
6269
|
+
const usageDescription = (purpose) => {
|
|
6270
|
+
if (purpose === "camera")
|
|
6271
|
+
return `${config.appName} uses your camera when you choose to take a photo.`;
|
|
6272
|
+
if (purpose === "photo-library")
|
|
6273
|
+
return `${config.appName} accesses your photo library only for photo actions you choose.`;
|
|
6274
|
+
if (purpose.startsWith("location-"))
|
|
6275
|
+
return `${config.appName} uses your location only while you use the app and request a location-based action.`;
|
|
6276
|
+
return `${config.appName} adds to your photo library only for photo actions you choose.`;
|
|
6277
|
+
};
|
|
6278
|
+
const descriptions = Object.fromEntries(requirements.iosUsageDescriptions.map((purpose) => [
|
|
6279
|
+
usageKey(purpose),
|
|
6280
|
+
usageDescription(purpose)
|
|
6281
|
+
]));
|
|
6282
|
+
const devicePlugins = [
|
|
6283
|
+
...new Set(devices.capabilities.flatMap((name) => devices.providers[name]?.plugins ?? []))
|
|
6284
|
+
];
|
|
6285
|
+
const configuredPlugins = devicePlugins.map((plugin) => {
|
|
6286
|
+
if (plugin === "expo-image-picker")
|
|
6287
|
+
return [
|
|
6288
|
+
plugin,
|
|
6289
|
+
{
|
|
6290
|
+
cameraPermission: descriptions.NSCameraUsageDescription,
|
|
6291
|
+
microphonePermission: false,
|
|
6292
|
+
photosPermission: descriptions.NSPhotoLibraryUsageDescription
|
|
6293
|
+
}
|
|
6294
|
+
];
|
|
6295
|
+
if (plugin === "expo-location")
|
|
6296
|
+
return [
|
|
6297
|
+
plugin,
|
|
6298
|
+
{
|
|
6299
|
+
locationWhenInUsePermission: descriptions.NSLocationWhenInUseUsageDescription
|
|
6300
|
+
}
|
|
6301
|
+
];
|
|
6302
|
+
return plugin;
|
|
6303
|
+
});
|
|
6304
|
+
return {
|
|
6305
|
+
expo: {
|
|
6306
|
+
android: {
|
|
6307
|
+
...devicePlugins.includes("expo-image-picker") ? {
|
|
6308
|
+
blockedPermissions: [
|
|
6309
|
+
"android.permission.READ_EXTERNAL_STORAGE",
|
|
6310
|
+
"android.permission.RECORD_AUDIO",
|
|
6311
|
+
"android.permission.WRITE_EXTERNAL_STORAGE"
|
|
6312
|
+
]
|
|
6313
|
+
} : {},
|
|
6314
|
+
intentFilters: config.deepLinkHosts.map((host2) => ({
|
|
6315
|
+
action: "VIEW",
|
|
6316
|
+
autoVerify: true,
|
|
6317
|
+
category: ["BROWSABLE", "DEFAULT"],
|
|
6318
|
+
data: [{ host: host2, pathPrefix: "/", scheme: "https" }]
|
|
6319
|
+
})),
|
|
6320
|
+
package: config.appId,
|
|
6321
|
+
permissions: requirements.androidPermissions
|
|
6322
|
+
},
|
|
6323
|
+
experiments: { typedRoutes: true },
|
|
6324
|
+
ios: {
|
|
6325
|
+
associatedDomains: config.deepLinkHosts.map((host2) => `applinks:${host2}`),
|
|
6326
|
+
bundleIdentifier: config.appId,
|
|
6327
|
+
infoPlist: descriptions,
|
|
6328
|
+
...requirements.iosPrivacyAccessedApis.length > 0 ? {
|
|
6329
|
+
privacyManifests: {
|
|
6330
|
+
NSPrivacyAccessedAPITypes: requirements.iosPrivacyAccessedApis.map(({ api, reasons }) => ({
|
|
6331
|
+
NSPrivacyAccessedAPIType: api,
|
|
6332
|
+
NSPrivacyAccessedAPITypeReasons: reasons
|
|
6333
|
+
}))
|
|
6334
|
+
}
|
|
6335
|
+
} : {},
|
|
6336
|
+
...config.iosVersion ? { buildNumber: config.iosVersion } : {}
|
|
6337
|
+
},
|
|
6338
|
+
name: config.appName,
|
|
6339
|
+
plugins: [
|
|
6340
|
+
"expo-router",
|
|
6341
|
+
["expo-dev-client", { launchMode: "most-recent" }],
|
|
6342
|
+
...auth ? ["expo-secure-store"] : [],
|
|
6343
|
+
...sync ? [
|
|
6344
|
+
"expo-sqlite",
|
|
6345
|
+
"expo-background-task",
|
|
6346
|
+
"expo-task-manager"
|
|
6347
|
+
] : [],
|
|
6348
|
+
...configuredPlugins
|
|
6349
|
+
],
|
|
6350
|
+
runtimeVersion: { policy: "appVersion" },
|
|
6351
|
+
scheme: config.deepLinkScheme,
|
|
6352
|
+
slug: config.appId.toLowerCase().replaceAll(".", "-"),
|
|
6353
|
+
version: config.iosVersion ?? "0.1.0"
|
|
6354
|
+
}
|
|
6355
|
+
};
|
|
6356
|
+
};
|
|
6216
6357
|
var expoDynamicAppConfig = `${EXPO_GENERATED_HEADER}const config = require('./app.json');
|
|
6217
6358
|
|
|
6218
6359
|
if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH) {
|
|
@@ -6280,6 +6421,7 @@ config.watchFolders = [appRoot];
|
|
|
6280
6421
|
module.exports = config;
|
|
6281
6422
|
`;
|
|
6282
6423
|
var layoutSource = (auth, sync) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
|
|
6424
|
+
import '../src/generated/AbsoluteDevices';
|
|
6283
6425
|
${auth ? `import { useEffect, useState } from 'react';
|
|
6284
6426
|
import { startAbsoluteExpoAuth } from '../src/generated/AbsoluteAuth';` : ""}
|
|
6285
6427
|
${sync ? "import { startAbsoluteExpoSync } from '../src/generated/AbsoluteSync';" : ""}
|
|
@@ -6326,6 +6468,76 @@ const styles = StyleSheet.create({
|
|
|
6326
6468
|
title: { color: '#f8fafc', fontSize: 32, fontWeight: '800' }
|
|
6327
6469
|
});
|
|
6328
6470
|
`;
|
|
6471
|
+
var devicesRuntimeSource = (config, plan, auth) => {
|
|
6472
|
+
const imports = plan.capabilities.map((name, index) => {
|
|
6473
|
+
const provider = plan.providers[name];
|
|
6474
|
+
if (!provider)
|
|
6475
|
+
throw new TypeError(`Missing Expo device capability provider ${name}.`);
|
|
6476
|
+
return `import { ${provider.factory} as absoluteExpoCapability${index} } from ${JSON.stringify(provider.module)};`;
|
|
6477
|
+
});
|
|
6478
|
+
const pushIndex = plan.capabilities.indexOf("pushNotifications");
|
|
6479
|
+
const push = pushIndex !== -1;
|
|
6480
|
+
if (push && !auth)
|
|
6481
|
+
throw new TypeError("Expo push notifications require the provisioned AbsoluteJS Auth runtime.");
|
|
6482
|
+
const entries = plan.capabilities.map((name, index) => `${JSON.stringify(name)}: absoluteExpoCapability${index}(${name === "pushNotifications" ? "absoluteExpoPushOptions" : ""})`).join(`,
|
|
6483
|
+
`);
|
|
6484
|
+
const pushSource = push ? `const INSTALLATION_KEY = 'absolutejs.push.installation-id';
|
|
6485
|
+
const requirePushResponse = async (response: Response, operation: string) => {
|
|
6486
|
+
if (!response.ok) throw new Error(\`AbsoluteJS native push \${operation} failed with HTTP \${response.status}.\`);
|
|
6487
|
+
return response.json() as Promise<Record<string, unknown>>;
|
|
6488
|
+
};
|
|
6489
|
+
const absoluteExpoPushOptions = {
|
|
6490
|
+
onRegistration: async (registration: { platform: 'apns' | 'fcm'; token: string }) => {
|
|
6491
|
+
const known = await absoluteExpoDevices.storage.get(INSTALLATION_KEY);
|
|
6492
|
+
const register = (installationId?: string | null) => absoluteExpoAuth.fetch('/auth/push', {
|
|
6493
|
+
body: JSON.stringify({ ...(installationId ? { installationId } : {}), platform: registration.platform, token: registration.token }),
|
|
6494
|
+
headers: { 'content-type': 'application/json' },
|
|
6495
|
+
method: 'POST'
|
|
6496
|
+
});
|
|
6497
|
+
let response = await register(known);
|
|
6498
|
+
const conflict = response.status === 409 && await response.clone().json().then(value => typeof value === 'object' && value !== null && Reflect.get(value, 'code') === 'installation-ownership').catch(() => false);
|
|
6499
|
+
if (known && conflict) {
|
|
6500
|
+
await absoluteExpoDevices.storage.remove(INSTALLATION_KEY);
|
|
6501
|
+
response = await register();
|
|
6502
|
+
}
|
|
6503
|
+
const result = await requirePushResponse(response, 'registration');
|
|
6504
|
+
if (typeof result.installationId !== 'string' || !result.installationId || result.installationId.length > 128) throw new Error('AbsoluteJS native push returned an invalid installation identity.');
|
|
6505
|
+
await absoluteExpoDevices.storage.set(INSTALLATION_KEY, result.installationId);
|
|
6506
|
+
},
|
|
6507
|
+
onUnregistration: async () => {
|
|
6508
|
+
const installationId = await absoluteExpoDevices.storage.get(INSTALLATION_KEY);
|
|
6509
|
+
if (!installationId) return;
|
|
6510
|
+
await requirePushResponse(await absoluteExpoAuth.fetch('/auth/push', {
|
|
6511
|
+
body: JSON.stringify({ installationId }),
|
|
6512
|
+
headers: { 'content-type': 'application/json' },
|
|
6513
|
+
method: 'DELETE'
|
|
6514
|
+
}), 'removal');
|
|
6515
|
+
await absoluteExpoDevices.storage.remove(INSTALLATION_KEY);
|
|
6516
|
+
}
|
|
6517
|
+
};` : "";
|
|
6518
|
+
return `${EXPO_GENERATED_HEADER}import { installDeviceAdapter } from '@absolutejs/devices/runtime';
|
|
6519
|
+
import { createExpoDeviceAdapter } from '@absolutejs/devices-expo';
|
|
6520
|
+
${push ? "import { absoluteExpoAuth } from './AbsoluteAuth';" : ""}
|
|
6521
|
+
${imports.join(`
|
|
6522
|
+
`)}
|
|
6523
|
+
|
|
6524
|
+
${pushSource}
|
|
6525
|
+
|
|
6526
|
+
export const absoluteExpoDeviceCapabilities = ${JSON.stringify(plan.capabilities)} as const;
|
|
6527
|
+
export const absoluteExpoDevices = createExpoDeviceAdapter({
|
|
6528
|
+
storagePrefix: ${JSON.stringify(`absolutejs.${config.appId}.`)},
|
|
6529
|
+
${entries}
|
|
6530
|
+
});
|
|
6531
|
+
installDeviceAdapter(absoluteExpoDevices);
|
|
6532
|
+
export const beforeAbsoluteExpoDeviceSignOut = async () => {
|
|
6533
|
+
${push ? "await absoluteExpoDevices.pushNotifications?.disable();" : ""}
|
|
6534
|
+
};
|
|
6535
|
+
${push ? `absoluteExpoAuth.onPrincipalChange(principal => {
|
|
6536
|
+
if (!principal) return;
|
|
6537
|
+
void absoluteExpoDevices.pushNotifications?.queryPermission().then(permission => permission.state === 'granted' ? absoluteExpoDevices.pushNotifications?.enable() : undefined).catch(() => undefined);
|
|
6538
|
+
});` : ""}
|
|
6539
|
+
`;
|
|
6540
|
+
};
|
|
6329
6541
|
var authRuntimeSource = (auth, appId) => {
|
|
6330
6542
|
const storageIdentity = createHash10("sha256").update(appId).digest("hex").slice(0, 24);
|
|
6331
6543
|
return `${EXPO_GENERATED_HEADER}import { createAbsoluteExpoAuthClient } from '@absolutejs/auth-expo';
|
|
@@ -6461,20 +6673,22 @@ var webHostSource = (config, auth, sync) => {
|
|
|
6461
6673
|
"/__absolute/native",
|
|
6462
6674
|
...Object.keys(config.expoNativeRoutes)
|
|
6463
6675
|
];
|
|
6464
|
-
|
|
6465
|
-
import * as Linking from 'expo-linking';
|
|
6676
|
+
const nativeRoutePatterns = nativeRoutes.map((route) => route.split("/").filter(Boolean));
|
|
6677
|
+
return `${EXPO_GENERATED_HEADER}import * as Linking from 'expo-linking';
|
|
6466
6678
|
import { router, usePathname } from 'expo-router';
|
|
6467
6679
|
import { useEffect, useRef, useState } from 'react';
|
|
6468
6680
|
import { ActivityIndicator, BackHandler, Platform, StyleSheet, View } from 'react-native';
|
|
6469
6681
|
import { WebView, type WebViewMessageEvent } from 'react-native-webview';
|
|
6470
6682
|
import { materializeAbsoluteWebBundle } from './webAssets';
|
|
6683
|
+
import { createExpoDevicesBridgeHost } from '@absolutejs/devices-expo/bridge';
|
|
6684
|
+
import { absoluteExpoDevices, beforeAbsoluteExpoDeviceSignOut } from './AbsoluteDevices';
|
|
6471
6685
|
${auth ? "import { absoluteExpoAuth, getAbsoluteExpoAuthPrincipal, startAbsoluteExpoAuth } from './AbsoluteAuth';" : ""}
|
|
6472
6686
|
${sync ? "import { createAbsoluteExpoSyncBridge, startAbsoluteExpoSync } from './AbsoluteSync';" : ""}
|
|
6473
6687
|
|
|
6474
6688
|
const BRIDGE_FORMAT = 3;
|
|
6475
6689
|
const MAX_MESSAGE_BYTES = 64 * 1024;
|
|
6476
6690
|
const MAX_HTTP_BODY_BYTES = 48 * 1024;
|
|
6477
|
-
const
|
|
6691
|
+
const NATIVE_ROUTE_PATTERNS = ${JSON.stringify(nativeRoutePatterns)};
|
|
6478
6692
|
const PRODUCTION_ORIGIN = ${JSON.stringify(config.productionOrigin)};
|
|
6479
6693
|
const DEV_ORIGIN = Platform.OS === 'android'
|
|
6480
6694
|
? process.env.EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN
|
|
@@ -6483,6 +6697,19 @@ const HMR_TARGET = Platform.OS === 'android' ? 'expo-android' : 'expo-ios';
|
|
|
6483
6697
|
const AUTH_ENABLED = ${auth ? "true" : "false"};
|
|
6484
6698
|
const SYNC_ENABLED = ${sync ? "true" : "false"};
|
|
6485
6699
|
|
|
6700
|
+
const isNativeRoute = (pathname: string) => {
|
|
6701
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
6702
|
+
return NATIVE_ROUTE_PATTERNS.some(pattern => {
|
|
6703
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
6704
|
+
const expected = pattern[index]!;
|
|
6705
|
+
if (expected === '*') return segments.length > index;
|
|
6706
|
+
if (segments[index] === undefined) return false;
|
|
6707
|
+
if (!expected.startsWith(':') && expected !== segments[index]) return false;
|
|
6708
|
+
}
|
|
6709
|
+
return segments.length === pattern.length;
|
|
6710
|
+
});
|
|
6711
|
+
};
|
|
6712
|
+
|
|
6486
6713
|
const bridgeBootstrap = (path: string) => {
|
|
6487
6714
|
const initialPath = DEV_ORIGIN
|
|
6488
6715
|
? 'location.pathname + location.search + location.hash'
|
|
@@ -6523,10 +6750,11 @@ const bridgeBootstrap = (path: string) => {
|
|
|
6523
6750
|
const id = 'web_' + Date.now().toString(36) + '_' + (++sequence).toString(36);
|
|
6524
6751
|
send({ format: 3, id, kind: 'request', method, params, path: currentPath });
|
|
6525
6752
|
return new Promise((resolve, reject) => {
|
|
6753
|
+
const interactive = method.startsWith('devices.camera.') || method.startsWith('devices.photos.') || method.startsWith('devices.documents.') || method.endsWith('.requestPermission');
|
|
6526
6754
|
const timer = setTimeout(() => {
|
|
6527
6755
|
pending.delete(id);
|
|
6528
6756
|
reject(new Error('Expo bridge request timed out.'));
|
|
6529
|
-
},
|
|
6757
|
+
}, interactive ? 5 * 60 * 1000 : 30 * 1000);
|
|
6530
6758
|
pending.set(id, { reject, resolve, timer });
|
|
6531
6759
|
});
|
|
6532
6760
|
},
|
|
@@ -6557,7 +6785,7 @@ const bridgeBootstrap = (path: string) => {
|
|
|
6557
6785
|
const anchor = event.target instanceof Element ? event.target.closest('a[href]') : null;
|
|
6558
6786
|
if (!anchor) return;
|
|
6559
6787
|
const url = new URL(anchor.href, location.href);
|
|
6560
|
-
if (
|
|
6788
|
+
if (!isNativeRoute(url.pathname)) return;
|
|
6561
6789
|
event.preventDefault();
|
|
6562
6790
|
send({ format: 3, kind: 'event', event: 'navigation', path: url.pathname + url.search + url.hash });
|
|
6563
6791
|
}, true);
|
|
@@ -6565,17 +6793,6 @@ const bridgeBootstrap = (path: string) => {
|
|
|
6565
6793
|
})(); true;\`;
|
|
6566
6794
|
};
|
|
6567
6795
|
|
|
6568
|
-
const impact = async (params: Record<string, unknown>) => {
|
|
6569
|
-
const style = params.style;
|
|
6570
|
-
if (style === 'selection') return Haptics.selectionAsync();
|
|
6571
|
-
if (style === 'success' || style === 'warning' || style === 'error') {
|
|
6572
|
-
const value = style === 'success' ? Haptics.NotificationFeedbackType.Success : style === 'warning' ? Haptics.NotificationFeedbackType.Warning : Haptics.NotificationFeedbackType.Error;
|
|
6573
|
-
return Haptics.notificationAsync(value);
|
|
6574
|
-
}
|
|
6575
|
-
const value = style === 'light' ? Haptics.ImpactFeedbackStyle.Light : style === 'heavy' ? Haptics.ImpactFeedbackStyle.Heavy : Haptics.ImpactFeedbackStyle.Medium;
|
|
6576
|
-
return Haptics.impactAsync(value);
|
|
6577
|
-
};
|
|
6578
|
-
|
|
6579
6796
|
const bridgeFetch = async (params: Record<string, unknown>) => {
|
|
6580
6797
|
if (typeof params.method !== 'string' || !['DELETE', 'GET', 'PATCH', 'POST', 'PUT'].includes(params.method) || typeof params.url !== 'string' || typeof params.headers !== 'object' || params.headers === null || Array.isArray(params.headers) || params.body !== undefined && typeof params.body !== 'string') throw new Error('Expo bridge HTTP request is invalid.');
|
|
6581
6798
|
const url = new URL(params.url);
|
|
@@ -6607,10 +6824,12 @@ const authStatus = async () => {
|
|
|
6607
6824
|
export function AbsoluteWebHost() {
|
|
6608
6825
|
const pathname = usePathname() || '/';
|
|
6609
6826
|
const webView = useRef<WebView>(null);
|
|
6827
|
+
const devicesBridge = useRef<{ close(): void | Promise<void>; request(method: string, params: Record<string, unknown>): Promise<unknown> } | undefined>(undefined);
|
|
6610
6828
|
const syncBridge = useRef<{ close(): void | Promise<void>; request(method: string, params: Record<string, unknown>): Promise<unknown> } | undefined>(undefined);
|
|
6611
6829
|
const [indexUri, setIndexUri] = useState<string>();
|
|
6612
6830
|
const [canGoBack, setCanGoBack] = useState(false);
|
|
6613
6831
|
const [runtimeReady, setRuntimeReady] = useState(!AUTH_ENABLED && !SYNC_ENABLED);
|
|
6832
|
+
const [devicesReady, setDevicesReady] = useState(false);
|
|
6614
6833
|
const activeWebPath = useRef(pathname);
|
|
6615
6834
|
|
|
6616
6835
|
useEffect(() => {
|
|
@@ -6660,6 +6879,23 @@ export function AbsoluteWebHost() {
|
|
|
6660
6879
|
if (new TextEncoder().encode(source).byteLength > MAX_MESSAGE_BYTES) throw new Error('Expo bridge response exceeds 64 KiB.');
|
|
6661
6880
|
webView.current?.injectJavaScript(\`globalThis.__absoluteExpoReceive(\${JSON.stringify(source)}); true;\`);
|
|
6662
6881
|
};
|
|
6882
|
+
useEffect(() => {
|
|
6883
|
+
let active = true;
|
|
6884
|
+
void createExpoDevicesBridgeHost(absoluteExpoDevices, (event, payload) => {
|
|
6885
|
+
if (!active) return;
|
|
6886
|
+
respond({ event, format: BRIDGE_FORMAT, kind: 'event', path: activeWebPath.current, payload });
|
|
6887
|
+
}).then(host => {
|
|
6888
|
+
if (!active) return void host.close();
|
|
6889
|
+
devicesBridge.current = host;
|
|
6890
|
+
setDevicesReady(true);
|
|
6891
|
+
});
|
|
6892
|
+
return () => {
|
|
6893
|
+
active = false;
|
|
6894
|
+
const host = devicesBridge.current;
|
|
6895
|
+
devicesBridge.current = undefined;
|
|
6896
|
+
void host?.close();
|
|
6897
|
+
};
|
|
6898
|
+
}, []);
|
|
6663
6899
|
const hasOrigin = (source: string, origin: string) => {
|
|
6664
6900
|
try { return new URL(source).origin === origin; } catch { return false; }
|
|
6665
6901
|
};
|
|
@@ -6672,18 +6908,16 @@ export function AbsoluteWebHost() {
|
|
|
6672
6908
|
if (message.kind === 'event' && (message.event === 'navigation' || message.event === 'ready')) {
|
|
6673
6909
|
const target = new URL(message.path, PRODUCTION_ORIGIN);
|
|
6674
6910
|
if (target.origin !== PRODUCTION_ORIGIN) return;
|
|
6675
|
-
if (
|
|
6911
|
+
if (isNativeRoute(target.pathname)) router.push(message.path as never);
|
|
6676
6912
|
else activeWebPath.current = message.path;
|
|
6677
6913
|
return;
|
|
6678
6914
|
}
|
|
6679
6915
|
if (message.kind !== 'request' || typeof message.id !== 'string' || message.path !== activeWebPath.current) return;
|
|
6680
6916
|
try {
|
|
6681
6917
|
if (typeof message.params !== 'object' || message.params === null || Array.isArray(message.params)) throw new Error('Expo bridge method params are invalid.');
|
|
6682
|
-
if (message.method === '
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
await impact(message.params as Record<string, unknown>);
|
|
6686
|
-
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: null });
|
|
6918
|
+
if (typeof message.method === 'string' && message.method.startsWith('devices.')) {
|
|
6919
|
+
if (!devicesBridge.current) throw new Error('Expo devices bridge is unavailable.');
|
|
6920
|
+
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await devicesBridge.current.request(message.method, message.params as Record<string, unknown>) });
|
|
6687
6921
|
} else if (message.method === 'http.fetch') {
|
|
6688
6922
|
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await bridgeFetch(message.params as Record<string, unknown>) });
|
|
6689
6923
|
} else if (message.method === 'auth.signIn') {
|
|
@@ -6692,7 +6926,8 @@ export function AbsoluteWebHost() {
|
|
|
6692
6926
|
await absoluteExpoAuth.signIn({ authorizationParameters: { login_hint: params.email, ...(params.signup ? { screen_hint: 'signup' } : {}) } });
|
|
6693
6927
|
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await authStatus() });` : "throw new Error('Expo Auth is not configured.');"}
|
|
6694
6928
|
} else if (message.method === 'auth.signOut') {
|
|
6695
|
-
${auth ? `await
|
|
6929
|
+
${auth ? `await beforeAbsoluteExpoDeviceSignOut();
|
|
6930
|
+
await absoluteExpoAuth.signOut();
|
|
6696
6931
|
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: null });` : "throw new Error('Expo Auth is not configured.');"}
|
|
6697
6932
|
} else if (message.method === 'auth.status') {
|
|
6698
6933
|
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await authStatus() });
|
|
@@ -6707,7 +6942,7 @@ export function AbsoluteWebHost() {
|
|
|
6707
6942
|
}
|
|
6708
6943
|
};
|
|
6709
6944
|
|
|
6710
|
-
if (!indexUri || !runtimeReady) return <View style={styles.loading}><ActivityIndicator /></View>;
|
|
6945
|
+
if (!indexUri || !runtimeReady || !devicesReady) return <View style={styles.loading}><ActivityIndicator /></View>;
|
|
6711
6946
|
return <WebView
|
|
6712
6947
|
allowFileAccess
|
|
6713
6948
|
allowFileAccessFromFileURLs
|
|
@@ -6791,6 +7026,18 @@ var writeManagedFile = async (path, source, force) => {
|
|
|
6791
7026
|
await rename11(temporary, path);
|
|
6792
7027
|
return true;
|
|
6793
7028
|
};
|
|
7029
|
+
var pruneStaleManagedExpoRoutes = async (project, expected) => {
|
|
7030
|
+
const appDirectory = join14(project, "app");
|
|
7031
|
+
if (!await exists3(appDirectory))
|
|
7032
|
+
return 0;
|
|
7033
|
+
const files = await walkFiles(appDirectory);
|
|
7034
|
+
const stale = (await Promise.all(files.map(async (path) => ({
|
|
7035
|
+
managed: path.endsWith(".tsx") && (await readFile14(path, "utf8")).startsWith(EXPO_GENERATED_HEADER),
|
|
7036
|
+
path
|
|
7037
|
+
})))).filter(({ managed, path }) => managed && !expected.has(path));
|
|
7038
|
+
await Promise.all(stale.map(({ path }) => rm9(path, { force: true })));
|
|
7039
|
+
return stale.length;
|
|
7040
|
+
};
|
|
6794
7041
|
var jsonSource = (value) => `${JSON.stringify(value, null, "\t")}
|
|
6795
7042
|
`;
|
|
6796
7043
|
var emptyWebAssetsSource = `${EXPO_GENERATED_HEADER}export const materializeAbsoluteWebBundle = async () => {
|
|
@@ -6803,6 +7050,7 @@ var writeAbsoluteExpoProject = async (config, options) => {
|
|
|
6803
7050
|
const projectRoot = resolve12(options.projectRoot);
|
|
6804
7051
|
const project = config.nativeProjectDirectory;
|
|
6805
7052
|
const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
|
|
7053
|
+
const devices = resolveAbsoluteDeviceCapabilityPlan(projectRoot, "expo");
|
|
6806
7054
|
const syncEnabled = Boolean(auth && projectUsesAbsoluteSync(projectRoot));
|
|
6807
7055
|
const syncSchema = syncEnabled ? { components: discoverAbsoluteSyncSchema(projectRoot).components } : undefined;
|
|
6808
7056
|
const routeModules = Object.entries(config.expoNativeRoutes);
|
|
@@ -6837,12 +7085,12 @@ node_modules/
|
|
|
6837
7085
|
],
|
|
6838
7086
|
[
|
|
6839
7087
|
join14(project, "app.json"),
|
|
6840
|
-
jsonSource(expoAppConfig(config, authEnabled, syncEnabled))
|
|
7088
|
+
jsonSource(expoAppConfig(config, authEnabled, syncEnabled, devices))
|
|
6841
7089
|
],
|
|
6842
7090
|
[join14(project, "app.config.js"), expoDynamicAppConfig],
|
|
6843
7091
|
[
|
|
6844
7092
|
join14(project, "package.json"),
|
|
6845
|
-
jsonSource(expoPackage(authEnabled, syncEnabled))
|
|
7093
|
+
jsonSource(expoPackage(authEnabled, syncEnabled, devices))
|
|
6846
7094
|
],
|
|
6847
7095
|
[join14(project, "metro.config.js"), metroConfig(projectRoot)],
|
|
6848
7096
|
[
|
|
@@ -6861,6 +7109,10 @@ node_modules/
|
|
|
6861
7109
|
join14(project, "app", "__absolute", "native", "index.tsx"),
|
|
6862
7110
|
nativeDiagnosticSource
|
|
6863
7111
|
],
|
|
7112
|
+
[
|
|
7113
|
+
join14(project, "src", "generated", "AbsoluteDevices.ts"),
|
|
7114
|
+
devicesRuntimeSource(config, devices, authEnabled)
|
|
7115
|
+
],
|
|
6864
7116
|
[
|
|
6865
7117
|
join14(project, "src", "generated", "AbsoluteWebHost.tsx"),
|
|
6866
7118
|
webHostSource(config, auth, syncEnabled)
|
|
@@ -6884,8 +7136,9 @@ node_modules/
|
|
|
6884
7136
|
const wrapper = route === "/" ? join14(project, "app", "index.tsx") : routeFile(project, route);
|
|
6885
7137
|
files.set(wrapper, nativeWrapperSource(wrapper, module));
|
|
6886
7138
|
}
|
|
7139
|
+
const removed = await pruneStaleManagedExpoRoutes(project, new Set([...files.keys()].filter((path) => path.startsWith(`${join14(project, "app")}${sep6}`))));
|
|
6887
7140
|
const changes = await Promise.all([...files].map(([path, source]) => writeManagedFile(path, source, true)));
|
|
6888
|
-
const changed = changes.filter(Boolean).length;
|
|
7141
|
+
const changed = removed + changes.filter(Boolean).length;
|
|
6889
7142
|
return { changed, path: project, written: [...files.keys()] };
|
|
6890
7143
|
};
|
|
6891
7144
|
var walkFiles = async (root, directory = root) => {
|
|
@@ -7072,11 +7325,8 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
7072
7325
|
const auth = resolveAbsoluteMobileAuthManifest(options.projectRoot, mobile);
|
|
7073
7326
|
const sync = auth !== undefined && projectUsesAbsoluteSync(options.projectRoot);
|
|
7074
7327
|
const syncSchema = sync ? discoverAbsoluteSyncSchema(options.projectRoot) : undefined;
|
|
7075
|
-
const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot);
|
|
7328
|
+
const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot, mobile.engine);
|
|
7076
7329
|
const usesPush = deviceCapabilities.capabilities.includes("pushNotifications");
|
|
7077
|
-
if (mobile.engine === "expo" && deviceCapabilities.capabilities.some((capability) => capability !== "haptics")) {
|
|
7078
|
-
throw new TypeError("Experimental Expo builds currently bridge only @absolutejs/devices haptics. Other detected device capabilities require their Expo adapters.");
|
|
7079
|
-
}
|
|
7080
7330
|
if (usesPush && !auth)
|
|
7081
7331
|
throw new TypeError("Portable push notifications require @absolutejs/auth so provider tokens can be registered without exposing identity controls to page code.");
|
|
7082
7332
|
if (usesPush && !loaded.app.routes.some((route) => route.path === "/auth/push" || route.path === "/auth/mobile/push"))
|
|
@@ -7234,7 +7484,64 @@ init_deviceCapabilities();
|
|
|
7234
7484
|
var ABSOLUTE_EXPO_BRIDGE_FORMAT = 3;
|
|
7235
7485
|
var ABSOLUTE_EXPO_BRIDGE_MAX_BYTES = 64 * 1024;
|
|
7236
7486
|
var ABSOLUTE_EXPO_BRIDGE_METHODS = [
|
|
7487
|
+
"devices.platform.getInfo",
|
|
7488
|
+
"devices.lifecycle.getState",
|
|
7489
|
+
"devices.links.getLaunchUrl",
|
|
7490
|
+
"devices.links.openExternal",
|
|
7491
|
+
"devices.network.getStatus",
|
|
7492
|
+
"devices.storage.clear",
|
|
7493
|
+
"devices.storage.get",
|
|
7494
|
+
"devices.storage.keys",
|
|
7495
|
+
"devices.storage.remove",
|
|
7496
|
+
"devices.storage.set",
|
|
7497
|
+
"devices.back.capability",
|
|
7498
|
+
"devices.clipboard.capability",
|
|
7499
|
+
"devices.clipboard.readText",
|
|
7500
|
+
"devices.clipboard.writeText",
|
|
7501
|
+
"devices.share.capability",
|
|
7502
|
+
"devices.share.share",
|
|
7503
|
+
"devices.haptics.capability",
|
|
7237
7504
|
"devices.haptics.impact",
|
|
7505
|
+
"devices.haptics.notification",
|
|
7506
|
+
"devices.haptics.selectionChanged",
|
|
7507
|
+
"devices.haptics.vibrate",
|
|
7508
|
+
"devices.keyboard.capability",
|
|
7509
|
+
"devices.keyboard.dismiss",
|
|
7510
|
+
"devices.keyboard.getState",
|
|
7511
|
+
"devices.systemBars.capability",
|
|
7512
|
+
"devices.systemBars.setAppearance",
|
|
7513
|
+
"devices.systemBars.setVisible",
|
|
7514
|
+
"devices.camera.capability",
|
|
7515
|
+
"devices.camera.queryPermission",
|
|
7516
|
+
"devices.camera.requestPermission",
|
|
7517
|
+
"devices.camera.takePhoto",
|
|
7518
|
+
"devices.photos.capability",
|
|
7519
|
+
"devices.photos.pick",
|
|
7520
|
+
"devices.location.capability",
|
|
7521
|
+
"devices.location.current",
|
|
7522
|
+
"devices.location.queryPermission",
|
|
7523
|
+
"devices.location.requestPermission",
|
|
7524
|
+
"devices.location.watch.start",
|
|
7525
|
+
"devices.location.watch.stop",
|
|
7526
|
+
"devices.localNotifications.capability",
|
|
7527
|
+
"devices.localNotifications.queryPermission",
|
|
7528
|
+
"devices.localNotifications.requestPermission",
|
|
7529
|
+
"devices.localNotifications.schedule",
|
|
7530
|
+
"devices.localNotifications.pending",
|
|
7531
|
+
"devices.localNotifications.cancel",
|
|
7532
|
+
"devices.pushNotifications.capability",
|
|
7533
|
+
"devices.pushNotifications.queryPermission",
|
|
7534
|
+
"devices.pushNotifications.requestPermission",
|
|
7535
|
+
"devices.pushNotifications.enable",
|
|
7536
|
+
"devices.pushNotifications.disable",
|
|
7537
|
+
"devices.documents.capability",
|
|
7538
|
+
"devices.documents.pick",
|
|
7539
|
+
"devices.transfer.read",
|
|
7540
|
+
"devices.transfer.close",
|
|
7541
|
+
"devices.upload.begin",
|
|
7542
|
+
"devices.upload.write",
|
|
7543
|
+
"devices.documents.export",
|
|
7544
|
+
"devices.documents.open",
|
|
7238
7545
|
"http.fetch",
|
|
7239
7546
|
"auth.signIn",
|
|
7240
7547
|
"auth.signOut",
|
|
@@ -7749,9 +8056,9 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
7749
8056
|
init_config();
|
|
7750
8057
|
|
|
7751
8058
|
// src/mobile/ciWorkflow.ts
|
|
7752
|
-
import { existsSync as
|
|
8059
|
+
import { existsSync as existsSync4 } from "fs";
|
|
7753
8060
|
import { access as access11, mkdir as mkdir12, readFile as readFile16, writeFile as writeFile13 } from "fs/promises";
|
|
7754
|
-
import { dirname as dirname11, extname as extname4, relative as relative11, resolve as resolve14, sep as
|
|
8061
|
+
import { dirname as dirname11, extname as extname4, relative as relative11, resolve as resolve14, sep as sep7 } from "path";
|
|
7755
8062
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
|
|
7756
8063
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
7757
8064
|
var CI_ENV_INDENTATION = 6;
|
|
@@ -7783,12 +8090,12 @@ var projectPath = (projectRoot, value, field2, options = {}) => {
|
|
|
7783
8090
|
const root = resolve14(projectRoot);
|
|
7784
8091
|
const path = resolve14(root, value);
|
|
7785
8092
|
const portable = relative11(root, path).replaceAll("\\", "/");
|
|
7786
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
8093
|
+
if (portable === ".." || portable.startsWith(`..${sep7}`) || portable.startsWith("../") || portable === "") {
|
|
7787
8094
|
throw new TypeError(`${field2} must remain inside the project root.`);
|
|
7788
8095
|
}
|
|
7789
8096
|
if (/\r|\n/u.test(portable) || portable.startsWith("-"))
|
|
7790
8097
|
throw new TypeError(`${field2} contains an unsafe path.`);
|
|
7791
|
-
if (!options.allowMissing && !
|
|
8098
|
+
if (!options.allowMissing && !existsSync4(path))
|
|
7792
8099
|
throw new TypeError(`${field2} does not exist inside the project.`);
|
|
7793
8100
|
return portable;
|
|
7794
8101
|
};
|
|
@@ -7797,7 +8104,7 @@ var workflowOutputPath = (projectRoot, value) => {
|
|
|
7797
8104
|
const workflows = resolve14(root, ".github/workflows");
|
|
7798
8105
|
const path = resolve14(root, value ?? ".github/workflows/absolute-mobile.yml");
|
|
7799
8106
|
const portable = relative11(workflows, path);
|
|
7800
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
8107
|
+
if (portable === ".." || portable.startsWith(`..${sep7}`) || extname4(path) !== ".yml" && extname4(path) !== ".yaml") {
|
|
7801
8108
|
throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
|
|
7802
8109
|
}
|
|
7803
8110
|
return path;
|
|
@@ -8976,7 +9283,7 @@ init_nativeAuth();
|
|
|
8976
9283
|
|
|
8977
9284
|
// src/mobile/releasePublisher.ts
|
|
8978
9285
|
import { access as access12 } from "fs/promises";
|
|
8979
|
-
import { isAbsolute as isAbsolute6, relative as relative12, resolve as resolve15, sep as
|
|
9286
|
+
import { isAbsolute as isAbsolute6, relative as relative12, resolve as resolve15, sep as sep8 } from "path";
|
|
8980
9287
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
8981
9288
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
8982
9289
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -9005,7 +9312,7 @@ var publisherModulePath = (projectRoot, requested) => {
|
|
|
9005
9312
|
const root = resolve15(projectRoot);
|
|
9006
9313
|
const path = resolve15(root, requested);
|
|
9007
9314
|
const projectRelative = relative12(root, path);
|
|
9008
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
9315
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep8}`) || isAbsolute6(projectRelative)) {
|
|
9009
9316
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
9010
9317
|
}
|
|
9011
9318
|
return path;
|
|
@@ -9073,7 +9380,7 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
9073
9380
|
return publication;
|
|
9074
9381
|
};
|
|
9075
9382
|
// src/mobile/routeMetadataTransform.ts
|
|
9076
|
-
import { existsSync as
|
|
9383
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
9077
9384
|
import { dirname as dirname13, extname as extname5, relative as relative13, resolve as resolve16 } from "path";
|
|
9078
9385
|
import ts2 from "typescript";
|
|
9079
9386
|
var ROUTE_METHODS = new Set(["get", "head"]);
|
|
@@ -9125,7 +9432,7 @@ var PAGE_HANDLERS = new Map([
|
|
|
9125
9432
|
]
|
|
9126
9433
|
]);
|
|
9127
9434
|
var posixPath = (value) => value.replace(/\\/g, "/");
|
|
9128
|
-
var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(dirname13(entry),
|
|
9435
|
+
var findTsconfig = (entry, projectRoot) => ts2.findConfigFile(dirname13(entry), existsSync7, "tsconfig.json") ?? ts2.findConfigFile(projectRoot, existsSync7, "tsconfig.json");
|
|
9129
9436
|
var createProgram = (entry, projectRoot) => {
|
|
9130
9437
|
const configPath2 = findTsconfig(entry, projectRoot);
|
|
9131
9438
|
if (!configPath2) {
|
|
@@ -9965,5 +10272,5 @@ export {
|
|
|
9965
10272
|
writeAbsoluteMobileGithubWorkflow
|
|
9966
10273
|
};
|
|
9967
10274
|
|
|
9968
|
-
//# debugId=
|
|
10275
|
+
//# debugId=CFD74BC8ED7A634C64756E2164756E21
|
|
9969
10276
|
//# sourceMappingURL=index.js.map
|