@absolutejs/absolute 0.20.0-beta.46 → 0.20.0-beta.48

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/cli/index.js CHANGED
@@ -718,7 +718,7 @@ var init_portScan = () => {};
718
718
 
719
719
  // src/mobile/config.ts
720
720
  import { resolve as resolve2 } from "path";
721
- var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, HOSTNAME_PATTERN, resolveProjectPath = (projectRoot, value, field) => {
721
+ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, HOSTNAME_PATTERN, EXPO_RESERVED_ROUTE_PREFIXES, resolveProjectPath = (projectRoot, value, field) => {
722
722
  const root = resolve2(projectRoot);
723
723
  const path = resolve2(root, value);
724
724
  if (path !== root && !path.startsWith(`${root}/`)) {
@@ -791,11 +791,31 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
791
791
  }
792
792
  return value.match(/.{2}/g)?.join(":") ?? value;
793
793
  }))
794
- ].sort(), normalizeExpoNativeRoutes = (config, projectRoot) => {
794
+ ].sort(), validateExpoNativeRouteSegment = (path, segment, index, count, parameters) => {
795
+ if (segment === "*" && (index !== count - 1 || count === 1)) {
796
+ throw new TypeError(`mobile.routes.native route ${path} must use * once, as the final segment after a static or parameterized prefix.`);
797
+ }
798
+ if (segment === "*")
799
+ return;
800
+ if (!segment.startsWith(":") && (segment.includes("*") || segment.includes(":"))) {
801
+ throw new TypeError(`mobile.routes.native route ${path} contains invalid segment ${segment}.`);
802
+ }
803
+ if (!segment.startsWith(":"))
804
+ return;
805
+ const name = segment.slice(1);
806
+ if (!/^[A-Za-z][A-Za-z0-9_]*$/u.test(name)) {
807
+ throw new TypeError(`mobile.routes.native route ${path} has invalid parameter ${segment}.`);
808
+ }
809
+ if (parameters.has(name)) {
810
+ throw new TypeError(`mobile.routes.native route ${path} repeats parameter ${segment}.`);
811
+ }
812
+ parameters.add(name);
813
+ }, normalizeExpoNativeRoutes = (config, projectRoot) => {
795
814
  if (config.engine !== "expo")
796
815
  return {};
797
816
  const routes = config.routes?.native ?? {};
798
817
  const normalized = {};
818
+ const ownership = new Map;
799
819
  for (const [route, module] of Object.entries(routes)) {
800
820
  const path = normalizeEntry(route);
801
821
  if (path.includes("?") || path.includes("#") || path !== "/" && path.endsWith("/")) {
@@ -804,9 +824,18 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
804
824
  if (path === "/__absolute/native") {
805
825
  throw new TypeError("mobile.routes.native reserves /__absolute/native for the Expo diagnostic screen.");
806
826
  }
807
- if (path.includes("*") || path.includes(":")) {
808
- throw new TypeError(`mobile.routes.native route ${path} must be static during the Expo experiment; parameters and wildcards are not supported yet.`);
827
+ const segments = path.split("/").filter(Boolean);
828
+ if (segments[0] && EXPO_RESERVED_ROUTE_PREFIXES.has(segments[0])) {
829
+ throw new TypeError(`mobile.routes.native route ${path} conflicts with an Expo Router or Metro reserved path.`);
830
+ }
831
+ const parameters = new Set;
832
+ segments.forEach((segment, index) => validateExpoNativeRouteSegment(path, segment, index, segments.length, parameters));
833
+ const signature = segments.map((segment) => segment.startsWith(":") ? ":" : segment).join("/");
834
+ const existing = ownership.get(signature);
835
+ if (existing) {
836
+ throw new TypeError(`mobile.routes.native routes ${existing} and ${path} claim the same Expo route pattern.`);
809
837
  }
838
+ ownership.set(signature, path);
810
839
  normalized[path] = resolveProjectPath(projectRoot, requireText(module, `mobile.routes.native[${path}]`), `mobile.routes.native[${path}]`);
811
840
  }
812
841
  return Object.fromEntries(Object.entries(normalized).sort(([left], [right]) => left.localeCompare(right)));
@@ -845,6 +874,16 @@ var init_config = __esm(() => {
845
874
  APPLE_APP_ID_PREFIX_PATTERN = /^[A-Z0-9]{10}$/;
846
875
  CERTIFICATE_FINGERPRINT_PATTERN = /^[0-9A-F]{64}$/;
847
876
  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])?))*$/;
877
+ EXPO_RESERVED_ROUTE_PREFIXES = new Set([
878
+ "_expo",
879
+ "_flight",
880
+ "_sitemap",
881
+ "assets",
882
+ "expo-dev-plugins",
883
+ "inspector",
884
+ "manifest",
885
+ "public"
886
+ ]);
848
887
  });
849
888
 
850
889
  // src/mobile/nativeAuth.ts
@@ -1600,7 +1639,7 @@ import {
1600
1639
  writeFile
1601
1640
  } from "fs/promises";
1602
1641
  import { createHash } from "crypto";
1603
- import { basename as basename2, dirname as dirname4, join as join8, relative as relative2, resolve as resolve5 } from "path";
1642
+ import { basename as basename2, dirname as dirname4, join as join8, relative as relative2, resolve as resolve5, sep } from "path";
1604
1643
  var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
1605
1644
  `, EXPO_ASSET_EXTENSION = ".absasset", EXPO_PROJECT_MARKER = ".absolutejs-expo-project", exists = async (path) => {
1606
1645
  try {
@@ -1609,17 +1648,61 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
1609
1648
  } catch {
1610
1649
  return false;
1611
1650
  }
1651
+ }, isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifestString = (value, field) => {
1652
+ if (typeof value !== "string" || !value) {
1653
+ throw new TypeError(`AbsoluteJS mobile manifest ${field} is invalid.`);
1654
+ }
1655
+ return value;
1656
+ }, expoNativeDataManifest = (value) => {
1657
+ if (!isRecord(value) || !Array.isArray(value.pages) || !Array.isArray(value.routes)) {
1658
+ throw new TypeError("AbsoluteJS mobile manifest is invalid.");
1659
+ }
1660
+ const pages = value.pages.map((page) => {
1661
+ if (!isRecord(page))
1662
+ throw new TypeError("AbsoluteJS mobile manifest page is invalid.");
1663
+ return {
1664
+ bundleHash: requireManifestString(page.bundleHash, "page.bundleHash"),
1665
+ contract: requireManifestString(page.contract, "page.contract"),
1666
+ pageId: requireManifestString(page.pageId, "page.pageId")
1667
+ };
1668
+ });
1669
+ const routes = value.routes.flatMap((route) => {
1670
+ if (!isRecord(route) || typeof route.method !== "string") {
1671
+ throw new TypeError("AbsoluteJS mobile manifest route is invalid.");
1672
+ }
1673
+ if (route.method !== "GET")
1674
+ return [];
1675
+ return [
1676
+ {
1677
+ method: "GET",
1678
+ pageId: requireManifestString(route.pageId, "route.pageId"),
1679
+ pattern: requireManifestString(route.pattern, "route.pattern")
1680
+ }
1681
+ ];
1682
+ });
1683
+ return {
1684
+ appBuild: requireManifestString(value.appBuild, "appBuild"),
1685
+ pages,
1686
+ productionOrigin: requireManifestString(value.productionOrigin, "productionOrigin"),
1687
+ routes,
1688
+ runtime: requireManifestString(value.runtime, "runtime")
1689
+ };
1612
1690
  }, portableRelative = (from, destination) => {
1613
1691
  const value = relative2(from, destination).replaceAll("\\", "/");
1614
1692
  return value.startsWith(".") ? value : `./${value}`;
1615
- }, routeSegments = (route) => route.split("/").filter(Boolean).map((segment) => {
1616
- if (segment.startsWith(":"))
1617
- return `[${segment.slice(1)}]`;
1618
- if (segment === "." || segment === ".." || !/^[A-Za-z0-9._~-]+$/u.test(segment)) {
1619
- throw new TypeError(`Expo native route ${route} contains an unsupported segment ${segment}.`);
1620
- }
1621
- return segment;
1622
- }), routeFile = (project, route) => join8(project, "app", ...routeSegments(route), "index.tsx"), packageDependencies = (plan) => Object.fromEntries(plan.requiredPackages.map((spec) => {
1693
+ }, routeSegments = (route) => {
1694
+ const segments = route.split("/").filter(Boolean);
1695
+ return segments.map((segment, index) => {
1696
+ if (segment.startsWith(":"))
1697
+ return `[${segment.slice(1)}]`;
1698
+ if (segment === "*" && index === segments.length - 1)
1699
+ return "[...absoluteWildcard]";
1700
+ if (segment === "." || segment === ".." || !/^[A-Za-z0-9._~-]+$/u.test(segment)) {
1701
+ throw new TypeError(`Expo native route ${route} contains an unsupported segment ${segment}.`);
1702
+ }
1703
+ return segment;
1704
+ });
1705
+ }, routeFile = (project, route) => join8(project, "app", ...routeSegments(route), "index.tsx"), packageDependencies = (plan) => Object.fromEntries(plan.requiredPackages.map((spec) => {
1623
1706
  const separator = spec.lastIndexOf("@");
1624
1707
  return [spec.slice(0, separator), spec.slice(separator + 1)];
1625
1708
  })), expoPackage = (auth, sync, devices) => ({
@@ -2005,6 +2088,7 @@ export const createAbsoluteExpoSyncBridge = async (
2005
2088
  "/__absolute/native",
2006
2089
  ...Object.keys(config.expoNativeRoutes)
2007
2090
  ];
2091
+ const nativeRoutePatterns = nativeRoutes.map((route) => route.split("/").filter(Boolean));
2008
2092
  return `${EXPO_GENERATED_HEADER}import * as Linking from 'expo-linking';
2009
2093
  import { router, usePathname } from 'expo-router';
2010
2094
  import { useEffect, useRef, useState } from 'react';
@@ -2019,7 +2103,7 @@ ${sync ? "import { createAbsoluteExpoSyncBridge, startAbsoluteExpoSync } from '.
2019
2103
  const BRIDGE_FORMAT = 3;
2020
2104
  const MAX_MESSAGE_BYTES = 64 * 1024;
2021
2105
  const MAX_HTTP_BODY_BYTES = 48 * 1024;
2022
- const NATIVE_ROUTES = new Set(${JSON.stringify(nativeRoutes)});
2106
+ const NATIVE_ROUTE_PATTERNS = ${JSON.stringify(nativeRoutePatterns)};
2023
2107
  const PRODUCTION_ORIGIN = ${JSON.stringify(config.productionOrigin)};
2024
2108
  const DEV_ORIGIN = Platform.OS === 'android'
2025
2109
  ? process.env.EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN
@@ -2028,6 +2112,19 @@ const HMR_TARGET = Platform.OS === 'android' ? 'expo-android' : 'expo-ios';
2028
2112
  const AUTH_ENABLED = ${auth ? "true" : "false"};
2029
2113
  const SYNC_ENABLED = ${sync ? "true" : "false"};
2030
2114
 
2115
+ const isNativeRoute = (pathname: string) => {
2116
+ const segments = pathname.split('/').filter(Boolean);
2117
+ return NATIVE_ROUTE_PATTERNS.some(pattern => {
2118
+ for (let index = 0; index < pattern.length; index += 1) {
2119
+ const expected = pattern[index]!;
2120
+ if (expected === '*') return segments.length > index;
2121
+ if (segments[index] === undefined) return false;
2122
+ if (!expected.startsWith(':') && expected !== segments[index]) return false;
2123
+ }
2124
+ return segments.length === pattern.length;
2125
+ });
2126
+ };
2127
+
2031
2128
  const bridgeBootstrap = (path: string) => {
2032
2129
  const initialPath = DEV_ORIGIN
2033
2130
  ? 'location.pathname + location.search + location.hash'
@@ -2103,7 +2200,7 @@ const bridgeBootstrap = (path: string) => {
2103
2200
  const anchor = event.target instanceof Element ? event.target.closest('a[href]') : null;
2104
2201
  if (!anchor) return;
2105
2202
  const url = new URL(anchor.href, location.href);
2106
- if (!${JSON.stringify(nativeRoutes)}.includes(url.pathname)) return;
2203
+ if (!isNativeRoute(url.pathname)) return;
2107
2204
  event.preventDefault();
2108
2205
  send({ format: 3, kind: 'event', event: 'navigation', path: url.pathname + url.search + url.hash });
2109
2206
  }, true);
@@ -2226,7 +2323,7 @@ export function AbsoluteWebHost() {
2226
2323
  if (message.kind === 'event' && (message.event === 'navigation' || message.event === 'ready')) {
2227
2324
  const target = new URL(message.path, PRODUCTION_ORIGIN);
2228
2325
  if (target.origin !== PRODUCTION_ORIGIN) return;
2229
- if (NATIVE_ROUTES.has(target.pathname)) router.push(message.path as never);
2326
+ if (isNativeRoute(target.pathname)) router.push(message.path as never);
2230
2327
  else activeWebPath.current = message.path;
2231
2328
  return;
2232
2329
  }
@@ -2282,7 +2379,133 @@ export function AbsoluteWebHost() {
2282
2379
 
2283
2380
  const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, justifyContent: 'center' }, web: { flex: 1 } });
2284
2381
  `;
2285
- }, webRouteSource, catchAllRouteSource, nativeWrapperSource = (wrapper, module) => `${EXPO_GENERATED_HEADER}export { default } from ${JSON.stringify(portableRelative(dirname4(wrapper), module).replace(/\.(?:[cm]?[jt]sx?)$/u, ""))};
2382
+ }, webRouteSource, catchAllRouteSource, nativeRouteRuntimeSource = (auth) => `${EXPO_GENERATED_HEADER}import { useLocalSearchParams, usePathname } from 'expo-router';
2383
+ import { type ComponentType, useEffect, useMemo, useState } from 'react';
2384
+ import { ActivityIndicator, Platform, Pressable, StyleSheet, Text, View } from 'react-native';
2385
+ import { ABSOLUTE_MOBILE_MANIFEST } from './webAssets';
2386
+ ${auth ? "import { absoluteExpoAuth } from './AbsoluteAuth';" : ""}
2387
+
2388
+ const PAGE_MEDIA_TYPE = 'application/vnd.absolute.page+json';
2389
+ const NATIVE_DATA_MEDIA_TYPE = 'application/vnd.absolute.native-route+json';
2390
+ const PROTOCOL = 1;
2391
+ const MAX_DATA_BYTES = 1024 * 1024;
2392
+ const DEV_ORIGIN = Platform.OS === 'android'
2393
+ ? process.env.EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN
2394
+ : process.env.EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN;
2395
+
2396
+ type RouteParams = Record<string, string | string[] | undefined>;
2397
+ type NativeRouteProps<PageProps extends object, Params extends object> = {
2398
+ pageProps: Readonly<PageProps>;
2399
+ params: Readonly<Params>;
2400
+ reload: () => void;
2401
+ };
2402
+ type NativeRouteState<PageProps> =
2403
+ | { kind: 'loading' }
2404
+ | { kind: 'ready'; pageProps: PageProps }
2405
+ | { kind: 'error' };
2406
+
2407
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
2408
+ typeof value === 'object' && value !== null && !Array.isArray(value);
2409
+ const routeSegmentPattern = (segment: string) => {
2410
+ if (segment === '*') return '.*';
2411
+ if (segment.startsWith(':') && segment.endsWith('?')) return '[^/]*';
2412
+ if (segment.startsWith(':')) return '[^/]+';
2413
+ return segment.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&');
2414
+ };
2415
+ const matchesRoute = (pattern: string, pathname: string) =>
2416
+ new RegExp('^' + pattern.split('/').map(routeSegmentPattern).join('/') + '/?$').test(pathname);
2417
+ const requestPathFor = (pathname: string, params: RouteParams, pattern: string) => {
2418
+ const pathNames = new Set(pattern.split('/').filter(segment => segment.startsWith(':')).map(segment => segment.replace(/^:/, '').replace(/\\?$/, '')));
2419
+ if (pattern.endsWith('/*')) pathNames.add('absoluteWildcard');
2420
+ const query = new URLSearchParams();
2421
+ for (const [name, value] of Object.entries(params).sort(([left], [right]) => left.localeCompare(right))) {
2422
+ if (pathNames.has(name) || name === '#' || value === undefined) continue;
2423
+ for (const item of Array.isArray(value) ? value : [value]) query.append(name, item);
2424
+ }
2425
+ const search = query.toString();
2426
+ return pathname + (search ? '?' + search : '');
2427
+ };
2428
+ const productionPage = (pathname: string) => {
2429
+ if (!ABSOLUTE_MOBILE_MANIFEST) throw new Error('AbsoluteJS native route data is not prepared for production.');
2430
+ const route = ABSOLUTE_MOBILE_MANIFEST.routes.find(candidate => candidate.method === 'GET' && matchesRoute(candidate.pattern, pathname));
2431
+ if (!route) throw new Error('No trusted AbsoluteJS page route owns this native URL.');
2432
+ const page = ABSOLUTE_MOBILE_MANIFEST.pages.find(candidate => candidate.pageId === route.pageId);
2433
+ if (!page) throw new Error('The embedded AbsoluteJS page contract is incomplete.');
2434
+ return page;
2435
+ };
2436
+ const createDataRequest = (path: string) => {
2437
+ const development = typeof DEV_ORIGIN === 'string' && DEV_ORIGIN.length > 0;
2438
+ const origin = development ? DEV_ORIGIN : ABSOLUTE_MOBILE_MANIFEST?.productionOrigin;
2439
+ if (!origin) throw new Error('AbsoluteJS native route data has no trusted server origin.');
2440
+ const url = new URL(path, origin);
2441
+ if (url.origin !== new URL(origin).origin) throw new Error('AbsoluteJS native route data left its trusted origin.');
2442
+ const headers = new Headers();
2443
+ headers.set('accept', development ? NATIVE_DATA_MEDIA_TYPE : PAGE_MEDIA_TYPE);
2444
+ headers.set('x-absolute-mobile-protocol', String(PROTOCOL));
2445
+ let page: ReturnType<typeof productionPage> | undefined;
2446
+ if (!development) {
2447
+ page = productionPage(url.pathname);
2448
+ headers.set('x-absolute-mobile-app-build', ABSOLUTE_MOBILE_MANIFEST!.appBuild);
2449
+ headers.set('x-absolute-mobile-page-bundle', page.bundleHash);
2450
+ headers.set('x-absolute-mobile-page-contracts', page.contract);
2451
+ headers.set('x-absolute-mobile-page-id', page.pageId);
2452
+ headers.set('x-absolute-mobile-runtime', ABSOLUTE_MOBILE_MANIFEST!.runtime);
2453
+ }
2454
+ return { page, request: new Request(url, { headers, method: 'GET' }) };
2455
+ };
2456
+ const requestData = async <PageProps extends object>(path: string, signal: AbortSignal) => {
2457
+ const { page, request } = createDataRequest(path);
2458
+ const response = await ${auth ? "absoluteExpoAuth.fetchOptional" : "fetch"}(request, { redirect: 'manual', signal });
2459
+ if (new URL(response.url || request.url).origin !== new URL(request.url).origin || response.status >= 300 && response.status < 400) throw new Error('AbsoluteJS native route data redirected outside its contract.');
2460
+ const source = await response.text();
2461
+ if (new TextEncoder().encode(source).byteLength > MAX_DATA_BYTES) throw new Error('AbsoluteJS native route data exceeded 1 MiB.');
2462
+ let envelope: unknown;
2463
+ try { envelope = JSON.parse(source); } catch { throw new Error('The server did not return an AbsoluteJS page envelope.'); }
2464
+ if (!isRecord(envelope) || envelope.protocol !== PROTOCOL || !isRecord(envelope.response)) throw new Error('The server returned an invalid AbsoluteJS page envelope.');
2465
+ const result = envelope.response;
2466
+ if (result.kind === 'upgrade-required') throw new Error('This app version must be updated before opening this screen.');
2467
+ if (result.kind !== 'page' || !isRecord(result.props) || typeof result.pageId !== 'string' || typeof result.contract !== 'string') throw new Error('The server did not produce native route page props.');
2468
+ if (page && (result.pageId !== page.pageId || result.contract !== page.contract)) throw new Error('The server returned a different page contract than this app contains.');
2469
+ return result.props as PageProps;
2470
+ };
2471
+
2472
+ export const createAbsoluteNativeRoute = <
2473
+ PageProps extends object,
2474
+ Params extends object = RouteParams
2475
+ >(Component: ComponentType<NativeRouteProps<PageProps, Params>>, pattern: string) => {
2476
+ function AbsoluteNativeRouteScreen() {
2477
+ const pathname = usePathname() || '/';
2478
+ const params = useLocalSearchParams() as RouteParams;
2479
+ const parameterKey = JSON.stringify(params);
2480
+ const requestPath = useMemo(() => requestPathFor(pathname, params, pattern), [parameterKey, pathname]);
2481
+ const [revision, setRevision] = useState(0);
2482
+ const [state, setState] = useState<NativeRouteState<PageProps>>({ kind: 'loading' });
2483
+ useEffect(() => {
2484
+ const controller = new AbortController();
2485
+ setState({ kind: 'loading' });
2486
+ void requestData<PageProps>(requestPath, controller.signal).then(
2487
+ pageProps => { if (!controller.signal.aborted) setState({ kind: 'ready', pageProps }); },
2488
+ () => { if (!controller.signal.aborted) setState({ kind: 'error' }); }
2489
+ );
2490
+ return () => controller.abort();
2491
+ }, [requestPath, revision]);
2492
+ const reload = () => setRevision(value => value + 1);
2493
+ if (state.kind === 'loading') return <View style={styles.center}><ActivityIndicator accessibilityLabel="Loading screen" /></View>;
2494
+ if (state.kind === 'error') return <View style={styles.center}><Text accessibilityRole="alert" style={styles.error}>This screen could not load.</Text><Pressable accessibilityRole="button" onPress={reload} style={styles.button}><Text>Try again</Text></Pressable></View>;
2495
+ return <Component pageProps={state.pageProps} params={params as Params} reload={reload} />;
2496
+ }
2497
+ return AbsoluteNativeRouteScreen;
2498
+ };
2499
+
2500
+ const styles = StyleSheet.create({
2501
+ button: { backgroundColor: '#e2e8f0', borderRadius: 10, paddingHorizontal: 16, paddingVertical: 12 },
2502
+ center: { alignItems: 'center', flex: 1, gap: 16, justifyContent: 'center', padding: 24 },
2503
+ error: { color: '#b91c1c', fontSize: 16, textAlign: 'center' }
2504
+ });
2505
+ `, nativeWrapperSource = (wrapper, module, runtime, route) => `${EXPO_GENERATED_HEADER}import ApplicationNativeRoute from ${JSON.stringify(portableRelative(dirname4(wrapper), module).replace(/\.(?:[cm]?[jt]sx?)$/u, ""))};
2506
+ import { createAbsoluteNativeRoute } from ${JSON.stringify(portableRelative(dirname4(wrapper), runtime).replace(/\.(?:[cm]?[jt]sx?)$/u, ""))};
2507
+
2508
+ export default createAbsoluteNativeRoute(ApplicationNativeRoute, ${JSON.stringify(route)});
2286
2509
  `, expoTsConfig = (projectRoot, project, auth, sync) => ({
2287
2510
  compilerOptions: {
2288
2511
  paths: {
@@ -2332,7 +2555,25 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
2332
2555
  await writeFile(temporary, source, { flag: "wx" });
2333
2556
  await rename(temporary, path);
2334
2557
  return true;
2558
+ }, pruneStaleManagedExpoRoutes = async (project, expected) => {
2559
+ const appDirectory = join8(project, "app");
2560
+ if (!await exists(appDirectory))
2561
+ return 0;
2562
+ const files = await walkFiles(appDirectory);
2563
+ const stale = (await Promise.all(files.map(async (path) => ({
2564
+ managed: path.endsWith(".tsx") && (await readFile(path, "utf8")).startsWith(EXPO_GENERATED_HEADER),
2565
+ path
2566
+ })))).filter(({ managed, path }) => managed && !expected.has(path));
2567
+ await Promise.all(stale.map(({ path }) => rm(path, { force: true })));
2568
+ return stale.length;
2335
2569
  }, jsonSource = (value) => `${JSON.stringify(value, null, "\t")}
2570
+ `, nativeDataManifestTypeSource = `type AbsoluteMobileManifest = {
2571
+ appBuild: string;
2572
+ pages: readonly { bundleHash: string; contract: string; pageId: string }[];
2573
+ productionOrigin: string;
2574
+ routes: readonly { method: 'GET'; pageId: string; pattern: string }[];
2575
+ runtime: string;
2576
+ };
2336
2577
  `, emptyWebAssetsSource, writeAbsoluteExpoProject = async (config, options) => {
2337
2578
  if (config.engine !== "expo")
2338
2579
  throw new TypeError("Expo project generation requires mobile.engine: expo.");
@@ -2402,6 +2643,10 @@ node_modules/
2402
2643
  join8(project, "src", "generated", "AbsoluteDevices.ts"),
2403
2644
  devicesRuntimeSource(config, devices, authEnabled)
2404
2645
  ],
2646
+ [
2647
+ join8(project, "src", "generated", "AbsoluteNativeRoute.tsx"),
2648
+ nativeRouteRuntimeSource(authEnabled)
2649
+ ],
2405
2650
  [
2406
2651
  join8(project, "src", "generated", "AbsoluteWebHost.tsx"),
2407
2652
  webHostSource(config, auth, syncEnabled)
@@ -2421,12 +2666,14 @@ node_modules/
2421
2666
  files.set(join8(project, "app", "index.tsx"), webRouteSource);
2422
2667
  }
2423
2668
  files.set(join8(project, "app", "[...absolute].tsx"), catchAllRouteSource);
2669
+ const nativeRouteRuntime = join8(project, "src", "generated", "AbsoluteNativeRoute.tsx");
2424
2670
  for (const [route, module] of routeModules) {
2425
2671
  const wrapper = route === "/" ? join8(project, "app", "index.tsx") : routeFile(project, route);
2426
- files.set(wrapper, nativeWrapperSource(wrapper, module));
2672
+ files.set(wrapper, nativeWrapperSource(wrapper, module, nativeRouteRuntime, route));
2427
2673
  }
2674
+ const removed = await pruneStaleManagedExpoRoutes(project, new Set([...files.keys()].filter((path) => path.startsWith(`${join8(project, "app")}${sep}`))));
2428
2675
  const changes = await Promise.all([...files].map(([path, source]) => writeManagedFile(path, source, true)));
2429
- const changed = changes.filter(Boolean).length;
2676
+ const changed = removed + changes.filter(Boolean).length;
2430
2677
  return { changed, path: project, written: [...files.keys()] };
2431
2678
  }, walkFiles = async (root, directory = root) => {
2432
2679
  const entries = await readdir(directory, { withFileTypes: true });
@@ -2461,11 +2708,13 @@ node_modules/
2461
2708
  await rename(backup, destination);
2462
2709
  throw error;
2463
2710
  }
2464
- }, assetModuleSource = (assets, bundleId) => `${EXPO_GENERATED_HEADER}import { Asset } from 'expo-asset';
2711
+ }, assetModuleSource = (assets, bundleId, manifest) => `${EXPO_GENERATED_HEADER}import { Asset } from 'expo-asset';
2465
2712
  import { Directory, File, Paths } from 'expo-file-system';
2466
2713
 
2714
+ ${nativeDataManifestTypeSource}
2467
2715
  declare const require: (path: string) => number;
2468
2716
  const BUNDLE_ID = ${JSON.stringify(bundleId)};
2717
+ export const ABSOLUTE_MOBILE_MANIFEST: AbsoluteMobileManifest = ${JSON.stringify(manifest)};
2469
2718
  const ASSETS = [
2470
2719
  ${assets.map(({ asset, path }) => ` { module: require(${JSON.stringify(asset)}), path: ${JSON.stringify(path)} }`).join(`,
2471
2720
  `)}
@@ -2498,9 +2747,8 @@ export const materializeAbsoluteWebBundle = async () => {
2498
2747
  }
2499
2748
  const manifestPath = join8(config.bundleDirectory, "absolute-mobile-manifest.json");
2500
2749
  const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
2501
- const appBuild = typeof manifest === "object" && manifest !== null && typeof Reflect.get(manifest, "appBuild") === "string" ? String(Reflect.get(manifest, "appBuild")) : undefined;
2502
- if (!appBuild)
2503
- throw new TypeError("AbsoluteJS mobile manifest has no appBuild.");
2750
+ const nativeDataManifest = expoNativeDataManifest(manifest);
2751
+ const { appBuild } = nativeDataManifest;
2504
2752
  const files = await walkFiles(config.bundleDirectory);
2505
2753
  const bundleHash = createHash("sha256");
2506
2754
  const filesWithContents = await Promise.all(files.map(async (file) => ({ contents: await readFile(file), file })));
@@ -2530,7 +2778,7 @@ export const materializeAbsoluteWebBundle = async () => {
2530
2778
  throw error;
2531
2779
  }
2532
2780
  const generated = join8(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
2533
- await writeManagedFile(generated, assetModuleSource(assets, bundleId), true);
2781
+ await writeManagedFile(generated, assetModuleSource(assets, bundleId, nativeDataManifest), true);
2534
2782
  return { appBuild, assets: assets.length, bundleId, path: destination };
2535
2783
  };
2536
2784
  var init_expoProject = __esm(() => {
@@ -2627,9 +2875,11 @@ export default AbsoluteWebHost;
2627
2875
 
2628
2876
  export default AbsoluteWebHost;
2629
2877
  `;
2630
- emptyWebAssetsSource = `${EXPO_GENERATED_HEADER}export const materializeAbsoluteWebBundle = async () => {
2878
+ emptyWebAssetsSource = `${EXPO_GENERATED_HEADER}${nativeDataManifestTypeSource}
2879
+ export const materializeAbsoluteWebBundle = async () => {
2631
2880
  throw new Error('The embedded AbsoluteJS bundle is unavailable. Run absolute prepare before a production Expo build.');
2632
2881
  };
2882
+ export const ABSOLUTE_MOBILE_MANIFEST: AbsoluteMobileManifest | undefined = undefined;
2633
2883
  `;
2634
2884
  });
2635
2885
 
@@ -3142,7 +3392,7 @@ import {
3142
3392
  join as join11,
3143
3393
  relative as relative4,
3144
3394
  resolve as resolve8,
3145
- sep,
3395
+ sep as sep2,
3146
3396
  win32
3147
3397
  } from "path";
3148
3398
  var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_FORMAT = 1, NATIVE_CACHE_FORMAT = 1, HASH_RADIX = 16, EXECUTABLE_MODE_MASK = 73, NATIVE_PUBLIC_PATH_SEGMENTS = 5, CAPACITOR_PROJECT_DIRECTORY_PATTERN, ANDROID_TIMING_PHASES, androidTimingSummary = (timings, physicalDevice = false) => ANDROID_TIMING_PHASES.map(([phase, label]) => {
@@ -3324,12 +3574,12 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
3324
3574
  const resolvedRoot = resolve8(root);
3325
3575
  const resolvedPath = resolve8(path);
3326
3576
  const relativePath = relative4(resolvedRoot, resolvedPath);
3327
- return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
3328
- }, isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), nativeCachePath = (projectRoot) => join11(projectRoot, ".absolutejs", "mobile", "cache", "android-debug.json"), parseNativeCache = (value) => {
3329
- if (!isRecord(value))
3577
+ return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep2}`) && !isAbsolute(relativePath);
3578
+ }, isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), nativeCachePath = (projectRoot) => join11(projectRoot, ".absolutejs", "mobile", "cache", "android-debug.json"), parseNativeCache = (value) => {
3579
+ if (!isRecord2(value))
3330
3580
  return null;
3331
3581
  const { appId, fingerprint, format, installations } = value;
3332
- if (format !== NATIVE_CACHE_FORMAT || typeof appId !== "string" || typeof fingerprint !== "string" || !isRecord(installations) || !Object.values(installations).every((identity) => typeof identity === "string")) {
3582
+ if (format !== NATIVE_CACHE_FORMAT || typeof appId !== "string" || typeof fingerprint !== "string" || !isRecord2(installations) || !Object.values(installations).every((identity) => typeof identity === "string")) {
3333
3583
  return null;
3334
3584
  }
3335
3585
  return {
@@ -3368,7 +3618,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
3368
3618
  }
3369
3619
  return { dependencies, settings };
3370
3620
  }, shouldIgnoreNativePath = (relativePath, ignorePublicBundle) => {
3371
- const parts = relativePath.split(sep);
3621
+ const parts = relativePath.split(sep2);
3372
3622
  if (parts.includes(".gradle") || parts.includes("build") || parts.includes(".absolutejs-dependencies")) {
3373
3623
  return true;
3374
3624
  }
@@ -3377,7 +3627,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
3377
3627
  const relativePath = relative4(root, path);
3378
3628
  if (shouldIgnoreNativePath(relativePath, ignorePublicBundle))
3379
3629
  return [];
3380
- const identity = `${label}:${relativePath.split(sep).join("/")}\x00`;
3630
+ const identity = `${label}:${relativePath.split(sep2).join("/")}\x00`;
3381
3631
  if (isDirectory) {
3382
3632
  return collectNativeDirectory(root, label, path, ignorePublicBundle);
3383
3633
  }
@@ -3434,7 +3684,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
3434
3684
  if ((manifestBackupPath !== undefined || nativeManifestPath !== undefined) && (typeof manifestBackupPath !== "string" || typeof nativeManifestPath !== "string")) {
3435
3685
  return null;
3436
3686
  }
3437
- if (projectedFiles !== undefined && (!Array.isArray(projectedFiles) || !projectedFiles.every((file) => isRecord(file) && typeof file.path === "string" && (file.backupPath === undefined || typeof file.backupPath === "string")))) {
3687
+ if (projectedFiles !== undefined && (!Array.isArray(projectedFiles) || !projectedFiles.every((file) => isRecord2(file) && typeof file.path === "string" && (file.backupPath === undefined || typeof file.backupPath === "string")))) {
3438
3688
  return null;
3439
3689
  }
3440
3690
  return {
@@ -3520,7 +3770,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
3520
3770
  const source = await readFile4(nativeConfigPath, "utf8");
3521
3771
  const manifestSource = await readFile4(nativeManifestPath, "utf8");
3522
3772
  const parsed = JSON.parse(source);
3523
- if (!isRecord(parsed)) {
3773
+ if (!isRecord2(parsed)) {
3524
3774
  throw new Error(`Invalid Capacitor native config at ${nativeConfigPath}.`);
3525
3775
  }
3526
3776
  await mkdir2(paths.root, { recursive: true });
@@ -4442,7 +4692,7 @@ import {
4442
4692
  stat,
4443
4693
  writeFile as writeFile4
4444
4694
  } from "fs/promises";
4445
- import { dirname as dirname6, isAbsolute as isAbsolute2, join as join13, relative as relative5, resolve as resolve9, sep as sep2 } from "path";
4695
+ import { dirname as dirname6, isAbsolute as isAbsolute2, join as join13, relative as relative5, resolve as resolve9, sep as sep3 } from "path";
4446
4696
  var developmentTeamArgument = (value) => {
4447
4697
  if (value === undefined)
4448
4698
  return;
@@ -4450,8 +4700,8 @@ var developmentTeamArgument = (value) => {
4450
4700
  if (!/^[A-Z0-9]{10}$/u.test(team))
4451
4701
  throw new TypeError("iOS development team must contain ten letters or digits.");
4452
4702
  return `DEVELOPMENT_TEAM=${team}`;
4453
- }, isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest = (value) => {
4454
- if (!isRecord2(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
4703
+ }, isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest = (value) => {
4704
+ if (!isRecord3(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
4455
4705
  throw new TypeError("Invalid embedded AbsoluteJS mobile manifest.");
4456
4706
  }
4457
4707
  return {
@@ -4524,7 +4774,7 @@ var developmentTeamArgument = (value) => {
4524
4774
  const root = resolve9(projectRoot);
4525
4775
  const output = resolve9(root, requested ?? ".absolutejs/mobile/releases/ios");
4526
4776
  const projectRelative = relative5(root, output);
4527
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep2}`) || isAbsolute2(projectRelative)) {
4777
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep3}`) || isAbsolute2(projectRelative)) {
4528
4778
  throw new TypeError("mobile build --outdir must remain inside the project.");
4529
4779
  }
4530
4780
  return output;
@@ -4560,7 +4810,7 @@ var developmentTeamArgument = (value) => {
4560
4810
  const destination = join13(releaseRoot, "App.ipa");
4561
4811
  if (await pathExists3(releaseRoot)) {
4562
4812
  const value = JSON.parse(await readFile6(join13(releaseRoot, "release.json"), "utf8"));
4563
- if (!isRecord2(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
4813
+ if (!isRecord3(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
4564
4814
  throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
4565
4815
  }
4566
4816
  const [bytes, sha256] = await Promise.all([
@@ -4720,8 +4970,8 @@ import {
4720
4970
  writeFile as writeFile5
4721
4971
  } from "fs/promises";
4722
4972
  import { isIP as isIP2 } from "net";
4723
- import { dirname as dirname7, isAbsolute as isAbsolute3, join as join14, relative as relative6, resolve as resolve10, sep as sep3 } from "path";
4724
- var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000, BOOT_POLL_MS = 1000, DEV_JOURNAL_FORMAT2 = 1, NATIVE_CACHE_FORMAT2 = 1, isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), pathExists4 = async (path) => {
4973
+ import { dirname as dirname7, isAbsolute as isAbsolute3, join as join14, relative as relative6, resolve as resolve10, sep as sep4 } from "path";
4974
+ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000, BOOT_POLL_MS = 1000, DEV_JOURNAL_FORMAT2 = 1, NATIVE_CACHE_FORMAT2 = 1, isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), pathExists4 = async (path) => {
4725
4975
  try {
4726
4976
  await access7(path);
4727
4977
  return true;
@@ -4855,7 +5105,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4855
5105
  }, parseJson = (source, label) => {
4856
5106
  try {
4857
5107
  const parsed = JSON.parse(source);
4858
- if (isRecord3(parsed))
5108
+ if (isRecord4(parsed))
4859
5109
  return parsed;
4860
5110
  } catch {}
4861
5111
  throw new Error(`Invalid ${label} JSON from simctl.`);
@@ -4865,7 +5115,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4865
5115
  if (!Array.isArray(types))
4866
5116
  return [];
4867
5117
  return types.flatMap((type) => {
4868
- if (!isRecord3(type))
5118
+ if (!isRecord4(type))
4869
5119
  return [];
4870
5120
  const { identifier } = type;
4871
5121
  const { name } = type;
@@ -4877,7 +5127,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4877
5127
  if (!Array.isArray(runtimes))
4878
5128
  return [];
4879
5129
  return runtimes.flatMap((runtime) => {
4880
- if (!isRecord3(runtime))
5130
+ if (!isRecord4(runtime))
4881
5131
  return [];
4882
5132
  const { identifier } = runtime;
4883
5133
  const { name } = runtime;
@@ -4896,13 +5146,13 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4896
5146
  }, parseIosSimulators = (source) => {
4897
5147
  const parsed = parseJson(source, "device");
4898
5148
  const { devices } = parsed;
4899
- if (!isRecord3(devices))
5149
+ if (!isRecord4(devices))
4900
5150
  return [];
4901
5151
  return Object.entries(devices).flatMap(([runtime, values]) => {
4902
5152
  if (!Array.isArray(values))
4903
5153
  return [];
4904
5154
  return values.flatMap((device) => {
4905
- if (!isRecord3(device))
5155
+ if (!isRecord4(device))
4906
5156
  return [];
4907
5157
  const { name } = device;
4908
5158
  const { state } = device;
@@ -4947,9 +5197,9 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4947
5197
  };
4948
5198
  }, nativeCachePath2 = (projectRoot) => join14(projectRoot, ".absolutejs", "mobile", "ios-native-cache.json"), isInside2 = (root, path) => {
4949
5199
  const value = relative6(resolve10(root), resolve10(path));
4950
- return value === "" || !value.startsWith(`..${sep3}`) && value !== ".." && !isAbsolute3(value);
5200
+ return value === "" || !value.startsWith(`..${sep4}`) && value !== ".." && !isAbsolute3(value);
4951
5201
  }, parseJournal2 = (value) => {
4952
- if (!isRecord3(value) || value.format !== DEV_JOURNAL_FORMAT2)
5202
+ if (!isRecord4(value) || value.format !== DEV_JOURNAL_FORMAT2)
4953
5203
  return null;
4954
5204
  const { configBackupPath } = value;
4955
5205
  const { infoBackupPath } = value;
@@ -5009,7 +5259,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
5009
5259
  readFile7(infoPath, "utf8")
5010
5260
  ]);
5011
5261
  const parsed = JSON.parse(configSource);
5012
- if (!isRecord3(parsed))
5262
+ if (!isRecord4(parsed))
5013
5263
  throw new Error(`Invalid Capacitor native config at ${nativeConfigPath}.`);
5014
5264
  await mkdir5(paths.root, { recursive: true });
5015
5265
  await Promise.all([
@@ -5032,7 +5282,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
5032
5282
  developmentUrl.searchParams.set("__absolute_target", "capacitor-ios");
5033
5283
  const existingServer = parsed.server;
5034
5284
  parsed.server = {
5035
- ...isRecord3(existingServer) ? existingServer : {},
5285
+ ...isRecord4(existingServer) ? existingServer : {},
5036
5286
  cleartext: !https,
5037
5287
  url: developmentUrl.href
5038
5288
  };
@@ -5042,10 +5292,10 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
5042
5292
  writeFile5(infoPath, iosDevelopmentInfoPlist(infoSource, !https))
5043
5293
  ]);
5044
5294
  }, parseNativeCache2 = (value) => {
5045
- if (!isRecord3(value))
5295
+ if (!isRecord4(value))
5046
5296
  return null;
5047
5297
  const { appId, fingerprint, format, installations } = value;
5048
- if (format !== NATIVE_CACHE_FORMAT2 || typeof appId !== "string" || typeof fingerprint !== "string" || !isRecord3(installations) || !Object.values(installations).every((identity) => typeof identity === "string"))
5298
+ if (format !== NATIVE_CACHE_FORMAT2 || typeof appId !== "string" || typeof fingerprint !== "string" || !isRecord4(installations) || !Object.values(installations).every((identity) => typeof identity === "string"))
5049
5299
  return null;
5050
5300
  return {
5051
5301
  appId,
@@ -5624,7 +5874,7 @@ import {
5624
5874
  posix,
5625
5875
  relative as relative7,
5626
5876
  resolve as resolvePath,
5627
- sep as sep4
5877
+ sep as sep5
5628
5878
  } from "path";
5629
5879
  var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join15(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
5630
5880
  format: PROFILE_FORMAT,
@@ -5884,7 +6134,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
5884
6134
  const bytes = await Bun.file(path).arrayBuffer();
5885
6135
  const sha256 = createHash6("sha256").update(new Uint8Array(bytes)).digest("hex");
5886
6136
  return { bytes: bytes.byteLength, path, sha256 };
5887
- }, portableRelativePath = (root, path) => relative7(root, path).split(sep4).join(posix.sep), portableMobileConfig = (project) => ({
6137
+ }, portableRelativePath = (root, path) => relative7(root, path).split(sep5).join(posix.sep), portableMobileConfig = (project) => ({
5888
6138
  appId: project.config.appId,
5889
6139
  appName: project.config.appName,
5890
6140
  bundleDirectory: portableRelativePath(project.projectRoot, project.config.bundleDirectory),
@@ -7300,23 +7550,23 @@ var stripStringsAndComments = (source) => {
7300
7550
  var toIslandFrameworkSegment = (framework) => framework[0]?.toUpperCase() + framework.slice(1), getIslandManifestKey = (framework, component2) => `Island${toIslandFrameworkSegment(framework)}${component2}`;
7301
7551
 
7302
7552
  // src/core/islands.ts
7303
- var isRecord4 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component2) => {
7553
+ var isRecord5 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component2) => {
7304
7554
  if (!isIslandComponentDefinition(component2))
7305
7555
  return null;
7306
7556
  return {
7307
7557
  export: component2.export,
7308
7558
  source: component2.source
7309
7559
  };
7310
- }, isIslandComponentDefinition = (value) => isRecord4(value) && ("component" in value) && ("source" in value) && typeof value.source === "string";
7560
+ }, isIslandComponentDefinition = (value) => isRecord5(value) && ("component" in value) && ("source" in value) && typeof value.source === "string";
7311
7561
  var init_islands = () => {};
7312
7562
 
7313
7563
  // src/build/islandEntries.ts
7314
7564
  import { dirname as dirname10, extname as extname2, join as join19, relative as relative10, resolve as resolve14 } from "path";
7315
7565
  import ts2 from "typescript";
7316
- var frameworks, isRecord5 = (value) => typeof value === "object" && value !== null, resolveRegistryExport = (mod) => {
7317
- if (isRecord5(mod.islandRegistry))
7566
+ var frameworks, isRecord6 = (value) => typeof value === "object" && value !== null, resolveRegistryExport = (mod) => {
7567
+ if (isRecord6(mod.islandRegistry))
7318
7568
  return mod.islandRegistry;
7319
- if (isRecord5(mod.default))
7569
+ if (isRecord6(mod.default))
7320
7570
  return mod.default;
7321
7571
  throw new Error("Island registry module must export `islandRegistry` or a default registry object.");
7322
7572
  }, hasSvelteImport = (source) => /from\s+['"][^'"]+\.svelte['"]/.test(source), resolveIslandSourcePath = (registryPath, sourcePath) => {
@@ -7466,7 +7716,7 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
7466
7716
  const registry2 = resolveRegistryExport(registryModule);
7467
7717
  const definitions = frameworks.flatMap((framework) => {
7468
7718
  const frameworkRegistry = registry2[framework];
7469
- if (!isRecord5(frameworkRegistry))
7719
+ if (!isRecord6(frameworkRegistry))
7470
7720
  return [];
7471
7721
  return Object.entries(frameworkRegistry).map(([component2, value]) => ({
7472
7722
  buildReference: getIslandBuildReference(value),
@@ -7476,7 +7726,7 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
7476
7726
  });
7477
7727
  return {
7478
7728
  definitions,
7479
- hasNamedExport: isRecord5(registryModule.islandRegistry),
7729
+ hasNamedExport: isRecord6(registryModule.islandRegistry),
7480
7730
  registry: registry2
7481
7731
  };
7482
7732
  }, loadIslandRegistryBuildInfo = async (registryPath) => {
@@ -7776,8 +8026,8 @@ var init_bunStringRawUnicodePlugin = __esm(() => {
7776
8026
  });
7777
8027
 
7778
8028
  // src/mobile/buildMetadata.ts
7779
- var ABSOLUTE_MOBILE_ROUTE_DETAIL = "x-absolute-mobile", frameworks2, isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPageFramework = (value) => typeof value === "string" && frameworks2.has(value), parseAbsoluteMobileBuildPageMetadata = (value) => {
7780
- if (!isRecord6(value))
8029
+ var ABSOLUTE_MOBILE_ROUTE_DETAIL = "x-absolute-mobile", frameworks2, isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPageFramework = (value) => typeof value === "string" && frameworks2.has(value), parseAbsoluteMobileBuildPageMetadata = (value) => {
8030
+ if (!isRecord7(value))
7781
8031
  return;
7782
8032
  if (typeof value.bundleKey !== "string" || typeof value.contract !== "string" || !isPageFramework(value.framework) || typeof value.pageId !== "string" || typeof value.propsSchemaHash !== "string") {
7783
8033
  return;
@@ -8597,14 +8847,14 @@ import {
8597
8847
  writeFile as writeFile9
8598
8848
  } from "fs/promises";
8599
8849
  import { dirname as dirname13, join as join22, resolve as resolvePath3 } from "path";
8600
- var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "current.json", BUNDLES_DIRECTORY = "bundles", ARTIFACT_FILE = "artifact.json", BUNDLE_ID_PATTERN, isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code, bundleIdFor = (currentReleaseId, releases) => {
8850
+ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "current.json", BUNDLES_DIRECTORY = "bundles", ARTIFACT_FILE = "artifact.json", BUNDLE_ID_PATTERN, isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code, bundleIdFor = (currentReleaseId, releases) => {
8601
8851
  const identity = JSON.stringify({
8602
8852
  currentReleaseId,
8603
8853
  releases: releases.map(({ releaseId }) => releaseId)
8604
8854
  });
8605
8855
  return `amb_${createHash11("sha256").update(identity).digest("hex")}`;
8606
8856
  }, parseBundleIndex = (value) => {
8607
- if (!isRecord7(value) || value.format !== ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT || typeof value.bundleId !== "string" || !BUNDLE_ID_PATTERN.test(value.bundleId) || typeof value.currentReleaseId !== "string" || !Array.isArray(value.releases)) {
8857
+ if (!isRecord8(value) || value.format !== ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT || typeof value.bundleId !== "string" || !BUNDLE_ID_PATTERN.test(value.bundleId) || typeof value.currentReleaseId !== "string" || !Array.isArray(value.releases)) {
8608
8858
  throw new TypeError("Invalid materialized mobile compatibility bundle.");
8609
8859
  }
8610
8860
  const releases = value.releases.map(parseAbsoluteMobileCompatibilityArtifact);
@@ -10910,8 +11160,8 @@ var DEFAULT_PROOF_LOCATION = ".absolutejs/lint-proof.json", PROOF_CONTRACT_VERSI
10910
11160
  `);
10911
11161
  renameSync2(temporary, path);
10912
11162
  return proof;
10913
- }, isRecord8 = (value) => value !== null && typeof value === "object", isLintProofAttestation = (value) => {
10914
- if (!isRecord8(value))
11163
+ }, isRecord9 = (value) => value !== null && typeof value === "object", isLintProofAttestation = (value) => {
11164
+ if (!isRecord9(value))
10915
11165
  return false;
10916
11166
  return Reflect.get(value, "algorithm") === "ed25519" && typeof Reflect.get(value, "keyId") === "string" && typeof Reflect.get(value, "signature") === "string";
10917
11167
  }, isLintProof = (value) => {
@@ -12298,7 +12548,7 @@ var init_mem = __esm(() => {
12298
12548
  });
12299
12549
 
12300
12550
  // src/cli/config/guards.ts
12301
- var isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12551
+ var isRecord10 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12302
12552
 
12303
12553
  // src/cli/config/schema/fromType.ts
12304
12554
  import ts6 from "typescript";
@@ -12361,7 +12611,7 @@ var VIRTUAL_NAME = "__absolute_type_introspect__.ts", MAX_DEPTH = 6, isFramework
12361
12611
  }, readDiskCache = (cwd, typeName, signature, specifier) => {
12362
12612
  try {
12363
12613
  const cached = JSON.parse(readFileSync21(cacheFile(cwd, typeName, specifier), "utf-8"));
12364
- if (isRecord9(cached) && cached.signature === signature && Array.isArray(cached.fields)) {
12614
+ if (isRecord10(cached) && cached.signature === signature && Array.isArray(cached.fields)) {
12365
12615
  return cached.fields;
12366
12616
  }
12367
12617
  } catch {}
@@ -12768,11 +13018,11 @@ var init_frameworks = __esm(() => {
12768
13018
 
12769
13019
  // src/cli/generate/context.ts
12770
13020
  import { dirname as dirname17, isAbsolute as isAbsolute5, join as join29, relative as relative16, resolve as resolve27 } from "path";
12771
- var asString = (value) => typeof value === "string" ? value : undefined, isRecord10 = (value) => typeof value === "object" && value !== null, resolveDir = (cwd, value) => isAbsolute5(value) ? value : resolve27(cwd, value), resolveStylesDir = (cwd, config) => {
13021
+ var asString = (value) => typeof value === "string" ? value : undefined, isRecord11 = (value) => typeof value === "object" && value !== null, resolveDir = (cwd, value) => isAbsolute5(value) ? value : resolve27(cwd, value), resolveStylesDir = (cwd, config) => {
12772
13022
  const styles = config.stylesConfig;
12773
13023
  if (typeof styles === "string")
12774
13024
  return resolveDir(cwd, styles);
12775
- if (isRecord10(styles)) {
13025
+ if (isRecord11(styles)) {
12776
13026
  const indexes = asString(styles.indexes);
12777
13027
  if (indexes)
12778
13028
  return resolveDir(cwd, indexes);
@@ -12783,7 +13033,7 @@ var asString = (value) => typeof value === "string" ? value : undefined, isRecor
12783
13033
  return dir ? dirname17(dir) : resolve27(project.cwd, "src/frontend");
12784
13034
  }, resolveProject = async (cwd, configOverride) => {
12785
13035
  const loaded = await loadConfig(configOverride);
12786
- const config = isRecord10(loaded) ? loaded : {};
13036
+ const config = isRecord11(loaded) ? loaded : {};
12787
13037
  const frameworkDirs = {};
12788
13038
  for (const key of FRAMEWORK_KEYS2) {
12789
13039
  const dir = asString(config[frameworks6[key].configDirKey]);
@@ -13763,7 +14013,7 @@ ${value.map((item) => `${pad}${serializeValue(item, level + 1, indent)}`).join(`
13763
14013
  `)}
13764
14014
  ${indent.repeat(level)}]`;
13765
14015
  }
13766
- if (isRecord9(value)) {
14016
+ if (isRecord10(value)) {
13767
14017
  const keys = Object.keys(value);
13768
14018
  if (keys.length === 0)
13769
14019
  return "{}";
@@ -13976,18 +14226,18 @@ var init_catalog = __esm(() => {
13976
14226
  // src/cli/integrations/addPlugin.ts
13977
14227
  import { existsSync as existsSync27, readFileSync as readFileSync27 } from "fs";
13978
14228
  import { join as join35 } from "path";
13979
- var isRecord11 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readPackageJson = (cwd) => {
14229
+ var isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readPackageJson = (cwd) => {
13980
14230
  const path = join35(cwd, "package.json");
13981
14231
  if (!existsSync27(path))
13982
14232
  return null;
13983
14233
  try {
13984
14234
  const parsed = JSON.parse(readFileSync27(path, "utf-8"));
13985
- return isRecord11(parsed) ? parsed : null;
14235
+ return isRecord12(parsed) ? parsed : null;
13986
14236
  } catch {
13987
14237
  return null;
13988
14238
  }
13989
14239
  }, addGroupKeys = (group, names) => {
13990
- if (!isRecord11(group))
14240
+ if (!isRecord12(group))
13991
14241
  return;
13992
14242
  for (const name of Object.keys(group))
13993
14243
  names.add(name);
@@ -14580,12 +14830,12 @@ var init_resolveAuthSettings = __esm(() => {
14580
14830
  import ts13 from "typescript";
14581
14831
  import { existsSync as existsSync29, readdirSync as readdirSync6, readFileSync as readFileSync29 } from "fs";
14582
14832
  import { join as join36, relative as relative19, resolve as resolve29 } from "path";
14583
- var AUTH_PACKAGE2 = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs/absolute-auth", NPM_URL = "https://www.npmjs.com/package/@absolutejs/auth", SKIP_DIRS, MAX_FILES = 4000, SETUP_EXPORTS, isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson2 = (path) => {
14833
+ var AUTH_PACKAGE2 = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs/absolute-auth", NPM_URL = "https://www.npmjs.com/package/@absolutejs/auth", SKIP_DIRS, MAX_FILES = 4000, SETUP_EXPORTS, isRecord13 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson2 = (path) => {
14584
14834
  if (!existsSync29(path))
14585
14835
  return null;
14586
14836
  try {
14587
14837
  const parsed = JSON.parse(readFileSync29(path, "utf-8"));
14588
- return isRecord12(parsed) ? parsed : null;
14838
+ return isRecord13(parsed) ? parsed : null;
14589
14839
  } catch {
14590
14840
  return null;
14591
14841
  }
@@ -14598,7 +14848,7 @@ var AUTH_PACKAGE2 = "@absolutejs/auth", REPO_URL = "https://github.com/absolutej
14598
14848
  return null;
14599
14849
  for (const field of ["dependencies", "devDependencies"]) {
14600
14850
  const group = pkg[field];
14601
- if (!isRecord12(group))
14851
+ if (!isRecord13(group))
14602
14852
  continue;
14603
14853
  const version2 = group[AUTH_PACKAGE2];
14604
14854
  if (typeof version2 === "string")
@@ -15887,17 +16137,17 @@ import {
15887
16137
  writeFileSync as writeFileSync19
15888
16138
  } from "fs";
15889
16139
  import { createRequire } from "module";
15890
- import { dirname as dirname25, join as join43, resolve as resolve32, sep as sep5 } from "path";
16140
+ import { dirname as dirname25, join as join43, resolve as resolve32, sep as sep6 } from "path";
15891
16141
  var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
15892
16142
  try {
15893
16143
  const parsed = JSON.parse(readFileSync35(path, "utf-8"));
15894
- return isRecord9(parsed) ? parsed : null;
16144
+ return isRecord10(parsed) ? parsed : null;
15895
16145
  } catch {
15896
16146
  return null;
15897
16147
  }
15898
16148
  }, dependencyRecord = (manifest, field) => {
15899
16149
  const value = Reflect.get(manifest, field);
15900
- return isRecord9(value) ? value : {};
16150
+ return isRecord10(value) ? value : {};
15901
16151
  }, dependencyNames2 = (manifest) => [
15902
16152
  ...new Set(DEPENDENCY_FIELDS.flatMap((field) => Object.keys(dependencyRecord(manifest, field))))
15903
16153
  ], declaresPackage = (manifest, name) => DEPENDENCY_FIELDS.some((field) => Object.hasOwn(dependencyRecord(manifest, field), name)), manifestName = (manifest, fallback) => {
@@ -16028,7 +16278,7 @@ var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
16028
16278
  if (!manifest)
16029
16279
  return [];
16030
16280
  const existing = Reflect.get(manifest, "overrides");
16031
- const overrides = isRecord9(existing) ? existing : {};
16281
+ const overrides = isRecord10(existing) ? existing : {};
16032
16282
  const changes = [];
16033
16283
  const rootName = manifestName(manifest, "<workspace>");
16034
16284
  for (const duplicate of duplicates) {
@@ -16047,8 +16297,8 @@ var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
16047
16297
  }, removeDuplicateTypeGraphPackages = (report) => {
16048
16298
  const manifest = readManifest(join43(report.installRoot, "package.json")) ?? {};
16049
16299
  const rootName = manifestName(manifest, "<workspace>");
16050
- const installPrefix = `${realpathSync2(report.installRoot)}${sep5}`;
16051
- const nodeModulesSegment = `${sep5}node_modules${sep5}`;
16300
+ const installPrefix = `${realpathSync2(report.installRoot)}${sep6}`;
16301
+ const nodeModulesSegment = `${sep6}node_modules${sep6}`;
16052
16302
  const removed = [];
16053
16303
  const stalePaths = duplicateTypeGraphPackages(report).flatMap((duplicate) => {
16054
16304
  const selected = preferredIdentity(duplicate, rootName);
@@ -16975,7 +17225,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
16975
17225
  if (!encoded)
16976
17226
  return;
16977
17227
  const map = JSON.parse(Buffer.from(encoded, "base64").toString("utf-8"));
16978
- if (!isRecord9(map))
17228
+ if (!isRecord10(map))
16979
17229
  return;
16980
17230
  if (!Array.isArray(map.sources))
16981
17231
  return;
@@ -17226,7 +17476,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
17226
17476
  }, pickExportEntry = (value) => {
17227
17477
  if (typeof value === "string")
17228
17478
  return value;
17229
- if (!isRecord9(value))
17479
+ if (!isRecord10(value))
17230
17480
  return;
17231
17481
  for (const key of ["bun", "node", "import", "module", "default"]) {
17232
17482
  const entry = pickExportEntry(value[key]);
@@ -19916,9 +20166,9 @@ import {
19916
20166
  stat as stat3,
19917
20167
  writeFile as writeFile15
19918
20168
  } from "fs/promises";
19919
- import { dirname as dirname31, isAbsolute as isAbsolute7, join as join53, relative as relative27, resolve as resolve40, sep as sep6 } from "path";
19920
- var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest2 = (value) => {
19921
- if (!isRecord13(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
20169
+ import { dirname as dirname31, isAbsolute as isAbsolute7, join as join53, relative as relative27, resolve as resolve40, sep as sep7 } from "path";
20170
+ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord14 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest2 = (value) => {
20171
+ if (!isRecord14(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
19922
20172
  throw new TypeError("Invalid embedded AbsoluteJS mobile manifest.");
19923
20173
  }
19924
20174
  return {
@@ -19984,7 +20234,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
19984
20234
  const root = resolve40(projectRoot);
19985
20235
  const output = resolve40(root, requested ?? ".absolutejs/mobile/releases/android");
19986
20236
  const projectRelative = relative27(root, output);
19987
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep6}`) || isAbsolute7(projectRelative)) {
20237
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep7}`) || isAbsolute7(projectRelative)) {
19988
20238
  throw new TypeError("mobile build --outdir must remain inside the project.");
19989
20239
  }
19990
20240
  return output;
@@ -20021,7 +20271,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
20021
20271
  });
20022
20272
  }
20023
20273
  }, requireManifestIdentity = (value, expected) => {
20024
- if (!isRecord13(value)) {
20274
+ if (!isRecord14(value)) {
20025
20275
  throw new TypeError("Existing Android release metadata is invalid.");
20026
20276
  }
20027
20277
  const { artifact } = value;
@@ -20570,7 +20820,7 @@ var init_androidTestReport = __esm(() => {
20570
20820
 
20571
20821
  // src/mobile/releasePublisher.ts
20572
20822
  import { access as access13 } from "fs/promises";
20573
- import { isAbsolute as isAbsolute8, relative as relative28, resolve as resolve41, sep as sep7 } from "path";
20823
+ import { isAbsolute as isAbsolute8, relative as relative28, resolve as resolve41, sep as sep8 } from "path";
20574
20824
  import { pathToFileURL as pathToFileURL2 } from "url";
20575
20825
  var prepareAbsoluteIosRelease = async (publisher, options) => {
20576
20826
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -20591,11 +20841,11 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
20591
20841
  throw new TypeError("Google Play publisher returned an invalid Android versionCode.");
20592
20842
  }
20593
20843
  return versionCode;
20594
- }, isRecord14 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPublisher = (value) => isRecord14(value) && typeof value.publish === "function", publisherModulePath = (projectRoot, requested) => {
20844
+ }, isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPublisher = (value) => isRecord15(value) && typeof value.publish === "function", publisherModulePath = (projectRoot, requested) => {
20595
20845
  const root = resolve41(projectRoot);
20596
20846
  const path = resolve41(root, requested);
20597
20847
  const projectRelative = relative28(root, path);
20598
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep7}`) || isAbsolute8(projectRelative)) {
20848
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep8}`) || isAbsolute8(projectRelative)) {
20599
20849
  throw new TypeError("mobile publish --registry must remain inside the project.");
20600
20850
  }
20601
20851
  return path;
@@ -20605,7 +20855,7 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
20605
20855
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
20606
20856
  });
20607
20857
  const loaded = await import(pathToFileURL2(modulePath).href);
20608
- const publisher = isRecord14(loaded) ? loaded.default ?? loaded.registry : undefined;
20858
+ const publisher = isRecord15(loaded) ? loaded.default ?? loaded.registry : undefined;
20609
20859
  if (!isPublisher(publisher)) {
20610
20860
  throw new TypeError("Native release registry module must default-export a registry with publish(options).");
20611
20861
  }
@@ -20812,7 +21062,7 @@ var init_mobileInspect = __esm(() => {
20812
21062
  // src/mobile/ciWorkflow.ts
20813
21063
  import { existsSync as existsSync43 } from "fs";
20814
21064
  import { access as access15, mkdir as mkdir15, readFile as readFile23, writeFile as writeFile17 } from "fs/promises";
20815
- import { dirname as dirname32, extname as extname9, relative as relative30, resolve as resolve43, sep as sep8 } from "path";
21065
+ import { dirname as dirname32, extname as extname9, relative as relative30, resolve as resolve43, sep as sep9 } from "path";
20816
21066
  var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1, SECRET_NAME_PATTERN, CI_ENV_INDENTATION = 6, RESERVED_SECRET_NAMES, exists4 = async (path) => {
20817
21067
  try {
20818
21068
  await access15(path);
@@ -20824,7 +21074,7 @@ var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1, SECRET_NAME_PATTERN, CI_ENV_INDENTAT
20824
21074
  const root = resolve43(projectRoot);
20825
21075
  const path = resolve43(root, value);
20826
21076
  const portable = relative30(root, path).replaceAll("\\", "/");
20827
- if (portable === ".." || portable.startsWith(`..${sep8}`) || portable.startsWith("../") || portable === "") {
21077
+ if (portable === ".." || portable.startsWith(`..${sep9}`) || portable.startsWith("../") || portable === "") {
20828
21078
  throw new TypeError(`${field} must remain inside the project root.`);
20829
21079
  }
20830
21080
  if (/\r|\n/u.test(portable) || portable.startsWith("-"))
@@ -20837,7 +21087,7 @@ var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1, SECRET_NAME_PATTERN, CI_ENV_INDENTAT
20837
21087
  const workflows = resolve43(root, ".github/workflows");
20838
21088
  const path = resolve43(root, value ?? ".github/workflows/absolute-mobile.yml");
20839
21089
  const portable = relative30(workflows, path);
20840
- if (portable === ".." || portable.startsWith(`..${sep8}`) || extname9(path) !== ".yml" && extname9(path) !== ".yaml") {
21090
+ if (portable === ".." || portable.startsWith(`..${sep9}`) || extname9(path) !== ".yml" && extname9(path) !== ".yaml") {
20841
21091
  throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
20842
21092
  }
20843
21093
  return path;
@@ -21295,14 +21545,14 @@ __export(exports_mobile, {
21295
21545
  import { access as access16, mkdir as mkdir16, readFile as readFile24, writeFile as writeFile18 } from "fs/promises";
21296
21546
  import { join as join56, relative as relative31, resolve as resolve44 } from "path";
21297
21547
  import { createInterface } from "readline/promises";
21298
- var NOT_FOUND4 = -1, isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
21548
+ var NOT_FOUND4 = -1, isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
21299
21549
  const manifest = JSON.parse(await readFile24(join56(projectRoot, "package.json"), "utf8"));
21300
- if (!isRecord15(manifest))
21550
+ if (!isRecord16(manifest))
21301
21551
  throw new TypeError("Application package.json must contain an object.");
21302
21552
  const names = new Set;
21303
21553
  for (const field of ["dependencies", "devDependencies"]) {
21304
21554
  const dependencies = Reflect.get(manifest, field);
21305
- if (isRecord15(dependencies))
21555
+ if (isRecord16(dependencies))
21306
21556
  for (const name of Object.keys(dependencies))
21307
21557
  names.add(name);
21308
21558
  }
@@ -21310,7 +21560,7 @@ var NOT_FOUND4 = -1, isRecord15 = (value) => typeof value === "object" && value
21310
21560
  }, resolvedPackageVersion = async (projectRoot, packageName) => {
21311
21561
  try {
21312
21562
  const manifest = JSON.parse(await readFile24(join56(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
21313
- return isRecord15(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
21563
+ return isRecord16(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
21314
21564
  } catch {
21315
21565
  return;
21316
21566
  }
@@ -22403,7 +22653,7 @@ Emulator setup verification:`);
22403
22653
  return;
22404
22654
  });
22405
22655
  const status2 = response?.ok ? await response.json().catch(() => null) : null;
22406
- const targets = isRecord15(status2) && isRecord15(status2.connectedTargets) ? status2.connectedTargets : undefined;
22656
+ const targets = isRecord16(status2) && isRecord16(status2.connectedTargets) ? status2.connectedTargets : undefined;
22407
22657
  if (targets && typeof targets["capacitor-ios"] === "number" && targets["capacitor-ios"] > 0)
22408
22658
  return;
22409
22659
  await Bun.sleep(100);