@absolutejs/absolute 0.20.0-beta.44 → 0.20.0-beta.45

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
@@ -850,7 +850,7 @@ var init_config = __esm(() => {
850
850
  // src/mobile/nativeAuth.ts
851
851
  import { readFileSync as readFileSync5 } from "fs";
852
852
  import { join as join5 } from "path";
853
- var ABSOLUTE_AUTH_PACKAGE = "@absolutejs/auth", ABSOLUTE_EXPO_AUTH_CORE_VERSION = "0.75.6", ABSOLUTE_EXPO_AUTH_PACKAGE = "@absolutejs/auth-expo", ABSOLUTE_EXPO_AUTH_VERSION = "0.0.2", ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV = "ABSOLUTE_AUTH_NATIVE_CLIENTS", ABSOLUTE_NATIVE_AUTH_SCOPES, ABSOLUTE_SYNC_PACKAGE = "@absolutejs/sync", readPackageManifest = (projectRoot) => {
853
+ var ABSOLUTE_AUTH_PACKAGE = "@absolutejs/auth", ABSOLUTE_EXPO_AUTH_CORE_VERSION = "0.75.6", ABSOLUTE_EXPO_AUTH_PACKAGE = "@absolutejs/auth-expo", ABSOLUTE_EXPO_AUTH_VERSION = "0.0.2", ABSOLUTE_EXPO_SYNC_CORE_VERSION = "2.31.0", ABSOLUTE_EXPO_SYNC_PACKAGE = "@absolutejs/sync-expo", ABSOLUTE_EXPO_SYNC_VERSION = "0.0.2", ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV = "ABSOLUTE_AUTH_NATIVE_CLIENTS", ABSOLUTE_NATIVE_AUTH_SCOPES, ABSOLUTE_SYNC_PACKAGE = "@absolutejs/sync", readPackageManifest = (projectRoot) => {
854
854
  try {
855
855
  return JSON.parse(readFileSync5(join5(projectRoot, "package.json"), "utf8"));
856
856
  } catch {
@@ -891,162 +891,699 @@ var init_nativeAuth = __esm(() => {
891
891
  ABSOLUTE_NATIVE_AUTH_SCOPES = ["openid", "profile"];
892
892
  });
893
893
 
894
- // src/mobile/expoProject.ts
895
- import {
896
- access,
897
- cp,
898
- mkdir,
899
- mkdtemp,
900
- readdir,
901
- readFile,
902
- rename,
903
- rm,
904
- writeFile
905
- } from "fs/promises";
906
- import { createHash } from "crypto";
907
- import { basename as basename2, dirname as dirname3, join as join6, relative, resolve as resolve3 } from "path";
908
- var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
909
- `, EXPO_ASSET_EXTENSION = ".absasset", EXPO_PROJECT_MARKER = ".absolutejs-expo-project", exists = async (path) => {
910
- try {
911
- await access(path);
912
- return true;
913
- } catch {
914
- return false;
894
+ // node_modules/@absolutejs/sync/dist/client/index.js
895
+ var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations")) && Array.isArray(Reflect.get(value, "clients")), registry, pools, SyncLocalDataPolicyError, SyncLocalStoreSchemaError, positiveVersion = (value, label) => {
896
+ if (!Number.isSafeInteger(value) || value < 1)
897
+ throw new SyncLocalStoreSchemaError("INVALID_PLAN", `${label} must be a positive safe integer`);
898
+ return value;
899
+ }, isSchemaBundle = (schema) => ("components" in schema), validatePolicyMatch = (match, label) => {
900
+ if (match.length === 0 || match.trim() !== match || /^\*+$/.test(match) || match.includes("**"))
901
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.match must be an exact name or a non-empty glob without adjacent wildcards.`);
902
+ }, validateSyncLocalDataPolicy = (policy, label = "localData") => {
903
+ if (policy.maxBytesPerNamespace !== undefined && (!Number.isSafeInteger(policy.maxBytesPerNamespace) || policy.maxBytesPerNamespace < 1))
904
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.maxBytesPerNamespace must be a positive safe integer.`);
905
+ for (const [index, rule] of (policy.collections ?? []).entries()) {
906
+ validatePolicyMatch(rule.match, `${label}.collections[${index}]`);
907
+ if (rule.maxAgeMs !== undefined && (!Number.isSafeInteger(rule.maxAgeMs) || rule.maxAgeMs < 1))
908
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}].maxAgeMs must be a positive safe integer.`);
909
+ if (rule.persistence === "memory-only" && rule.protection === "required")
910
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] cannot require at-rest protection when it is memory-only.`);
911
+ if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
912
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] declares ${rule.sensitivity} data without required protection or memory-only persistence.`);
915
913
  }
916
- }, portableRelative = (from, destination) => {
917
- const value = relative(from, destination).replaceAll("\\", "/");
918
- return value.startsWith(".") ? value : `./${value}`;
919
- }, routeSegments = (route) => route.split("/").filter(Boolean).map((segment) => {
920
- if (segment.startsWith(":"))
921
- return `[${segment.slice(1)}]`;
922
- if (segment === "." || segment === ".." || !/^[A-Za-z0-9._~-]+$/u.test(segment)) {
923
- throw new TypeError(`Expo native route ${route} contains an unsupported segment ${segment}.`);
914
+ for (const [index, rule] of (policy.mutations ?? []).entries()) {
915
+ validatePolicyMatch(rule.match, `${label}.mutations[${index}]`);
916
+ if (rule.conflict !== undefined && rule.conflict.strategy !== "client-wins" && rule.conflict.strategy !== "manual" && rule.conflict.strategy !== "server-wins")
917
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.strategy is invalid.`);
918
+ if (rule.conflict?.maxAttempts !== undefined && (!Number.isSafeInteger(rule.conflict.maxAttempts) || rule.conflict.maxAttempts < 1))
919
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts must be a positive safe integer.`);
920
+ if (rule.conflict?.maxAttempts !== undefined && rule.conflict.strategy !== "client-wins")
921
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts is only valid for client-wins.`);
922
+ if (rule.persistence === "memory-only" && rule.protection === "required")
923
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] cannot require at-rest protection when it is memory-only.`);
924
+ if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
925
+ throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] declares ${rule.sensitivity} arguments without required protection.`);
924
926
  }
925
- return segment;
926
- }), routeFile = (project, route) => join6(project, "app", ...routeSegments(route), "index.tsx"), expoPackage = (auth) => ({
927
- dependencies: {
928
- ...auth ? {
929
- "@absolutejs/auth": ABSOLUTE_EXPO_AUTH_CORE_VERSION,
930
- [ABSOLUTE_EXPO_AUTH_PACKAGE]: ABSOLUTE_EXPO_AUTH_VERSION
931
- } : {},
932
- expo: "~57.0.9",
933
- "expo-asset": "~57.0.15",
934
- "expo-constants": "~57.0.16",
935
- "expo-dev-client": "~57.0.16",
936
- "expo-file-system": "~57.0.6",
937
- "expo-haptics": "~57.0.2",
938
- "expo-linking": "~57.0.8",
939
- "expo-router": "~57.0.17",
940
- ...auth ? {
941
- "expo-secure-store": "~57.0.2",
942
- "expo-web-browser": "~57.0.2"
943
- } : {},
944
- react: "19.2.3",
945
- "react-native": "0.86.3",
946
- "react-native-safe-area-context": "~5.7.0",
947
- "react-native-screens": "4.26.0",
948
- "react-native-webview": "13.16.1"
949
- },
950
- devDependencies: {
951
- "@types/react": "~19.2.2",
952
- typescript: "~6.0.3"
953
- },
954
- main: "expo-router/entry",
955
- name: "absolutejs-expo-shell",
956
- private: true,
957
- scripts: {
958
- android: "expo run:android",
959
- ios: "expo run:ios",
960
- start: "expo start --dev-client"
961
- },
962
- version: "0.0.0"
963
- }), expoAppConfig = (config, auth) => ({
964
- expo: {
965
- android: {
966
- intentFilters: config.deepLinkHosts.map((host) => ({
967
- action: "VIEW",
968
- autoVerify: true,
969
- category: ["BROWSABLE", "DEFAULT"],
970
- data: [{ host, pathPrefix: "/", scheme: "https" }]
971
- })),
972
- package: config.appId
973
- },
974
- experiments: { typedRoutes: true },
975
- ios: {
976
- associatedDomains: config.deepLinkHosts.map((host) => `applinks:${host}`),
977
- bundleIdentifier: config.appId,
978
- ...config.iosVersion ? { buildNumber: config.iosVersion } : {}
979
- },
980
- name: config.appName,
981
- plugins: [
982
- "expo-router",
983
- ["expo-dev-client", { launchMode: "most-recent" }],
984
- ...auth ? ["expo-secure-store"] : []
985
- ],
986
- runtimeVersion: { policy: "appVersion" },
987
- scheme: config.deepLinkScheme,
988
- slug: config.appId.toLowerCase().replaceAll(".", "-"),
989
- version: config.iosVersion ?? "0.1.0"
927
+ return policy;
928
+ }, normalizeSyncLocalSchemaComponents = (schema = { version: 1 }) => {
929
+ const components = isSchemaBundle(schema) ? [...schema.components] : [{ ...schema, id: "@absolutejs/app" }];
930
+ const ids = new Set;
931
+ for (const component of components) {
932
+ if (typeof component.id !== "string" || component.id.trim() !== component.id || component.id.length === 0)
933
+ throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Sync schema component id must be non-empty and trimmed");
934
+ if (ids.has(component.id))
935
+ throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync schema component "${component.id}" is declared more than once`);
936
+ ids.add(component.id);
937
+ if (component.localData)
938
+ validateSyncLocalDataPolicy(component.localData, `${component.id}.localData`);
990
939
  }
991
- }), expoDynamicAppConfig, expoDevelopmentCaPlugin, metroConfig = (projectRoot) => `${EXPO_GENERATED_HEADER}const { getDefaultConfig } = require('expo/metro-config');
992
- const path = require('node:path');
993
-
994
- const projectRoot = __dirname;
995
- const appRoot = ${JSON.stringify(projectRoot)};
996
- const config = getDefaultConfig(projectRoot);
997
- config.resolver.assetExts.push('absasset');
998
- config.resolver.nodeModulesPaths = [
999
- path.join(projectRoot, 'node_modules'),
1000
- path.join(appRoot, 'node_modules')
1001
- ];
1002
- config.watchFolders = [appRoot];
1003
-
1004
- module.exports = config;
1005
- `, layoutSource = (auth) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
1006
- ${auth ? `import { useEffect } from 'react';
1007
- import { startAbsoluteExpoAuth } from '../src/generated/AbsoluteAuth';` : ""}
1008
-
1009
- export default function AbsoluteLayout() {
1010
- ${auth ? `useEffect(() => {
1011
- void startAbsoluteExpoAuth();
1012
- }, []);` : ""}
1013
- return <Stack screenOptions={{ headerShown: false }} />;
1014
- }
1015
- `, nativeDiagnosticSource, authRuntimeSource = (auth, appId) => {
1016
- const storageIdentity = createHash("sha256").update(appId).digest("hex").slice(0, 24);
1017
- return `${EXPO_GENERATED_HEADER}import { createAbsoluteExpoAuthClient } from '@absolutejs/auth-expo';
1018
- import { createMobileAuthTransport, installAuthClientRuntimeTransport } from '@absolutejs/auth/client/mobile';
1019
-
1020
- export const absoluteExpoAuth = createAbsoluteExpoAuthClient({
1021
- allowedOrigins: [${JSON.stringify(auth.issuer)}],
1022
- clientId: ${JSON.stringify(auth.clientId)},
1023
- issuer: ${JSON.stringify(auth.issuer)},
1024
- redirectUri: ${JSON.stringify(auth.redirectUri)},
1025
- resource: ${JSON.stringify(auth.issuer)},
1026
- scopes: ${JSON.stringify(auth.scopes)},
1027
- storagePrefix: ${JSON.stringify(`absolutejs.auth.${storageIdentity}`)}
1028
- });
1029
- installAuthClientRuntimeTransport(createMobileAuthTransport(absoluteExpoAuth, { baseUrl: ${JSON.stringify(auth.issuer)} }));
1030
-
1031
- let currentPrincipal: Awaited<ReturnType<typeof absoluteExpoAuth.principal>> = null;
1032
- absoluteExpoAuth.onPrincipalChange(principal => {
1033
- currentPrincipal = principal;
940
+ return components.sort((a, b) => a.id.localeCompare(b.id));
941
+ }, resolveSyncLocalSchemaComponents = (storedVersions, schema = { version: 1 }) => {
942
+ const components = normalizeSyncLocalSchemaComponents(schema).map((component) => {
943
+ const current = resolveSyncLocalMigrations(component.version, component);
944
+ return {
945
+ id: component.id,
946
+ ...resolveSyncLocalMigrations(storedVersions[component.id] ?? current.minimumCompatibleVersion, component)
947
+ };
948
+ });
949
+ const active = new Set(components.map((component) => component.id));
950
+ const orphanedComponents = Object.keys(storedVersions).filter((id) => !active.has(id)).sort();
951
+ return { components, orphanedComponents };
952
+ }, resolveSyncLocalMigrations = (storedVersion, schema = { version: 1 }) => {
953
+ positiveVersion(storedVersion, "Stored Sync schema version");
954
+ const targetVersion = positiveVersion(schema.version, "Target Sync schema version");
955
+ const migrations = [...schema.migrations ?? []].sort((a, b) => a.toVersion - b.toVersion);
956
+ const versions = new Set;
957
+ for (const migration of migrations) {
958
+ positiveVersion(migration.toVersion, "Sync migration toVersion");
959
+ if (versions.has(migration.toVersion))
960
+ throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync migration ${migration.toVersion} is declared more than once`);
961
+ versions.add(migration.toVersion);
962
+ }
963
+ const inferredMinimum = migrations[0] ? migrations[0].toVersion - 1 : targetVersion;
964
+ const minimumCompatibleVersion = positiveVersion(schema.minimumCompatibleVersion ?? inferredMinimum, "Minimum compatible Sync schema version");
965
+ if (minimumCompatibleVersion > targetVersion)
966
+ throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Minimum compatible Sync schema version cannot exceed its target");
967
+ if (storedVersion > targetVersion)
968
+ throw new SyncLocalStoreSchemaError("SCHEMA_TOO_NEW", `Stored Sync schema ${storedVersion} is newer than this runtime's schema ${targetVersion}`, { storedVersion, targetVersion });
969
+ if (storedVersion < minimumCompatibleVersion)
970
+ throw new SyncLocalStoreSchemaError("SCHEMA_TOO_OLD", `Stored Sync schema ${storedVersion} is older than the minimum compatible schema ${minimumCompatibleVersion}`, { storedVersion, targetVersion });
971
+ const steps = [];
972
+ for (let version = storedVersion + 1;version <= targetVersion; version++) {
973
+ const migration = migrations.find((candidate) => candidate.toVersion === version);
974
+ if (migration === undefined)
975
+ throw new SyncLocalStoreSchemaError("MIGRATION_MISSING", `Sync migration ${version - 1} -> ${version} is missing`, { storedVersion, targetVersion });
976
+ steps.push(migration);
977
+ }
978
+ return { minimumCompatibleVersion, steps, targetVersion };
979
+ };
980
+ var init_client = __esm(() => {
981
+ RUNTIME_TRANSPORT = Symbol.for("@absolutejs/sync/client-runtime-transport");
982
+ host = globalThis;
983
+ registry = (() => {
984
+ const existing = host[RUNTIME_TRANSPORT];
985
+ if (isRegistry(existing))
986
+ return existing;
987
+ if (typeof existing === "object" && existing !== null && Array.isArray(Reflect.get(existing, "installations"))) {
988
+ Reflect.set(existing, "clients", []);
989
+ return existing;
990
+ }
991
+ const created = { clients: [], installations: [] };
992
+ Object.defineProperty(host, RUNTIME_TRANSPORT, {
993
+ configurable: false,
994
+ enumerable: false,
995
+ value: created,
996
+ writable: false
997
+ });
998
+ return created;
999
+ })();
1000
+ pools = new Map;
1001
+ SyncLocalDataPolicyError = class SyncLocalDataPolicyError extends Error {
1002
+ code;
1003
+ constructor(code, message) {
1004
+ super(message);
1005
+ this.name = "SyncLocalDataPolicyError";
1006
+ this.code = code;
1007
+ }
1008
+ };
1009
+ SyncLocalStoreSchemaError = class SyncLocalStoreSchemaError extends Error {
1010
+ code;
1011
+ storedVersion;
1012
+ targetVersion;
1013
+ constructor(code, message, versions = {}) {
1014
+ super(message);
1015
+ this.name = "SyncLocalStoreSchemaError";
1016
+ this.code = code;
1017
+ this.storedVersion = versions.storedVersion;
1018
+ this.targetVersion = versions.targetVersion;
1019
+ }
1020
+ };
1034
1021
  });
1035
- export const getAbsoluteExpoAuthPrincipal = () => currentPrincipal;
1036
-
1037
- let startPromise: Promise<void> | undefined;
1038
- export const startAbsoluteExpoAuth = () => {
1039
- startPromise ??= absoluteExpoAuth.start().then(async () => {
1040
- await absoluteExpoAuth.principal();
1041
- });
1042
1022
 
1043
- return startPromise;
1044
- };
1045
- `;
1046
- }, webHostSource = (config, auth) => {
1047
- const nativeRoutes = [
1048
- "/__absolute/native",
1049
- ...Object.keys(config.expoNativeRoutes)
1023
+ // src/mobile/syncSchema.ts
1024
+ import { readFileSync as readFileSync6 } from "fs";
1025
+ import { dirname as dirname3, join as join6, resolve as resolve3 } from "path";
1026
+ var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value), manifestAt = (path) => {
1027
+ try {
1028
+ const value = JSON.parse(readFileSync6(path, "utf8"));
1029
+ return object(value) ? value : undefined;
1030
+ } catch {
1031
+ return;
1032
+ }
1033
+ }, localSchemaMetadata = (manifest) => {
1034
+ const absolutejs = Reflect.get(manifest, "absolutejs");
1035
+ if (!object(absolutejs))
1036
+ return;
1037
+ const sync = Reflect.get(absolutejs, "sync");
1038
+ if (!object(sync))
1039
+ return;
1040
+ return Reflect.get(sync, "localSchema");
1041
+ }, packageManifestPath = (projectRoot, packageName) => {
1042
+ let directory = resolve3(projectRoot);
1043
+ while (true) {
1044
+ const candidate = join6(directory, "node_modules", packageName, "package.json");
1045
+ const manifest = manifestAt(candidate);
1046
+ if (manifest && Reflect.get(manifest, "name") === packageName)
1047
+ return candidate;
1048
+ const parent = dirname3(directory);
1049
+ if (parent === directory)
1050
+ return;
1051
+ directory = parent;
1052
+ }
1053
+ }, metadataError = (id, detail) => new TypeError(`Invalid AbsoluteJS Sync schema metadata for ${id}: ${detail}`), positiveVersion2 = (value, id, field) => {
1054
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
1055
+ throw metadataError(id, `${field} must be a positive safe integer.`);
1056
+ return value;
1057
+ }, nonEmpty = (value, id, field) => {
1058
+ if (typeof value !== "string" || value.trim() !== value || value.length === 0)
1059
+ throw metadataError(id, `${field} must be a non-empty trimmed string.`);
1060
+ return value;
1061
+ }, requireObject = (value, id, detail) => {
1062
+ if (!object(value))
1063
+ throw metadataError(id, detail);
1064
+ return value;
1065
+ }, unknownField = (record, key) => record[key], normalizeJsonValue = (value, id, field) => {
1066
+ if (value === null || typeof value === "string" || typeof value === "boolean")
1067
+ return value;
1068
+ if (typeof value === "number" && Number.isFinite(value))
1069
+ return value;
1070
+ if (Array.isArray(value))
1071
+ return value.map((entry) => normalizeJsonValue(entry, id, field));
1072
+ if (object(value))
1073
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
1074
+ key,
1075
+ normalizeJsonValue(entry, id, field)
1076
+ ]));
1077
+ throw metadataError(id, `${field} must be JSON-safe.`);
1078
+ }, operation = (value, id, index) => {
1079
+ const record = requireObject(value, id, `migration operation ${index} must be an object.`);
1080
+ const type = Reflect.get(record, "type");
1081
+ const collection = nonEmpty(Reflect.get(record, "collection"), id, `operation ${index}.collection`);
1082
+ if (type === "delete-collection")
1083
+ return { collection, type };
1084
+ if (type === "rename-field")
1085
+ return {
1086
+ collection,
1087
+ from: nonEmpty(Reflect.get(record, "from"), id, `operation ${index}.from`),
1088
+ to: nonEmpty(Reflect.get(record, "to"), id, `operation ${index}.to`),
1089
+ type
1090
+ };
1091
+ const field = nonEmpty(Reflect.get(record, "field"), id, `operation ${index}.field`);
1092
+ if (type === "remove-field")
1093
+ return { collection, field, type };
1094
+ if (type === "set-default")
1095
+ return {
1096
+ collection,
1097
+ field,
1098
+ type,
1099
+ value: normalizeJsonValue(Reflect.get(record, "value"), id, `operation ${index}.value`)
1100
+ };
1101
+ throw metadataError(id, `operation ${index}.type is not supported.`);
1102
+ }, migration = (value, id, index) => {
1103
+ const record = requireObject(value, id, `migration ${index} must be an object.`);
1104
+ const allowed = new Set(["operations", "toVersion"]);
1105
+ const unsupported = Object.keys(record).find((key) => !allowed.has(key));
1106
+ if (unsupported)
1107
+ throw metadataError(id, `migration ${index}.${unsupported} is not declarative metadata.`);
1108
+ const declaredOperations = Reflect.get(record, "operations");
1109
+ if (declaredOperations !== undefined && !Array.isArray(declaredOperations))
1110
+ throw metadataError(id, `migration ${index}.operations must be an array.`);
1111
+ const operations = Array.isArray(declaredOperations) ? declaredOperations : [];
1112
+ return {
1113
+ operations: operations.map((entry, operationIndex) => operation(entry, id, operationIndex)),
1114
+ toVersion: positiveVersion2(Reflect.get(record, "toVersion"), id, `migration ${index}.toVersion`)
1115
+ };
1116
+ }, localDataPolicy = (value, id) => {
1117
+ const record = requireObject(value, id, "localData must be an object.");
1118
+ const allowed = new Set([
1119
+ "collections",
1120
+ "maxBytesPerNamespace",
1121
+ "mutations"
1122
+ ]);
1123
+ const unsupported = Object.keys(record).find((key) => !allowed.has(key));
1124
+ if (unsupported)
1125
+ throw metadataError(id, `localData.${unsupported} is not supported.`);
1126
+ const collectionRules = Reflect.get(record, "collections");
1127
+ const mutationRules = Reflect.get(record, "mutations");
1128
+ if (collectionRules !== undefined && !Array.isArray(collectionRules))
1129
+ throw metadataError(id, "localData.collections must be an array.");
1130
+ if (mutationRules !== undefined && !Array.isArray(mutationRules))
1131
+ throw metadataError(id, "localData.mutations must be an array.");
1132
+ const collections = Array.isArray(collectionRules) ? collectionRules.map((entry, index) => {
1133
+ const rule = requireObject(entry, id, `localData.collections[${index}] must be an object.`);
1134
+ const allowedRuleKeys = new Set([
1135
+ "evictionPriority",
1136
+ "match",
1137
+ "maxAgeMs",
1138
+ "onProtectionUnavailable",
1139
+ "persistence",
1140
+ "protection",
1141
+ "sensitivity"
1142
+ ]);
1143
+ const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
1144
+ if (unsupportedRuleKey)
1145
+ throw metadataError(id, `localData.collections[${index}].${unsupportedRuleKey} is not supported.`);
1146
+ const match = nonEmpty(Reflect.get(rule, "match"), id, `localData.collections[${index}].match`);
1147
+ const persistence = unknownField(rule, "persistence");
1148
+ const sensitivity = unknownField(rule, "sensitivity");
1149
+ const protection = unknownField(rule, "protection");
1150
+ const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
1151
+ const evictionPriority = unknownField(rule, "evictionPriority");
1152
+ const maxAge = unknownField(rule, "maxAgeMs");
1153
+ if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
1154
+ throw metadataError(id, `localData.collections[${index}].persistence is invalid.`);
1155
+ if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
1156
+ throw metadataError(id, `localData.collections[${index}].sensitivity is invalid.`);
1157
+ if (protection !== undefined && protection !== "none" && protection !== "required")
1158
+ throw metadataError(id, `localData.collections[${index}].protection is invalid.`);
1159
+ if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
1160
+ throw metadataError(id, `localData.collections[${index}].onProtectionUnavailable is invalid.`);
1161
+ if (evictionPriority !== undefined && evictionPriority !== "critical" && evictionPriority !== "normal" && evictionPriority !== "disposable")
1162
+ throw metadataError(id, `localData.collections[${index}].evictionPriority is invalid.`);
1163
+ return {
1164
+ match,
1165
+ ...sensitivity ? { sensitivity } : {},
1166
+ ...persistence ? { persistence } : {},
1167
+ ...protection ? { protection } : {},
1168
+ ...onProtectionUnavailable ? {
1169
+ onProtectionUnavailable
1170
+ } : {},
1171
+ ...evictionPriority ? { evictionPriority } : {},
1172
+ ...maxAge === undefined ? {} : {
1173
+ maxAgeMs: positiveVersion2(maxAge, id, `localData.collections[${index}].maxAgeMs`)
1174
+ }
1175
+ };
1176
+ }) : undefined;
1177
+ const mutations = Array.isArray(mutationRules) ? mutationRules.map((entry, index) => {
1178
+ const rule = requireObject(entry, id, `localData.mutations[${index}] must be an object.`);
1179
+ const allowedRuleKeys = new Set([
1180
+ "conflict",
1181
+ "match",
1182
+ "onProtectionUnavailable",
1183
+ "persistence",
1184
+ "protection",
1185
+ "sensitivity"
1186
+ ]);
1187
+ const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
1188
+ if (unsupportedRuleKey)
1189
+ throw metadataError(id, `localData.mutations[${index}].${unsupportedRuleKey} is not supported.`);
1190
+ const protection = unknownField(rule, "protection");
1191
+ const sensitivity = unknownField(rule, "sensitivity");
1192
+ const persistence = unknownField(rule, "persistence");
1193
+ const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
1194
+ const declaredConflict = unknownField(rule, "conflict");
1195
+ let conflict;
1196
+ if (declaredConflict !== undefined) {
1197
+ const conflictRecord = requireObject(declaredConflict, id, `localData.mutations[${index}].conflict must be an object.`);
1198
+ const unsupportedConflictKey = Object.keys(conflictRecord).find((key) => key !== "maxAttempts" && key !== "strategy");
1199
+ if (unsupportedConflictKey)
1200
+ throw metadataError(id, `localData.mutations[${index}].conflict.${unsupportedConflictKey} is not supported.`);
1201
+ const strategy = unknownField(conflictRecord, "strategy");
1202
+ if (strategy !== "client-wins" && strategy !== "manual" && strategy !== "server-wins")
1203
+ throw metadataError(id, `localData.mutations[${index}].conflict.strategy is invalid.`);
1204
+ const maxAttempts = unknownField(conflictRecord, "maxAttempts");
1205
+ if (maxAttempts !== undefined && strategy !== "client-wins")
1206
+ throw metadataError(id, `localData.mutations[${index}].conflict.maxAttempts requires client-wins.`);
1207
+ conflict = {
1208
+ strategy,
1209
+ ...maxAttempts === undefined ? {} : {
1210
+ maxAttempts: positiveVersion2(maxAttempts, id, `localData.mutations[${index}].conflict.maxAttempts`)
1211
+ }
1212
+ };
1213
+ }
1214
+ if (protection !== undefined && protection !== "none" && protection !== "required")
1215
+ throw metadataError(id, `localData.mutations[${index}].protection is invalid.`);
1216
+ if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
1217
+ throw metadataError(id, `localData.mutations[${index}].sensitivity is invalid.`);
1218
+ if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
1219
+ throw metadataError(id, `localData.mutations[${index}].onProtectionUnavailable is invalid.`);
1220
+ if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
1221
+ throw metadataError(id, `localData.mutations[${index}].persistence is invalid.`);
1222
+ return {
1223
+ match: nonEmpty(Reflect.get(rule, "match"), id, `localData.mutations[${index}].match`),
1224
+ ...conflict ? { conflict } : {},
1225
+ ...sensitivity ? { sensitivity } : {},
1226
+ ...onProtectionUnavailable ? { onProtectionUnavailable } : {},
1227
+ ...persistence ? {
1228
+ persistence
1229
+ } : {},
1230
+ ...protection ? { protection } : {}
1231
+ };
1232
+ }) : undefined;
1233
+ const quota = Reflect.get(record, "maxBytesPerNamespace");
1234
+ return {
1235
+ ...collections ? { collections } : {},
1236
+ ...mutations ? { mutations } : {},
1237
+ ...quota === undefined ? {} : {
1238
+ maxBytesPerNamespace: positiveVersion2(quota, id, "localData.maxBytesPerNamespace")
1239
+ }
1240
+ };
1241
+ }, component = (id, value) => {
1242
+ const record = requireObject(value, id, "localSchema must be an object.");
1243
+ const allowed = new Set([
1244
+ "localData",
1245
+ "migrations",
1246
+ "minimumCompatibleVersion",
1247
+ "version"
1248
+ ]);
1249
+ const unsupported = Object.keys(record).find((key) => !allowed.has(key));
1250
+ if (unsupported)
1251
+ throw metadataError(id, `${unsupported} is not supported.`);
1252
+ const version = positiveVersion2(Reflect.get(record, "version"), id, "version");
1253
+ const declaredMinimum = Reflect.get(record, "minimumCompatibleVersion");
1254
+ const minimumCompatibleVersion = declaredMinimum === undefined ? Math.max(1, version - 2) : positiveVersion2(declaredMinimum, id, "minimumCompatibleVersion");
1255
+ const declaredMigrations = Reflect.get(record, "migrations");
1256
+ const declaredLocalData = Reflect.get(record, "localData");
1257
+ if (declaredMigrations !== undefined && !Array.isArray(declaredMigrations))
1258
+ throw metadataError(id, "migrations must be an array.");
1259
+ const migrations = Array.isArray(declaredMigrations) ? declaredMigrations : undefined;
1260
+ return {
1261
+ id,
1262
+ ...declaredLocalData === undefined ? {} : { localData: localDataPolicy(declaredLocalData, id) },
1263
+ minimumCompatibleVersion,
1264
+ ...Array.isArray(migrations) ? {
1265
+ migrations: migrations.map((entry, index) => migration(entry, id, index))
1266
+ } : {},
1267
+ version
1268
+ };
1269
+ }, dependencyNames = (manifest) => [
1270
+ Reflect.get(manifest, "dependencies"),
1271
+ Reflect.get(manifest, "optionalDependencies"),
1272
+ Reflect.get(manifest, "devDependencies"),
1273
+ Reflect.get(manifest, "peerDependencies")
1274
+ ].flatMap((dependencies) => object(dependencies) ? Object.keys(dependencies) : []).filter((name, index, names) => names.indexOf(name) === index).sort(), discoverAbsoluteSyncSchema = (projectRoot) => {
1275
+ const appManifestPath = join6(resolve3(projectRoot), "package.json");
1276
+ const appManifest = manifestAt(appManifestPath);
1277
+ if (!appManifest)
1278
+ return {
1279
+ components: [
1280
+ {
1281
+ id: "@absolutejs/app",
1282
+ minimumCompatibleVersion: 1,
1283
+ version: 1
1284
+ }
1285
+ ],
1286
+ sources: []
1287
+ };
1288
+ const appMetadata = localSchemaMetadata(appManifest);
1289
+ const components = [
1290
+ appMetadata === undefined ? { id: "@absolutejs/app", minimumCompatibleVersion: 1, version: 1 } : component("@absolutejs/app", appMetadata)
1291
+ ];
1292
+ const sources = [
1293
+ { id: "@absolutejs/app", manifestPath: appManifestPath }
1294
+ ];
1295
+ for (const name of dependencyNames(appManifest)) {
1296
+ const manifestPath = packageManifestPath(projectRoot, name);
1297
+ if (!manifestPath)
1298
+ continue;
1299
+ const manifest = manifestAt(manifestPath);
1300
+ if (!manifest)
1301
+ continue;
1302
+ const metadata = localSchemaMetadata(manifest);
1303
+ if (metadata === undefined)
1304
+ continue;
1305
+ components.push(component(name, metadata));
1306
+ sources.push({ id: name, manifestPath });
1307
+ }
1308
+ components.sort((left, right) => left.id.localeCompare(right.id));
1309
+ sources.sort((left, right) => left.id.localeCompare(right.id));
1310
+ resolveSyncLocalSchemaComponents({}, { components });
1311
+ return { components, sources };
1312
+ };
1313
+ var init_syncSchema = __esm(() => {
1314
+ init_client();
1315
+ });
1316
+
1317
+ // src/mobile/expoProject.ts
1318
+ import {
1319
+ access,
1320
+ cp,
1321
+ mkdir,
1322
+ mkdtemp,
1323
+ readdir,
1324
+ readFile,
1325
+ rename,
1326
+ rm,
1327
+ writeFile
1328
+ } from "fs/promises";
1329
+ import { createHash } from "crypto";
1330
+ import { basename as basename2, dirname as dirname4, join as join7, relative, resolve as resolve4 } from "path";
1331
+ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
1332
+ `, EXPO_ASSET_EXTENSION = ".absasset", EXPO_PROJECT_MARKER = ".absolutejs-expo-project", exists = async (path) => {
1333
+ try {
1334
+ await access(path);
1335
+ return true;
1336
+ } catch {
1337
+ return false;
1338
+ }
1339
+ }, portableRelative = (from, destination) => {
1340
+ const value = relative(from, destination).replaceAll("\\", "/");
1341
+ return value.startsWith(".") ? value : `./${value}`;
1342
+ }, routeSegments = (route) => route.split("/").filter(Boolean).map((segment) => {
1343
+ if (segment.startsWith(":"))
1344
+ return `[${segment.slice(1)}]`;
1345
+ if (segment === "." || segment === ".." || !/^[A-Za-z0-9._~-]+$/u.test(segment)) {
1346
+ throw new TypeError(`Expo native route ${route} contains an unsupported segment ${segment}.`);
1347
+ }
1348
+ return segment;
1349
+ }), routeFile = (project, route) => join7(project, "app", ...routeSegments(route), "index.tsx"), expoPackage = (auth, sync) => ({
1350
+ dependencies: {
1351
+ ...auth ? {
1352
+ "@absolutejs/auth": ABSOLUTE_EXPO_AUTH_CORE_VERSION,
1353
+ [ABSOLUTE_EXPO_AUTH_PACKAGE]: ABSOLUTE_EXPO_AUTH_VERSION
1354
+ } : {},
1355
+ ...sync ? {
1356
+ "@absolutejs/sync": ABSOLUTE_EXPO_SYNC_CORE_VERSION,
1357
+ [ABSOLUTE_EXPO_SYNC_PACKAGE]: ABSOLUTE_EXPO_SYNC_VERSION
1358
+ } : {},
1359
+ expo: "~57.0.9",
1360
+ "expo-asset": "~57.0.15",
1361
+ "expo-constants": "~57.0.16",
1362
+ "expo-dev-client": "~57.0.16",
1363
+ "expo-file-system": "~57.0.6",
1364
+ "expo-haptics": "~57.0.2",
1365
+ "expo-linking": "~57.0.8",
1366
+ ...sync ? {
1367
+ "expo-background-task": "~57.0.14",
1368
+ "expo-network": "~57.0.1",
1369
+ "expo-sqlite": "~57.0.2",
1370
+ "expo-task-manager": "~57.0.14",
1371
+ "expo-updates": "~57.0.19"
1372
+ } : {},
1373
+ "expo-router": "~57.0.17",
1374
+ ...auth ? {
1375
+ "expo-secure-store": "~57.0.2",
1376
+ "expo-web-browser": "~57.0.2"
1377
+ } : {},
1378
+ react: "19.2.3",
1379
+ "react-native": "0.86.3",
1380
+ "react-native-safe-area-context": "~5.7.0",
1381
+ "react-native-screens": "4.26.0",
1382
+ "react-native-webview": "13.16.1"
1383
+ },
1384
+ devDependencies: {
1385
+ "@types/react": "~19.2.2",
1386
+ typescript: "~6.0.3"
1387
+ },
1388
+ main: "expo-router/entry",
1389
+ name: "absolutejs-expo-shell",
1390
+ private: true,
1391
+ scripts: {
1392
+ android: "expo run:android",
1393
+ ios: "expo run:ios",
1394
+ start: "expo start --dev-client"
1395
+ },
1396
+ version: "0.0.0"
1397
+ }), expoAppConfig = (config, auth, sync) => ({
1398
+ expo: {
1399
+ android: {
1400
+ intentFilters: config.deepLinkHosts.map((host2) => ({
1401
+ action: "VIEW",
1402
+ autoVerify: true,
1403
+ category: ["BROWSABLE", "DEFAULT"],
1404
+ data: [{ host: host2, pathPrefix: "/", scheme: "https" }]
1405
+ })),
1406
+ package: config.appId
1407
+ },
1408
+ experiments: { typedRoutes: true },
1409
+ ios: {
1410
+ associatedDomains: config.deepLinkHosts.map((host2) => `applinks:${host2}`),
1411
+ bundleIdentifier: config.appId,
1412
+ ...config.iosVersion ? { buildNumber: config.iosVersion } : {}
1413
+ },
1414
+ name: config.appName,
1415
+ plugins: [
1416
+ "expo-router",
1417
+ ["expo-dev-client", { launchMode: "most-recent" }],
1418
+ ...auth ? ["expo-secure-store"] : [],
1419
+ ...sync ? ["expo-sqlite", "expo-background-task", "expo-task-manager"] : []
1420
+ ],
1421
+ runtimeVersion: { policy: "appVersion" },
1422
+ scheme: config.deepLinkScheme,
1423
+ slug: config.appId.toLowerCase().replaceAll(".", "-"),
1424
+ version: config.iosVersion ?? "0.1.0"
1425
+ }
1426
+ }), expoDynamicAppConfig, expoDevelopmentCaPlugin, metroConfig = (projectRoot) => `${EXPO_GENERATED_HEADER}const { getDefaultConfig } = require('expo/metro-config');
1427
+ const path = require('node:path');
1428
+
1429
+ const projectRoot = __dirname;
1430
+ const appRoot = ${JSON.stringify(projectRoot)};
1431
+ const config = getDefaultConfig(projectRoot);
1432
+ config.resolver.assetExts.push('absasset');
1433
+ config.resolver.nodeModulesPaths = [
1434
+ path.join(projectRoot, 'node_modules'),
1435
+ path.join(appRoot, 'node_modules')
1436
+ ];
1437
+ config.watchFolders = [appRoot];
1438
+
1439
+ module.exports = config;
1440
+ `, layoutSource = (auth, sync) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
1441
+ ${auth ? `import { useEffect, useState } from 'react';
1442
+ import { startAbsoluteExpoAuth } from '../src/generated/AbsoluteAuth';` : ""}
1443
+ ${sync ? "import { startAbsoluteExpoSync } from '../src/generated/AbsoluteSync';" : ""}
1444
+
1445
+ export default function AbsoluteLayout() {
1446
+ ${auth ? `const [ready, setReady] = useState(false);
1447
+ useEffect(() => {
1448
+ let active = true;
1449
+ void ${sync ? "startAbsoluteExpoSync" : "startAbsoluteExpoAuth"}().then(() => { if (active) setReady(true); });
1450
+ return () => { active = false; };
1451
+ }, []);
1452
+ if (!ready) return null;` : ""}
1453
+ return <Stack screenOptions={{ headerShown: false }} />;
1454
+ }
1455
+ `, nativeDiagnosticSource, authRuntimeSource = (auth, appId) => {
1456
+ const storageIdentity = createHash("sha256").update(appId).digest("hex").slice(0, 24);
1457
+ return `${EXPO_GENERATED_HEADER}import { createAbsoluteExpoAuthClient } from '@absolutejs/auth-expo';
1458
+ import { createMobileAuthTransport, installAuthClientRuntimeTransport } from '@absolutejs/auth/client/mobile';
1459
+
1460
+ export const absoluteExpoAuth = createAbsoluteExpoAuthClient({
1461
+ allowedOrigins: [${JSON.stringify(auth.issuer)}],
1462
+ clientId: ${JSON.stringify(auth.clientId)},
1463
+ issuer: ${JSON.stringify(auth.issuer)},
1464
+ redirectUri: ${JSON.stringify(auth.redirectUri)},
1465
+ resource: ${JSON.stringify(auth.issuer)},
1466
+ scopes: ${JSON.stringify(auth.scopes)},
1467
+ storagePrefix: ${JSON.stringify(`absolutejs.auth.${storageIdentity}`)}
1468
+ });
1469
+ installAuthClientRuntimeTransport(createMobileAuthTransport(absoluteExpoAuth, { baseUrl: ${JSON.stringify(auth.issuer)} }));
1470
+
1471
+ let currentPrincipal: Awaited<ReturnType<typeof absoluteExpoAuth.principal>> = null;
1472
+ absoluteExpoAuth.onPrincipalChange(principal => {
1473
+ currentPrincipal = principal;
1474
+ });
1475
+ export const getAbsoluteExpoAuthPrincipal = () => currentPrincipal;
1476
+
1477
+ let startPromise: Promise<void> | undefined;
1478
+ export const startAbsoluteExpoAuth = () => {
1479
+ startPromise ??= absoluteExpoAuth.start().then(async () => {
1480
+ currentPrincipal = await absoluteExpoAuth.principal();
1481
+ });
1482
+
1483
+ return startPromise;
1484
+ };
1485
+ `;
1486
+ }, syncRuntimeSource = (config, schema) => {
1487
+ const storageIdentity = createHash("sha256").update(config.appId).digest("hex").slice(0, 24);
1488
+ const backgroundEndpoint = new URL("/__absolute/sync/background", config.productionOrigin).href;
1489
+ const backgroundTask = `${config.appId}.absolutejs.sync`;
1490
+ return `${EXPO_GENERATED_HEADER}import { runHeadlessSync } from '@absolutejs/sync/client/headless';
1491
+ import type { SyncLocalStoreSchemaBundle } from '@absolutejs/sync/client';
1492
+ import { installSyncClientRuntimeTransport } from '@absolutejs/sync/client/runtime';
1493
+ import {
1494
+ createExpoSyncBridgeHost,
1495
+ createExpoSyncLocalStore,
1496
+ createExpoSyncProtection,
1497
+ createExpoSyncSocketBridgeHost,
1498
+ defineExpoSyncBackgroundTask,
1499
+ installExpoSyncLifecycle,
1500
+ registerExpoSyncBackgroundTask
1501
+ } from '@absolutejs/sync-expo';
1502
+ import * as Updates from 'expo-updates';
1503
+ import { absoluteExpoAuth, startAbsoluteExpoAuth } from './AbsoluteAuth';
1504
+
1505
+ const BACKGROUND_TASK = ${JSON.stringify(backgroundTask)};
1506
+ const BACKGROUND_ENDPOINT = ${JSON.stringify(backgroundEndpoint)};
1507
+ const PRODUCTION_ORIGIN = ${JSON.stringify(config.productionOrigin)};
1508
+ const STORAGE_SCHEMA: SyncLocalStoreSchemaBundle = ${JSON.stringify(schema)};
1509
+ const store = createExpoSyncLocalStore({
1510
+ databaseName: ${JSON.stringify(`absolutejs-sync-${storageIdentity}.db`)},
1511
+ protection: createExpoSyncProtection({ storagePrefix: ${JSON.stringify(`absolutejs.sync.${storageIdentity}`)} }),
1512
+ storageSchema: STORAGE_SCHEMA
1513
+ });
1514
+
1515
+ const runBackgroundSync = async () => {
1516
+ await startAbsoluteExpoAuth();
1517
+ const principal = await absoluteExpoAuth.principal();
1518
+ if (!principal) return;
1519
+ await runHeadlessSync({
1520
+ endpoint: BACKGROUND_ENDPOINT,
1521
+ fetch: (url, init) => absoluteExpoAuth.fetch(url, init),
1522
+ namespace: principal.namespace,
1523
+ store
1524
+ });
1525
+ };
1526
+ defineExpoSyncBackgroundTask(BACKGROUND_TASK, runBackgroundSync);
1527
+
1528
+ let activeNamespace: string | undefined;
1529
+ let started = false;
1530
+ let startPromise: Promise<void> | undefined;
1531
+ export const startAbsoluteExpoSync = () => {
1532
+ startPromise ??= (async () => {
1533
+ await startAbsoluteExpoAuth();
1534
+ const principal = await absoluteExpoAuth.principal();
1535
+ activeNamespace = principal?.namespace;
1536
+ installSyncClientRuntimeTransport({
1537
+ ...(principal ? { durable: { namespace: principal.namespace, store }, socketTicket: () => absoluteExpoAuth.socketTicket() } : {}),
1538
+ registerClient: client => installExpoSyncLifecycle({ client })
1539
+ });
1540
+ await registerExpoSyncBackgroundTask(BACKGROUND_TASK, { minimumInterval: 15 });
1541
+ started = true;
1542
+ })();
1543
+
1544
+ return startPromise;
1545
+ };
1546
+
1547
+ absoluteExpoAuth.onPrincipalChange(principal => {
1548
+ if (!started || principal?.namespace === activeNamespace) return;
1549
+ void Updates.reloadAsync();
1550
+ });
1551
+
1552
+ export const createAbsoluteExpoSyncBridge = async (
1553
+ emit: (event: 'sync.socket' | 'sync.wake', payload: Record<string, unknown>) => void
1554
+ ) => {
1555
+ await startAbsoluteExpoSync();
1556
+ const principal = await absoluteExpoAuth.principal();
1557
+ if (!principal) return {
1558
+ close: () => undefined,
1559
+ request: async () => { throw new Error('Expo Sync requires an authenticated principal.'); }
1560
+ };
1561
+ const transactions = createExpoSyncBridgeHost({ namespace: principal.namespace, store });
1562
+ const sockets = createExpoSyncSocketBridgeHost({
1563
+ allowedOrigin: PRODUCTION_ORIGIN,
1564
+ emit: payload => emit('sync.socket', payload),
1565
+ socketTicket: audience => absoluteExpoAuth.socketTicket(audience)
1566
+ });
1567
+ const removeLifecycle = installExpoSyncLifecycle({
1568
+ client: { reconnect: () => emit('sync.wake', {}) }
1569
+ });
1570
+
1571
+ return {
1572
+ close: async () => {
1573
+ removeLifecycle();
1574
+ await Promise.all([transactions.close(), sockets.close()]);
1575
+ },
1576
+ request: (method: string, params: Record<string, unknown>) =>
1577
+ method.startsWith('sync.socket.')
1578
+ ? sockets.request(method, params)
1579
+ : transactions.request(method, params)
1580
+ };
1581
+ };
1582
+ `;
1583
+ }, webHostSource = (config, auth, sync) => {
1584
+ const nativeRoutes = [
1585
+ "/__absolute/native",
1586
+ ...Object.keys(config.expoNativeRoutes)
1050
1587
  ];
1051
1588
  return `${EXPO_GENERATED_HEADER}import * as Haptics from 'expo-haptics';
1052
1589
  import * as Linking from 'expo-linking';
@@ -1056,8 +1593,9 @@ import { ActivityIndicator, BackHandler, Platform, StyleSheet, View } from 'reac
1056
1593
  import { WebView, type WebViewMessageEvent } from 'react-native-webview';
1057
1594
  import { materializeAbsoluteWebBundle } from './webAssets';
1058
1595
  ${auth ? "import { absoluteExpoAuth, getAbsoluteExpoAuthPrincipal, startAbsoluteExpoAuth } from './AbsoluteAuth';" : ""}
1596
+ ${sync ? "import { createAbsoluteExpoSyncBridge, startAbsoluteExpoSync } from './AbsoluteSync';" : ""}
1059
1597
 
1060
- const BRIDGE_FORMAT = 2;
1598
+ const BRIDGE_FORMAT = 3;
1061
1599
  const MAX_MESSAGE_BYTES = 64 * 1024;
1062
1600
  const MAX_HTTP_BODY_BYTES = 48 * 1024;
1063
1601
  const NATIVE_ROUTES = new Set(${JSON.stringify(nativeRoutes)});
@@ -1067,6 +1605,7 @@ const DEV_ORIGIN = Platform.OS === 'android'
1067
1605
  : process.env.EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN;
1068
1606
  const HMR_TARGET = Platform.OS === 'android' ? 'expo-android' : 'expo-ios';
1069
1607
  const AUTH_ENABLED = ${auth ? "true" : "false"};
1608
+ const SYNC_ENABLED = ${sync ? "true" : "false"};
1070
1609
 
1071
1610
  const bridgeBootstrap = (path: string) => {
1072
1611
  const initialPath = DEV_ORIGIN
@@ -1106,7 +1645,7 @@ const bridgeBootstrap = (path: string) => {
1106
1645
  },
1107
1646
  request(method, params) {
1108
1647
  const id = 'web_' + Date.now().toString(36) + '_' + (++sequence).toString(36);
1109
- send({ format: 2, id, kind: 'request', method, params, path: currentPath });
1648
+ send({ format: 3, id, kind: 'request', method, params, path: currentPath });
1110
1649
  return new Promise((resolve, reject) => {
1111
1650
  const timer = setTimeout(() => {
1112
1651
  pending.delete(id);
@@ -1117,7 +1656,7 @@ const bridgeBootstrap = (path: string) => {
1117
1656
  },
1118
1657
  setPath(path) {
1119
1658
  currentPath = path;
1120
- send({ format: 2, kind: 'event', event: 'navigation', path });
1659
+ send({ format: 3, kind: 'event', event: 'navigation', path });
1121
1660
  }
1122
1661
  };
1123
1662
  if (\${DEV_ORIGIN ? 'true' : 'false'}) {
@@ -1125,7 +1664,7 @@ const bridgeBootstrap = (path: string) => {
1125
1664
  const path = location.pathname + location.search + location.hash;
1126
1665
  if (path === currentPath) return;
1127
1666
  currentPath = path;
1128
- send({ format: 2, kind: 'event', event: 'navigation', path });
1667
+ send({ format: 3, kind: 'event', event: 'navigation', path });
1129
1668
  };
1130
1669
  for (const method of ['pushState', 'replaceState']) {
1131
1670
  const original = history[method];
@@ -1144,9 +1683,9 @@ const bridgeBootstrap = (path: string) => {
1144
1683
  const url = new URL(anchor.href, location.href);
1145
1684
  if (!${JSON.stringify(nativeRoutes)}.includes(url.pathname)) return;
1146
1685
  event.preventDefault();
1147
- send({ format: 2, kind: 'event', event: 'navigation', path: url.pathname + url.search + url.hash });
1686
+ send({ format: 3, kind: 'event', event: 'navigation', path: url.pathname + url.search + url.hash });
1148
1687
  }, true);
1149
- send({ format: 2, kind: 'event', event: 'ready', path: currentPath });
1688
+ send({ format: 3, kind: 'event', event: 'ready', path: currentPath });
1150
1689
  })(); true;\`;
1151
1690
  };
1152
1691
 
@@ -1192,21 +1731,34 @@ const authStatus = async () => {
1192
1731
  export function AbsoluteWebHost() {
1193
1732
  const pathname = usePathname() || '/';
1194
1733
  const webView = useRef<WebView>(null);
1734
+ const syncBridge = useRef<{ close(): void | Promise<void>; request(method: string, params: Record<string, unknown>): Promise<unknown> } | undefined>(undefined);
1195
1735
  const [indexUri, setIndexUri] = useState<string>();
1196
1736
  const [canGoBack, setCanGoBack] = useState(false);
1197
- const [authReady, setAuthReady] = useState(!AUTH_ENABLED);
1737
+ const [runtimeReady, setRuntimeReady] = useState(!AUTH_ENABLED && !SYNC_ENABLED);
1198
1738
  const activeWebPath = useRef(pathname);
1199
1739
 
1200
1740
  useEffect(() => {
1201
- if (!AUTH_ENABLED) return;
1741
+ if (!AUTH_ENABLED && !SYNC_ENABLED) return;
1202
1742
  let active = true;
1203
1743
  ${auth ? `const stop = absoluteExpoAuth.onPrincipalChange(principal => {
1204
1744
  if (!active) return;
1205
1745
  const source = JSON.stringify({ event: 'auth.principal', format: BRIDGE_FORMAT, kind: 'event', path: activeWebPath.current, payload: { principal: principal ? { namespace: principal.namespace } : null } });
1206
1746
  webView.current?.injectJavaScript(\`globalThis.__absoluteExpoReceive(\${JSON.stringify(source)}); true;\`);
1207
1747
  });
1208
- void startAbsoluteExpoAuth().then(() => { if (active) setAuthReady(true); });
1209
- return () => { active = false; stop(); };` : ""}
1748
+ void ${sync ? "startAbsoluteExpoSync" : "startAbsoluteExpoAuth"}().then(async () => {
1749
+ ${sync ? `syncBridge.current = await createAbsoluteExpoSyncBridge((event, payload) => {
1750
+ const source = JSON.stringify({ event, format: BRIDGE_FORMAT, kind: 'event', path: activeWebPath.current, payload });
1751
+ webView.current?.injectJavaScript(\`globalThis.__absoluteExpoReceive(\${JSON.stringify(source)}); true;\`);
1752
+ });` : ""}
1753
+ if (active) setRuntimeReady(true);
1754
+ });
1755
+ return () => {
1756
+ active = false;
1757
+ stop();
1758
+ const bridge = syncBridge.current;
1759
+ syncBridge.current = undefined;
1760
+ void bridge?.close();
1761
+ };` : ""}
1210
1762
  }, []);
1211
1763
 
1212
1764
  useEffect(() => {
@@ -1268,6 +1820,9 @@ export function AbsoluteWebHost() {
1268
1820
  respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: null });` : "throw new Error('Expo Auth is not configured.');"}
1269
1821
  } else if (message.method === 'auth.status') {
1270
1822
  respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await authStatus() });
1823
+ } else if (typeof message.method === 'string' && message.method.startsWith('sync.')) {
1824
+ if (!syncBridge.current) throw new Error('Expo Sync bridge is unavailable.');
1825
+ respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await syncBridge.current.request(message.method, message.params as Record<string, unknown>) });
1271
1826
  } else {
1272
1827
  throw new Error('Expo bridge method is not allowed.');
1273
1828
  }
@@ -1276,7 +1831,7 @@ export function AbsoluteWebHost() {
1276
1831
  }
1277
1832
  };
1278
1833
 
1279
- if (!indexUri || !authReady) return <View style={styles.loading}><ActivityIndicator /></View>;
1834
+ if (!indexUri || !runtimeReady) return <View style={styles.loading}><ActivityIndicator /></View>;
1280
1835
  return <WebView
1281
1836
  allowFileAccess
1282
1837
  allowFileAccessFromFileURLs
@@ -1298,16 +1853,30 @@ export function AbsoluteWebHost() {
1298
1853
 
1299
1854
  const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, justifyContent: 'center' }, web: { flex: 1 } });
1300
1855
  `;
1301
- }, webRouteSource, catchAllRouteSource, nativeWrapperSource = (wrapper, module) => `${EXPO_GENERATED_HEADER}export { default } from ${JSON.stringify(portableRelative(dirname3(wrapper), module).replace(/\.(?:[cm]?[jt]sx?)$/u, ""))};
1302
- `, expoTsConfig = (projectRoot, project, auth) => ({
1856
+ }, webRouteSource, catchAllRouteSource, nativeWrapperSource = (wrapper, module) => `${EXPO_GENERATED_HEADER}export { default } from ${JSON.stringify(portableRelative(dirname4(wrapper), module).replace(/\.(?:[cm]?[jt]sx?)$/u, ""))};
1857
+ `, expoTsConfig = (projectRoot, project, auth, sync) => ({
1303
1858
  compilerOptions: {
1304
1859
  paths: {
1305
1860
  ...auth ? {
1306
1861
  "@absolutejs/auth": [
1307
1862
  "./node_modules/@absolutejs/auth/dist/index.d.ts"
1308
1863
  ],
1309
- "@absolutejs/auth/*": [
1310
- "./node_modules/@absolutejs/auth/dist/*"
1864
+ "@absolutejs/auth/*": [
1865
+ "./node_modules/@absolutejs/auth/dist/*"
1866
+ ]
1867
+ } : {},
1868
+ ...sync ? {
1869
+ "@absolutejs/sync": [
1870
+ "./node_modules/@absolutejs/sync/dist/index.d.ts"
1871
+ ],
1872
+ "@absolutejs/sync-expo": [
1873
+ "./node_modules/@absolutejs/sync-expo/dist/index.d.ts"
1874
+ ],
1875
+ "@absolutejs/sync-expo/*": [
1876
+ "./node_modules/@absolutejs/sync-expo/dist/*"
1877
+ ],
1878
+ "@absolutejs/sync/*": [
1879
+ "./node_modules/@absolutejs/sync/dist/*"
1311
1880
  ]
1312
1881
  } : {},
1313
1882
  "*": [
@@ -1321,7 +1890,7 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
1321
1890
  },
1322
1891
  extends: "expo/tsconfig.base"
1323
1892
  }), writeManagedFile = async (path, source, force) => {
1324
- await mkdir(dirname3(path), { recursive: true });
1893
+ await mkdir(dirname4(path), { recursive: true });
1325
1894
  if (await exists(path)) {
1326
1895
  const current = await readFile(path, "utf8");
1327
1896
  if (current === source)
@@ -1338,9 +1907,11 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
1338
1907
  `, emptyWebAssetsSource, writeAbsoluteExpoProject = async (config, options) => {
1339
1908
  if (config.engine !== "expo")
1340
1909
  throw new TypeError("Expo project generation requires mobile.engine: expo.");
1341
- const projectRoot = resolve3(options.projectRoot);
1910
+ const projectRoot = resolve4(options.projectRoot);
1342
1911
  const project = config.nativeProjectDirectory;
1343
1912
  const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
1913
+ const syncEnabled = Boolean(auth && projectUsesAbsoluteSync(projectRoot));
1914
+ const syncSchema = syncEnabled ? { components: discoverAbsoluteSyncSchema(projectRoot).components } : undefined;
1344
1915
  const routeModules = Object.entries(config.expoNativeRoutes);
1345
1916
  const moduleChecks = await Promise.all(routeModules.map(async ([route, module]) => ({
1346
1917
  exists: await exists(module),
@@ -1351,7 +1922,7 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
1351
1922
  if (missing) {
1352
1923
  throw new TypeError(`Expo native route ${missing.route} references missing module ${missing.module}.`);
1353
1924
  }
1354
- const marker = join6(project, EXPO_PROJECT_MARKER);
1925
+ const marker = join7(project, EXPO_PROJECT_MARKER);
1355
1926
  if (await exists(project) && !await exists(marker)) {
1356
1927
  const entries = await readdir(project);
1357
1928
  if (entries.length > 0 && !options.force) {
@@ -1364,7 +1935,7 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
1364
1935
  const authEnabled = Boolean(auth);
1365
1936
  const files = new Map([
1366
1937
  [
1367
- join6(project, ".gitignore"),
1938
+ join7(project, ".gitignore"),
1368
1939
  `.expo/
1369
1940
  android/
1370
1941
  ios/
@@ -1372,43 +1943,52 @@ node_modules/
1372
1943
  `
1373
1944
  ],
1374
1945
  [
1375
- join6(project, "app.json"),
1376
- jsonSource(expoAppConfig(config, authEnabled))
1946
+ join7(project, "app.json"),
1947
+ jsonSource(expoAppConfig(config, authEnabled, syncEnabled))
1948
+ ],
1949
+ [join7(project, "app.config.js"), expoDynamicAppConfig],
1950
+ [
1951
+ join7(project, "package.json"),
1952
+ jsonSource(expoPackage(authEnabled, syncEnabled))
1377
1953
  ],
1378
- [join6(project, "app.config.js"), expoDynamicAppConfig],
1379
- [join6(project, "package.json"), jsonSource(expoPackage(authEnabled))],
1380
- [join6(project, "metro.config.js"), metroConfig(projectRoot)],
1954
+ [join7(project, "metro.config.js"), metroConfig(projectRoot)],
1381
1955
  [
1382
- join6(project, "plugins", "withAbsoluteDevelopmentCa.js"),
1956
+ join7(project, "plugins", "withAbsoluteDevelopmentCa.js"),
1383
1957
  expoDevelopmentCaPlugin
1384
1958
  ],
1385
1959
  [
1386
- join6(project, "tsconfig.json"),
1387
- jsonSource(expoTsConfig(projectRoot, project, authEnabled))
1960
+ join7(project, "tsconfig.json"),
1961
+ jsonSource(expoTsConfig(projectRoot, project, authEnabled, syncEnabled))
1388
1962
  ],
1389
- [join6(project, "app", "_layout.tsx"), layoutSource(authEnabled)],
1390
1963
  [
1391
- join6(project, "app", "__absolute", "native", "index.tsx"),
1964
+ join7(project, "app", "_layout.tsx"),
1965
+ layoutSource(authEnabled, syncEnabled)
1966
+ ],
1967
+ [
1968
+ join7(project, "app", "__absolute", "native", "index.tsx"),
1392
1969
  nativeDiagnosticSource
1393
1970
  ],
1394
1971
  [
1395
- join6(project, "src", "generated", "AbsoluteWebHost.tsx"),
1396
- webHostSource(config, auth)
1972
+ join7(project, "src", "generated", "AbsoluteWebHost.tsx"),
1973
+ webHostSource(config, auth, syncEnabled)
1397
1974
  ]
1398
1975
  ]);
1399
1976
  if (auth) {
1400
- files.set(join6(project, "src", "generated", "AbsoluteAuth.ts"), authRuntimeSource(auth, config.appId));
1977
+ files.set(join7(project, "src", "generated", "AbsoluteAuth.ts"), authRuntimeSource(auth, config.appId));
1978
+ }
1979
+ if (syncSchema) {
1980
+ files.set(join7(project, "src", "generated", "AbsoluteSync.ts"), syncRuntimeSource(config, syncSchema));
1401
1981
  }
1402
- const webAssetsPath = join6(project, "src", "generated", "webAssets.ts");
1982
+ const webAssetsPath = join7(project, "src", "generated", "webAssets.ts");
1403
1983
  if (!await exists(webAssetsPath)) {
1404
1984
  files.set(webAssetsPath, emptyWebAssetsSource);
1405
1985
  }
1406
1986
  if (!config.expoNativeRoutes["/"]) {
1407
- files.set(join6(project, "app", "index.tsx"), webRouteSource);
1987
+ files.set(join7(project, "app", "index.tsx"), webRouteSource);
1408
1988
  }
1409
- files.set(join6(project, "app", "[...absolute].tsx"), catchAllRouteSource);
1989
+ files.set(join7(project, "app", "[...absolute].tsx"), catchAllRouteSource);
1410
1990
  for (const [route, module] of routeModules) {
1411
- const wrapper = route === "/" ? join6(project, "app", "index.tsx") : routeFile(project, route);
1991
+ const wrapper = route === "/" ? join7(project, "app", "index.tsx") : routeFile(project, route);
1412
1992
  files.set(wrapper, nativeWrapperSource(wrapper, module));
1413
1993
  }
1414
1994
  const changes = await Promise.all([...files].map(([path, source]) => writeManagedFile(path, source, true)));
@@ -1417,7 +1997,7 @@ node_modules/
1417
1997
  }, walkFiles = async (root, directory = root) => {
1418
1998
  const entries = await readdir(directory, { withFileTypes: true });
1419
1999
  const nested = await Promise.all(entries.map((entry) => {
1420
- const path = join6(directory, entry.name);
2000
+ const path = join7(directory, entry.name);
1421
2001
  if (entry.isDirectory())
1422
2002
  return walkFiles(root, path);
1423
2003
  if (entry.isFile())
@@ -1478,11 +2058,11 @@ export const materializeAbsoluteWebBundle = async () => {
1478
2058
  `, syncAbsoluteExpoWebAssets = async (config) => {
1479
2059
  if (config.engine !== "expo")
1480
2060
  throw new TypeError("Expo asset sync requires mobile.engine: expo.");
1481
- const marker = join6(config.nativeProjectDirectory, EXPO_PROJECT_MARKER);
2061
+ const marker = join7(config.nativeProjectDirectory, EXPO_PROJECT_MARKER);
1482
2062
  if (!await exists(marker)) {
1483
2063
  throw new TypeError("Expo asset sync requires an AbsoluteJS-managed Expo project. Run mobile init first.");
1484
2064
  }
1485
- const manifestPath = join6(config.bundleDirectory, "absolute-mobile-manifest.json");
2065
+ const manifestPath = join7(config.bundleDirectory, "absolute-mobile-manifest.json");
1486
2066
  const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
1487
2067
  const appBuild = typeof manifest === "object" && manifest !== null && typeof Reflect.get(manifest, "appBuild") === "string" ? String(Reflect.get(manifest, "appBuild")) : undefined;
1488
2068
  if (!appBuild)
@@ -1497,16 +2077,16 @@ export const materializeAbsoluteWebBundle = async () => {
1497
2077
  bundleHash.update("\x00");
1498
2078
  });
1499
2079
  const bundleId = `amexpo_${bundleHash.digest("hex")}`;
1500
- const destination = join6(config.nativeProjectDirectory, "assets", "absolute");
1501
- await mkdir(dirname3(destination), { recursive: true });
1502
- const staging = await mkdtemp(join6(dirname3(destination), `.${basename2(destination)}.stage-`));
2080
+ const destination = join7(config.nativeProjectDirectory, "assets", "absolute");
2081
+ await mkdir(dirname4(destination), { recursive: true });
2082
+ const staging = await mkdtemp(join7(dirname4(destination), `.${basename2(destination)}.stage-`));
1503
2083
  let assets;
1504
2084
  try {
1505
2085
  assets = await Promise.all(files.map(async (source, index) => {
1506
2086
  const name = `${String(index).padStart(6, "0")}${EXPO_ASSET_EXTENSION}`;
1507
- await cp(source, join6(staging, name));
2087
+ await cp(source, join7(staging, name));
1508
2088
  return {
1509
- asset: portableRelative(join6(config.nativeProjectDirectory, "src", "generated"), join6(destination, name)),
2089
+ asset: portableRelative(join7(config.nativeProjectDirectory, "src", "generated"), join7(destination, name)),
1510
2090
  path: relative(config.bundleDirectory, source).replaceAll("\\", "/")
1511
2091
  };
1512
2092
  }));
@@ -1515,12 +2095,13 @@ export const materializeAbsoluteWebBundle = async () => {
1515
2095
  await rm(staging, { force: true, recursive: true });
1516
2096
  throw error;
1517
2097
  }
1518
- const generated = join6(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
2098
+ const generated = join7(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
1519
2099
  await writeManagedFile(generated, assetModuleSource(assets, bundleId), true);
1520
2100
  return { appBuild, assets: assets.length, bundleId, path: destination };
1521
2101
  };
1522
2102
  var init_expoProject = __esm(() => {
1523
2103
  init_nativeAuth();
2104
+ init_syncSchema();
1524
2105
  expoDynamicAppConfig = `${EXPO_GENERATED_HEADER}const config = require('./app.json');
1525
2106
 
1526
2107
  if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH) {
@@ -1626,14 +2207,14 @@ import {
1626
2207
  isIP
1627
2208
  } from "net";
1628
2209
  import { readFile as readFile2 } from "fs/promises";
1629
- var closeServer = (server) => new Promise((resolve4, reject) => {
2210
+ var closeServer = (server) => new Promise((resolve5, reject) => {
1630
2211
  server.close((error) => {
1631
2212
  if (error)
1632
2213
  reject(error);
1633
2214
  else
1634
- resolve4();
2215
+ resolve5();
1635
2216
  });
1636
- }), listen = (server, port) => new Promise((resolve4, reject) => {
2217
+ }), listen = (server, port) => new Promise((resolve5, reject) => {
1637
2218
  server.once("error", reject);
1638
2219
  server.listen(port, "0.0.0.0", () => {
1639
2220
  server.off("error", reject);
@@ -1642,11 +2223,11 @@ var closeServer = (server) => new Promise((resolve4, reject) => {
1642
2223
  reject(new Error("Could not determine the iOS device helper port."));
1643
2224
  return;
1644
2225
  }
1645
- resolve4(address.port);
2226
+ resolve5(address.port);
1646
2227
  });
1647
2228
  }), findEphemeralPort = async () => {
1648
2229
  const probe = createTcpServer();
1649
- const port = await new Promise((resolve4, reject) => {
2230
+ const port = await new Promise((resolve5, reject) => {
1650
2231
  probe.once("error", reject);
1651
2232
  probe.listen(0, "127.0.0.1", () => {
1652
2233
  const address = probe.address();
@@ -1654,7 +2235,7 @@ var closeServer = (server) => new Promise((resolve4, reject) => {
1654
2235
  reject(new Error("Could not allocate the iOS CA enrollment port."));
1655
2236
  return;
1656
2237
  }
1657
- resolve4(address.port);
2238
+ resolve5(address.port);
1658
2239
  });
1659
2240
  });
1660
2241
  await closeServer(probe);
@@ -1669,9 +2250,9 @@ var closeServer = (server) => new Promise((resolve4, reject) => {
1669
2250
  if (!normalized || normalized.length > 256 || /[\0\r\n]/u.test(normalized))
1670
2251
  throw new TypeError("--ios-device requires a valid Xcode device identifier or name.");
1671
2252
  return normalized;
1672
- }, urlForHost = (protocol, host, port) => {
2253
+ }, urlForHost = (protocol, host2, port) => {
1673
2254
  const url = new URL(`${protocol}://localhost:${port}`);
1674
- const normalizedHost = normalizeAbsoluteIosDeviceHost(host);
2255
+ const normalizedHost = normalizeAbsoluteIosDeviceHost(host2);
1675
2256
  url.hostname = isIP(normalizedHost) === 6 ? `[${normalizedHost}]` : normalizedHost;
1676
2257
  return url;
1677
2258
  }, startAbsoluteIosCaEnrollmentServer = async (options) => {
@@ -1711,12 +2292,12 @@ var init_iosPhysicalDeviceTransport = () => {};
1711
2292
  // src/cli/utils.ts
1712
2293
  var {$: $2 } = globalThis.Bun;
1713
2294
  import { execSync } from "child_process";
1714
- import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
2295
+ import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
1715
2296
  import { createServer as createServer3 } from "net";
1716
- import { resolve as resolve4 } from "path";
2297
+ import { resolve as resolve5 } from "path";
1717
2298
  var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backend/server.ts", isWSLEnvironment = () => {
1718
2299
  try {
1719
- const release = readFileSync6("/proc/version", "utf-8");
2300
+ const release = readFileSync7("/proc/version", "utf-8");
1720
2301
  return /microsoft|wsl/i.test(release);
1721
2302
  } catch {
1722
2303
  return false;
@@ -1815,7 +2396,7 @@ var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backe
1815
2396
  }, printHint = () => {
1816
2397
  console.log("\x1B[90mpress h + enter to show shortcuts\x1B[0m");
1817
2398
  }, readDbScripts = async () => {
1818
- const pkgPath = resolve4("package.json");
2399
+ const pkgPath = resolve5("package.json");
1819
2400
  if (!existsSync3(pkgPath))
1820
2401
  return null;
1821
2402
  const pkg = await Bun.file(pkgPath).json();
@@ -1850,7 +2431,7 @@ var init_utils = __esm(() => {
1850
2431
  // src/mobile/emulatorDoctor.ts
1851
2432
  import { access as access3 } from "fs/promises";
1852
2433
  import { homedir as homedir3 } from "os";
1853
- import { join as join8 } from "path";
2434
+ import { join as join9 } from "path";
1854
2435
  var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command) => {
1855
2436
  try {
1856
2437
  const result = Bun.spawnSync(command, {
@@ -1893,17 +2474,17 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1893
2474
  } catch {
1894
2475
  return;
1895
2476
  }
1896
- }, absoluteManagedAndroidSdkRoot = (host, env = process.env) => {
1897
- if (host === "windows") {
1898
- return join8(env.LOCALAPPDATA ?? join8(homedir3(), "AppData", "Local"), "AbsoluteJS", "Android", "Sdk");
2477
+ }, absoluteManagedAndroidSdkRoot = (host2, env = process.env) => {
2478
+ if (host2 === "windows") {
2479
+ return join9(env.LOCALAPPDATA ?? join9(homedir3(), "AppData", "Local"), "AbsoluteJS", "Android", "Sdk");
1899
2480
  }
1900
- if (host === "wsl") {
2481
+ if (host2 === "wsl") {
1901
2482
  const localAppData = windowsLocalAppDataFromWsl();
1902
2483
  if (localAppData) {
1903
- return join8(localAppData, "AbsoluteJS", "Android", "Sdk");
2484
+ return join9(localAppData, "AbsoluteJS", "Android", "Sdk");
1904
2485
  }
1905
2486
  }
1906
- return join8(homedir3(), ".absolutejs", "android-sdk");
2487
+ return join9(homedir3(), ".absolutejs", "android-sdk");
1907
2488
  }, pathExists = async (path) => {
1908
2489
  try {
1909
2490
  await access3(path);
@@ -1919,18 +2500,18 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1919
2500
  if (platform2 === "linux" && wsl)
1920
2501
  return "wsl";
1921
2502
  return "linux";
1922
- }, executableNames = (host, name) => {
1923
- if (host === "wsl")
2503
+ }, executableNames = (host2, name) => {
2504
+ if (host2 === "wsl")
1924
2505
  return [`${name}.exe`, `${name}.bat`, name];
1925
- if (host === "windows")
2506
+ if (host2 === "windows")
1926
2507
  return [name, `${name}.exe`, `${name}.bat`];
1927
2508
  return [name];
1928
- }, findExecutable = async (name, paths, options, host) => {
2509
+ }, findExecutable = async (name, paths, options, host2) => {
1929
2510
  const existing = await Promise.all(paths.map(async (path) => await options.exists(path) ? path : undefined));
1930
2511
  const configured = existing.find((path) => path !== undefined);
1931
2512
  if (configured)
1932
2513
  return configured;
1933
- for (const candidate of executableNames(host, name)) {
2514
+ for (const candidate of executableNames(host2, name)) {
1934
2515
  const path = options.which(candidate);
1935
2516
  if (path)
1936
2517
  return path;
@@ -1950,23 +2531,23 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1950
2531
  status: "fail"
1951
2532
  }, inspectAbsoluteMobileToolchain = async (input = {}) => {
1952
2533
  const env = input.env ?? process.env;
1953
- const host = input.host ?? detectAbsoluteMobileHost();
2534
+ const host2 = input.host ?? detectAbsoluteMobileHost();
1954
2535
  const exists2 = input.exists ?? pathExists;
1955
2536
  const which = input.which ?? ((command) => Bun.which(command));
1956
2537
  const capture = input.capture ?? captureCommand;
1957
- const androidRoot = input.androidRoot === null ? undefined : input.androidRoot ?? env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host, env);
1958
- const windowsAndroidTools = host === "windows" || host === "wsl";
1959
- const android = (segments) => androidRoot ? join8(androidRoot, ...segments) : undefined;
2538
+ const androidRoot = input.androidRoot === null ? undefined : input.androidRoot ?? env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2, env);
2539
+ const windowsAndroidTools = host2 === "windows" || host2 === "wsl";
2540
+ const android = (segments) => androidRoot ? join9(androidRoot, ...segments) : undefined;
1960
2541
  const paths = (values) => values.filter((value) => Boolean(value));
1961
2542
  const adb = await findExecutable("adb", paths([
1962
2543
  android(["platform-tools", windowsAndroidTools ? "adb.exe" : "adb"])
1963
- ]), { exists: exists2, which }, host);
2544
+ ]), { exists: exists2, which }, host2);
1964
2545
  const emulator = await findExecutable("emulator", paths([
1965
2546
  android([
1966
2547
  "emulator",
1967
2548
  windowsAndroidTools ? "emulator.exe" : "emulator"
1968
2549
  ])
1969
- ]), { exists: exists2, which }, host);
2550
+ ]), { exists: exists2, which }, host2);
1970
2551
  const sdkmanager = await findExecutable("sdkmanager", paths([
1971
2552
  android([
1972
2553
  "cmdline-tools",
@@ -1974,7 +2555,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1974
2555
  "bin",
1975
2556
  windowsAndroidTools ? "sdkmanager.bat" : "sdkmanager"
1976
2557
  ])
1977
- ]), { exists: exists2, which }, host);
2558
+ ]), { exists: exists2, which }, host2);
1978
2559
  const avdmanager = await findExecutable("avdmanager", paths([
1979
2560
  android([
1980
2561
  "cmdline-tools",
@@ -1982,12 +2563,12 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1982
2563
  "bin",
1983
2564
  windowsAndroidTools ? "avdmanager.bat" : "avdmanager"
1984
2565
  ])
1985
- ]), { exists: exists2, which }, host);
1986
- const java = await findExecutable("java", [], { exists: exists2, which }, host);
2566
+ ]), { exists: exists2, which }, host2);
2567
+ const java = await findExecutable("java", [], { exists: exists2, which }, host2);
1987
2568
  const checks = [
1988
2569
  {
1989
2570
  id: "host",
1990
- label: `Development host: ${host}`,
2571
+ label: `Development host: ${host2}`,
1991
2572
  platform: "host",
1992
2573
  status: "pass"
1993
2574
  },
@@ -2008,7 +2589,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
2008
2589
  status: hasManagedAvd ? "pass" : "fail"
2009
2590
  });
2010
2591
  }
2011
- if (host === "wsl") {
2592
+ if (host2 === "wsl") {
2012
2593
  checks.push({
2013
2594
  id: "android.virtualization",
2014
2595
  label: adb?.endsWith(".exe") ? "Windows-host Android bridge available to WSL" : "WSL requires a Windows-host emulator bridge or Linux KVM",
@@ -2016,7 +2597,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
2016
2597
  remediation: adb?.endsWith(".exe") ? undefined : "Expose the Windows Android SDK adb.exe to WSL, or enable /dev/kvm for a Linux SDK.",
2017
2598
  status: adb?.endsWith(".exe") ? "pass" : "warn"
2018
2599
  });
2019
- } else if (host === "linux") {
2600
+ } else if (host2 === "linux") {
2020
2601
  const hasKvm = await exists2("/dev/kvm");
2021
2602
  checks.push({
2022
2603
  id: "android.virtualization",
@@ -2026,7 +2607,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
2026
2607
  status: hasKvm ? "pass" : "warn"
2027
2608
  });
2028
2609
  }
2029
- if (host !== "macos") {
2610
+ if (host2 !== "macos") {
2030
2611
  checks.push({
2031
2612
  id: "ios.simulator",
2032
2613
  label: "iOS Simulator requires macOS and Xcode",
@@ -2035,8 +2616,8 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
2035
2616
  });
2036
2617
  return checks;
2037
2618
  }
2038
- const xcrun = await findExecutable("xcrun", [], { exists: exists2, which }, host);
2039
- const xcodebuild = await findExecutable("xcodebuild", [], { exists: exists2, which }, host);
2619
+ const xcrun = await findExecutable("xcrun", [], { exists: exists2, which }, host2);
2620
+ const xcodebuild = await findExecutable("xcodebuild", [], { exists: exists2, which }, host2);
2040
2621
  checks.push(toolCheck("ios.xcrun", "Xcode command runner", "ios", xcrun, "Install Xcode and select it with xcode-select."), toolCheck("ios.xcodebuild", "Xcode build system", "ios", xcodebuild, "Install Xcode and select it with xcode-select."));
2041
2622
  if (xcrun) {
2042
2623
  const runtimes = capture([
@@ -2063,7 +2644,7 @@ var init_emulatorDoctor = __esm(() => {
2063
2644
 
2064
2645
  // src/mobile/capacitorProject.ts
2065
2646
  import { access as access4, readFile as readFile3, rename as rename2, writeFile as writeFile2 } from "fs/promises";
2066
- import { relative as relative2, resolve as resolve5 } from "path";
2647
+ import { relative as relative2, resolve as resolve6 } from "path";
2067
2648
  var CONFIG_FILE = "capacitor.config.ts", portableRelative2 = (root, path) => relative2(root, path).replaceAll("\\", "/"), capacitorConfigSource = (config, projectRoot) => `import type { CapacitorConfig } from '@capacitor/cli';
2068
2649
 
2069
2650
  const config: CapacitorConfig = {
@@ -2087,8 +2668,8 @@ export default config;
2087
2668
  return false;
2088
2669
  }
2089
2670
  }, writeAbsoluteCapacitorConfig = async (config, options) => {
2090
- const projectRoot = resolve5(options.projectRoot);
2091
- const destination = resolve5(projectRoot, CONFIG_FILE);
2671
+ const projectRoot = resolve6(options.projectRoot);
2672
+ const destination = resolve6(projectRoot, CONFIG_FILE);
2092
2673
  const source = capacitorConfigSource(config, projectRoot);
2093
2674
  if (await exists2(destination)) {
2094
2675
  const current = await readFile3(destination, "utf8");
@@ -2121,11 +2702,11 @@ import {
2121
2702
  } from "fs/promises";
2122
2703
  import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
2123
2704
  import {
2124
- dirname as dirname4,
2705
+ dirname as dirname5,
2125
2706
  isAbsolute,
2126
- join as join9,
2707
+ join as join10,
2127
2708
  relative as relative3,
2128
- resolve as resolve6,
2709
+ resolve as resolve7,
2129
2710
  sep,
2130
2711
  win32
2131
2712
  } from "path";
@@ -2295,21 +2876,21 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2295
2876
  return;
2296
2877
  throw new DOMException("Android development startup was cancelled.", "AbortError");
2297
2878
  }, journalPaths = (projectRoot) => {
2298
- const root = join9(projectRoot, ".absolutejs", "mobile", "dev-session");
2879
+ const root = join10(projectRoot, ".absolutejs", "mobile", "dev-session");
2299
2880
  return {
2300
- backup: join9(root, "capacitor.config.backup.json"),
2301
- caBackup: join9(root, "absolutejs_dev_ca.backup.pem"),
2302
- journal: join9(root, "journal.json"),
2303
- manifestBackup: join9(root, "AndroidManifest.backup.xml"),
2304
- networkConfigBackup: join9(root, "absolutejs_dev_network_security.backup.xml"),
2881
+ backup: join10(root, "capacitor.config.backup.json"),
2882
+ caBackup: join10(root, "absolutejs_dev_ca.backup.pem"),
2883
+ journal: join10(root, "journal.json"),
2884
+ manifestBackup: join10(root, "AndroidManifest.backup.xml"),
2885
+ networkConfigBackup: join10(root, "absolutejs_dev_network_security.backup.xml"),
2305
2886
  root
2306
2887
  };
2307
2888
  }, isInside = (root, path) => {
2308
- const resolvedRoot = resolve6(root);
2309
- const resolvedPath = resolve6(path);
2889
+ const resolvedRoot = resolve7(root);
2890
+ const resolvedPath = resolve7(path);
2310
2891
  const relativePath = relative3(resolvedRoot, resolvedPath);
2311
2892
  return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
2312
- }, isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), nativeCachePath = (projectRoot) => join9(projectRoot, ".absolutejs", "mobile", "cache", "android-debug.json"), parseNativeCache = (value) => {
2893
+ }, isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), nativeCachePath = (projectRoot) => join10(projectRoot, ".absolutejs", "mobile", "cache", "android-debug.json"), parseNativeCache = (value) => {
2313
2894
  if (!isRecord(value))
2314
2895
  return null;
2315
2896
  const { appId, fingerprint, format, installations } = value;
@@ -2328,7 +2909,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2328
2909
  }, readNativeCache = async (projectRoot) => readFile4(nativeCachePath(projectRoot), "utf8").then((source) => parseNativeCache(JSON.parse(source))).catch(() => null), writeNativeCache = async (projectRoot, cache) => {
2329
2910
  const destination = nativeCachePath(projectRoot);
2330
2911
  const temporary = `${destination}.${process.pid}.${randomUUID2()}.tmp`;
2331
- await mkdir2(dirname4(destination), { recursive: true });
2912
+ await mkdir2(dirname5(destination), { recursive: true });
2332
2913
  try {
2333
2914
  await writeFile3(temporary, `${JSON.stringify(cache, null, "\t")}
2334
2915
  `, {
@@ -2341,11 +2922,11 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2341
2922
  });
2342
2923
  }
2343
2924
  }, nativeDependencySources = async (nativeDirectory) => {
2344
- const settings = await readFile4(join9(nativeDirectory, "capacitor.settings.gradle"), "utf8");
2925
+ const settings = await readFile4(join10(nativeDirectory, "capacitor.settings.gradle"), "utf8");
2345
2926
  const pattern = new RegExp(CAPACITOR_PROJECT_DIRECTORY_PATTERN.source, CAPACITOR_PROJECT_DIRECTORY_PATTERN.flags);
2346
2927
  const dependencies = [...settings.matchAll(pattern)].map((match) => ({
2347
2928
  name: (match[1] ?? "").slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_"),
2348
- source: resolve6(nativeDirectory, match[2] ?? "")
2929
+ source: resolve7(nativeDirectory, match[2] ?? "")
2349
2930
  }));
2350
2931
  if (dependencies.length === 0) {
2351
2932
  throw new Error("Capacitor Android settings did not declare any native dependencies.");
@@ -2381,7 +2962,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2381
2962
  }, collectNativeDirectory = async (root, label, directory, ignorePublicBundle) => {
2382
2963
  const entries = await readdir2(directory, { withFileTypes: true });
2383
2964
  entries.sort((left, right) => left.name.localeCompare(right.name));
2384
- const records = await Promise.all(entries.map((entry) => collectNativePath(root, label, join9(directory, entry.name), entry.isDirectory(), entry.isFile(), entry.isSymbolicLink(), ignorePublicBundle)));
2965
+ const records = await Promise.all(entries.map((entry) => collectNativePath(root, label, join10(directory, entry.name), entry.isDirectory(), entry.isFile(), entry.isSymbolicLink(), ignorePublicBundle)));
2385
2966
  return records.flat();
2386
2967
  }, hashNativeTree = async (root, label, ignorePublicBundle) => {
2387
2968
  const resolvedRoot = await realpath(root);
@@ -2434,7 +3015,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2434
3015
  await rm2(file.path, { force: true });
2435
3016
  return;
2436
3017
  }
2437
- await mkdir2(dirname4(file.path), { recursive: true });
3018
+ await mkdir2(dirname5(file.path), { recursive: true });
2438
3019
  await copyFile(file.backupPath, file.path);
2439
3020
  }, repairAbsoluteAndroidDevSession = async (projectRoot) => {
2440
3021
  const paths = journalPaths(projectRoot);
@@ -2447,11 +3028,11 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2447
3028
  throw new Error(`Refusing unsafe or invalid mobile dev journal at ${paths.journal}.`);
2448
3029
  }
2449
3030
  if (await pathExists2(journal.backupPath)) {
2450
- await mkdir2(dirname4(journal.nativeConfigPath), { recursive: true });
3031
+ await mkdir2(dirname5(journal.nativeConfigPath), { recursive: true });
2451
3032
  await copyFile(journal.backupPath, journal.nativeConfigPath);
2452
3033
  }
2453
3034
  if (journal.manifestBackupPath && journal.nativeManifestPath && await pathExists2(journal.manifestBackupPath)) {
2454
- await mkdir2(dirname4(journal.nativeManifestPath), { recursive: true });
3035
+ await mkdir2(dirname5(journal.nativeManifestPath), { recursive: true });
2455
3036
  await copyFile(journal.manifestBackupPath, journal.nativeManifestPath);
2456
3037
  }
2457
3038
  await Promise.all((journal.projectedFiles ?? []).map(restoreAndroidProjectedFile));
@@ -2510,11 +3091,11 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2510
3091
  await mkdir2(paths.root, { recursive: true });
2511
3092
  await writeFile3(paths.backup, source, { flag: "wx" });
2512
3093
  await writeFile3(paths.manifestBackup, manifestSource, { flag: "wx" });
2513
- const resourceRoot = join9(dirname4(nativeManifestPath), "res");
2514
- const caPath = join9(resourceRoot, "raw", "absolutejs_dev_ca.pem");
3094
+ const resourceRoot = join10(dirname5(nativeManifestPath), "res");
3095
+ const caPath = join10(resourceRoot, "raw", "absolutejs_dev_ca.pem");
2515
3096
  const existingNetworkConfig = manifestSource.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
2516
3097
  const networkConfigName = existingNetworkConfig ?? "absolutejs_dev_network_security";
2517
- const networkConfigPath = join9(resourceRoot, "xml", `${networkConfigName}.xml`);
3098
+ const networkConfigPath = join10(resourceRoot, "xml", `${networkConfigName}.xml`);
2518
3099
  const backupProjectedFile = async ([path, backupPath]) => {
2519
3100
  if (!await pathExists2(path))
2520
3101
  return { path };
@@ -2551,8 +3132,8 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2551
3132
  `);
2552
3133
  if (https && certificateAuthorityPath) {
2553
3134
  await Promise.all([
2554
- mkdir2(dirname4(caPath), { recursive: true }),
2555
- mkdir2(dirname4(networkConfigPath), { recursive: true })
3135
+ mkdir2(dirname5(caPath), { recursive: true }),
3136
+ mkdir2(dirname5(networkConfigPath), { recursive: true })
2556
3137
  ]);
2557
3138
  await copyFile(certificateAuthorityPath, caPath);
2558
3139
  const existingNetworkConfigSource = existingNetworkConfig ? await readFile4(networkConfigPath, "utf8") : `<?xml version="1.0" encoding="utf-8"?>
@@ -2607,12 +3188,12 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2607
3188
  }
2608
3189
  return result.stdout.trim();
2609
3190
  }, mirroredCapacitorDependencies = async (project, capture) => {
2610
- const settingsPath = join9(project.nativeDirectory, "capacitor.settings.gradle");
3191
+ const settingsPath = join10(project.nativeDirectory, "capacitor.settings.gradle");
2611
3192
  const settings = await readFile4(settingsPath, "utf8");
2612
3193
  const dependencies = [];
2613
3194
  const rewrittenSettings = settings.replace(CAPACITOR_PROJECT_DIRECTORY_PATTERN, (_statement, projectName, sourcePath) => {
2614
3195
  const name = projectName.slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_");
2615
- const source = resolve6(project.nativeDirectory, sourcePath);
3196
+ const source = resolve7(project.nativeDirectory, sourcePath);
2616
3197
  dependencies.push({
2617
3198
  name,
2618
3199
  windowsSource: windowsPathFromWsl(source, capture)
@@ -2653,7 +3234,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2653
3234
  ].join("; ");
2654
3235
  return Buffer.from(source, "utf16le").toString("base64");
2655
3236
  }, gradleArtifactPath = (nativeDirectory, task, windows = false) => {
2656
- const pathJoin = windows ? win32.join : join9;
3237
+ const pathJoin = windows ? win32.join : join10;
2657
3238
  if (task === "assembleDebug") {
2658
3239
  return pathJoin(nativeDirectory, "app", "build", "outputs", "apk", "debug", "app-debug.apk");
2659
3240
  }
@@ -2666,7 +3247,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2666
3247
  if (task !== "assembleRelease" || await pathExists2(primary)) {
2667
3248
  return primary;
2668
3249
  }
2669
- return join9(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
3250
+ return join10(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
2670
3251
  }, buildAbsoluteAndroidGradleArtifact = async (options) => {
2671
3252
  const { project, task } = options;
2672
3253
  const capture = options.capture ?? captureCommand2;
@@ -2676,7 +3257,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2676
3257
  if (project.host === "wsl") {
2677
3258
  const windowsSource = windowsPathFromWsl(project.nativeDirectory, capture);
2678
3259
  const buildId = Bun.hash(project.projectRoot).toString(HASH_RADIX);
2679
- const managedBuildDirectory = resolve6(project.androidRoot, "..", "..", "Builds", `${project.config.appId}-${buildId}`);
3260
+ const managedBuildDirectory = resolve7(project.androidRoot, "..", "..", "Builds", `${project.config.appId}-${buildId}`);
2680
3261
  const windowsDirectory = windowsPathFromWsl(managedBuildDirectory, capture);
2681
3262
  const windowsAndroidRoot = windowsPathFromWsl(project.androidRoot, capture);
2682
3263
  const { dependencies, rewrittenSettings } = await mirroredCapacitorDependencies(project, capture);
@@ -2859,10 +3440,10 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2859
3440
  "chromium:I"
2860
3441
  ], { env }, onLine);
2861
3442
  }, prepareAbsoluteAndroidDevProject = async (config, options) => {
2862
- const projectRoot = resolve6(options.projectRoot);
2863
- const host = detectAbsoluteMobileHost();
2864
- const androidRoot = process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host);
2865
- const checks = await inspectAbsoluteMobileToolchain({ androidRoot, host });
3443
+ const projectRoot = resolve7(options.projectRoot);
3444
+ const host2 = detectAbsoluteMobileHost();
3445
+ const androidRoot = process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2);
3446
+ const checks = await inspectAbsoluteMobileToolchain({ androidRoot, host: host2 });
2866
3447
  const deviceOnlyChecks = new Set([
2867
3448
  "android.avd",
2868
3449
  "android.avdmanager",
@@ -2878,18 +3459,18 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2878
3459
  if (!adb || options.target !== "device" && !emulator) {
2879
3460
  throw new Error("Android SDK tools disappeared after readiness checks.");
2880
3461
  }
2881
- const cap = join9(projectRoot, "node_modules", ".bin", host === "windows" ? "cap.cmd" : "cap");
3462
+ const cap = join10(projectRoot, "node_modules", ".bin", host2 === "windows" ? "cap.cmd" : "cap");
2882
3463
  if (!await pathExists2(cap)) {
2883
3464
  throw new Error("Capacitor CLI is not installed. Run absolute mobile init first.");
2884
3465
  }
2885
3466
  await writeAbsoluteCapacitorConfig(config, { projectRoot });
2886
3467
  await mkdir2(config.bundleDirectory, { recursive: true });
2887
- const placeholder = join9(config.bundleDirectory, "index.html");
3468
+ const placeholder = join10(config.bundleDirectory, "index.html");
2888
3469
  if (!await pathExists2(placeholder)) {
2889
3470
  await writeFile3(placeholder, `<!doctype html><title>AbsoluteJS mobile development</title>
2890
3471
  `);
2891
3472
  }
2892
- const nativeDirectory = join9(config.nativeProjectDirectory, "android");
3473
+ const nativeDirectory = join10(config.nativeProjectDirectory, "android");
2893
3474
  if (!await pathExists2(nativeDirectory)) {
2894
3475
  if (!options.createNativeProject) {
2895
3476
  throw new Error("Android native project has not been created.");
@@ -2907,7 +3488,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2907
3488
  cap,
2908
3489
  config,
2909
3490
  ...emulator ? { emulator } : {},
2910
- host,
3491
+ host: host2,
2911
3492
  nativeDirectory,
2912
3493
  projectRoot
2913
3494
  };
@@ -2950,8 +3531,8 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2950
3531
  await requireSuccess([project.cap, "sync", "android"], "Capacitor Android synchronization", run, { cwd: project.projectRoot, env, signal: options.signal });
2951
3532
  throwIfAborted(options.signal);
2952
3533
  transition("configuring");
2953
- const nativeConfigPath = join9(project.nativeDirectory, "app", "src", "main", "assets", "capacitor.config.json");
2954
- const nativeManifestPath = join9(project.nativeDirectory, "app", "src", "main", "AndroidManifest.xml");
3534
+ const nativeConfigPath = join10(project.nativeDirectory, "app", "src", "main", "assets", "capacitor.config.json");
3535
+ const nativeManifestPath = join10(project.nativeDirectory, "app", "src", "main", "AndroidManifest.xml");
2955
3536
  let connectedSerial;
2956
3537
  let nativeLogs = null;
2957
3538
  const closeNativeLogs = async () => {
@@ -3110,7 +3691,7 @@ var init_androidEmulatorController = __esm(() => {
3110
3691
  import { createHash as createHash3 } from "crypto";
3111
3692
  import { cp as cp2, mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile5, rm as rm3 } from "fs/promises";
3112
3693
  import { tmpdir } from "os";
3113
- import { basename as basename3, join as join10 } from "path";
3694
+ import { basename as basename3, join as join11 } from "path";
3114
3695
  var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION = "15859902", LICENSE_ACCEPTANCE_RESPONSES = 100, COMMAND_LINE_TOOLS, defaultRun = async (command, options = {}) => {
3115
3696
  const subprocess = Bun.spawn(command, {
3116
3697
  env: options.env,
@@ -3142,33 +3723,33 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3142
3723
  exitCode: result.exitCode,
3143
3724
  stdout: result.stdout.toString()
3144
3725
  };
3145
- }, commandPath = (root, host, tool) => join10(root, "cmdline-tools", "latest", "bin", host === "windows" || host === "wsl" ? `${tool}.bat` : tool), executablePath = (root, host, directory, tool) => join10(root, directory, host === "windows" || host === "wsl" ? `${tool}.exe` : tool), windowsPath = (path) => {
3726
+ }, commandPath = (root, host2, tool) => join11(root, "cmdline-tools", "latest", "bin", host2 === "windows" || host2 === "wsl" ? `${tool}.bat` : tool), executablePath = (root, host2, directory, tool) => join11(root, directory, host2 === "windows" || host2 === "wsl" ? `${tool}.exe` : tool), windowsPath = (path) => {
3146
3727
  const match = /^\/mnt\/([a-z])\/(.*)$/i.exec(path);
3147
3728
  if (!match)
3148
3729
  return path;
3149
3730
  return `${match[1]?.toUpperCase()}:\\${match[2]?.replaceAll("/", "\\")}`;
3150
- }, runnableCommand = (host, path, args) => {
3151
- if (host !== "wsl" || !path.endsWith(".bat"))
3731
+ }, runnableCommand = (host2, path, args) => {
3732
+ if (host2 !== "wsl" || !path.endsWith(".bat"))
3152
3733
  return [path, ...args];
3153
3734
  return ["cmd.exe", "/d", "/c", windowsPath(path), ...args];
3154
- }, systemImageFor = (host, arch2) => {
3155
- const imageArch = host === "macos" && arch2 === "arm64" ? "arm64-v8a" : "x86_64";
3735
+ }, systemImageFor = (host2, arch2) => {
3736
+ const imageArch = host2 === "macos" && arch2 === "arm64" ? "arm64-v8a" : "x86_64";
3156
3737
  return `system-images;android-${ANDROID_API};google_apis;${imageArch}`;
3157
3738
  }, planAbsoluteMobileEmulatorInstall = (platform2, input = {}) => {
3158
- const host = input.host ?? detectAbsoluteMobileHost();
3739
+ const host2 = input.host ?? detectAbsoluteMobileHost();
3159
3740
  const env = {
3160
3741
  ...process.env,
3161
3742
  ...input.env
3162
3743
  };
3163
- const androidRoot = env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host, env);
3744
+ const androidRoot = env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2, env);
3164
3745
  if (platform2 === "ios") {
3165
3746
  return {
3166
3747
  androidRoot,
3167
- host,
3748
+ host: host2,
3168
3749
  platform: platform2,
3169
3750
  steps: [
3170
3751
  {
3171
- detail: host === "macos" ? "Use Xcode to download the current iOS Simulator runtime." : "iOS Simulator setup must run on a macOS host.",
3752
+ detail: host2 === "macos" ? "Use Xcode to download the current iOS Simulator runtime." : "iOS Simulator setup must run on a macOS host.",
3172
3753
  id: "ios.runtime",
3173
3754
  label: "Install iOS Simulator runtime"
3174
3755
  }
@@ -3177,7 +3758,7 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3177
3758
  }
3178
3759
  return {
3179
3760
  androidRoot,
3180
- host,
3761
+ host: host2,
3181
3762
  platform: platform2,
3182
3763
  steps: [
3183
3764
  {
@@ -3197,15 +3778,15 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3197
3778
  }
3198
3779
  ]
3199
3780
  };
3200
- }, releaseFor = (host, arch2) => {
3201
- let platform2 = host;
3202
- if (host === "wsl")
3781
+ }, releaseFor = (host2, arch2) => {
3782
+ let platform2 = host2;
3783
+ if (host2 === "wsl")
3203
3784
  platform2 = "windows";
3204
- if (host === "macos")
3785
+ if (host2 === "macos")
3205
3786
  platform2 = "darwin";
3206
3787
  const release = COMMAND_LINE_TOOLS[`${platform2}-${arch2}`];
3207
3788
  if (!release) {
3208
- throw new Error(`Automatic Android command-line-tools installation is not available for ${host}/${arch2}.`);
3789
+ throw new Error(`Automatic Android command-line-tools installation is not available for ${host2}/${arch2}.`);
3209
3790
  }
3210
3791
  return release;
3211
3792
  }, installCommandLineTools = async (plan, input) => {
@@ -3216,10 +3797,10 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3216
3797
  if (digest !== release.sha256) {
3217
3798
  throw new Error(`Android command-line-tools checksum mismatch: expected ${release.sha256}, received ${digest}.`);
3218
3799
  }
3219
- const temporary = await mkdtemp2(join10(tmpdir(), "absolutejs-android-sdk-"));
3800
+ const temporary = await mkdtemp2(join11(tmpdir(), "absolutejs-android-sdk-"));
3220
3801
  try {
3221
- const archive = join10(temporary, "command-line-tools.zip");
3222
- const extracted = join10(temporary, "extracted");
3802
+ const archive = join11(temporary, "command-line-tools.zip");
3803
+ const extracted = join11(temporary, "extracted");
3223
3804
  await Bun.write(archive, bytes);
3224
3805
  await mkdir3(extracted, { recursive: true });
3225
3806
  const extraction = plan.host === "windows" ? [
@@ -3236,12 +3817,12 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3236
3817
  if (await input.run(extraction) !== 0) {
3237
3818
  throw new Error("Failed to extract Android command-line tools.");
3238
3819
  }
3239
- const destination = join10(plan.androidRoot, "cmdline-tools", "latest");
3240
- await mkdir3(join10(plan.androidRoot, "cmdline-tools"), {
3820
+ const destination = join11(plan.androidRoot, "cmdline-tools", "latest");
3821
+ await mkdir3(join11(plan.androidRoot, "cmdline-tools"), {
3241
3822
  recursive: true
3242
3823
  });
3243
3824
  await rm3(destination, { force: true, recursive: true });
3244
- await cp2(join10(extracted, "cmdline-tools"), destination, {
3825
+ await cp2(join11(extracted, "cmdline-tools"), destination, {
3245
3826
  recursive: true
3246
3827
  });
3247
3828
  } finally {
@@ -3251,9 +3832,9 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3251
3832
  if (await run(command, options) !== 0) {
3252
3833
  throw new Error(`${label} failed.`);
3253
3834
  }
3254
- }, installJavaRuntime = async (host, run, which) => {
3255
- if (host === "windows" || host === "wsl") {
3256
- const winget = host === "wsl" ? "winget.exe" : "winget";
3835
+ }, installJavaRuntime = async (host2, run, which) => {
3836
+ if (host2 === "windows" || host2 === "wsl") {
3837
+ const winget = host2 === "wsl" ? "winget.exe" : "winget";
3257
3838
  if (!which(winget)) {
3258
3839
  throw new Error("Java 21 is required. Install Temurin 21 or make winget available, then run mobile doctor --fix again.");
3259
3840
  }
@@ -3266,10 +3847,10 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3266
3847
  "--accept-package-agreements",
3267
3848
  "--accept-source-agreements"
3268
3849
  ];
3269
- await ensureCommand(host === "wsl" ? ["cmd.exe", "/d", "/c", ...command] : command, "Java 21 installation", run);
3850
+ await ensureCommand(host2 === "wsl" ? ["cmd.exe", "/d", "/c", ...command] : command, "Java 21 installation", run);
3270
3851
  return;
3271
3852
  }
3272
- if (host === "macos") {
3853
+ if (host2 === "macos") {
3273
3854
  if (!which("brew")) {
3274
3855
  throw new Error("Java 21 is required. Install Homebrew or Temurin 21, then run mobile doctor --fix again.");
3275
3856
  }
@@ -3426,7 +4007,7 @@ import {
3426
4007
  stat,
3427
4008
  writeFile as writeFile4
3428
4009
  } from "fs/promises";
3429
- import { dirname as dirname5, isAbsolute as isAbsolute2, join as join11, relative as relative4, resolve as resolve7, sep as sep2 } from "path";
4010
+ import { dirname as dirname6, isAbsolute as isAbsolute2, join as join12, relative as relative4, resolve as resolve8, sep as sep2 } from "path";
3430
4011
  var developmentTeamArgument = (value) => {
3431
4012
  if (value === undefined)
3432
4013
  return;
@@ -3483,7 +4064,7 @@ var developmentTeamArgument = (value) => {
3483
4064
  }, ignoredFingerprintDirectories, fingerprintFiles = async (root, current = root, options = {}) => {
3484
4065
  const entries = await readdir3(current, { withFileTypes: true });
3485
4066
  const nested = await Promise.all(entries.sort((left, right) => left.name.localeCompare(right.name)).map(async (entry) => {
3486
- const path = join11(current, entry.name);
4067
+ const path = join12(current, entry.name);
3487
4068
  const projectRelative = relative4(root, path).replaceAll("\\", "/");
3488
4069
  const ignored = entry.isDirectory() && (ignoredFingerprintDirectories.has(entry.name) || projectRelative === "App/App/public" && options.includePublicBundle !== true);
3489
4070
  if (ignored)
@@ -3505,8 +4086,8 @@ var developmentTeamArgument = (value) => {
3505
4086
  });
3506
4087
  return hasher.digest("hex");
3507
4088
  }, safeOutputDirectory = (projectRoot, requested) => {
3508
- const root = resolve7(projectRoot);
3509
- const output = resolve7(root, requested ?? ".absolutejs/mobile/releases/ios");
4089
+ const root = resolve8(projectRoot);
4090
+ const output = resolve8(root, requested ?? ".absolutejs/mobile/releases/ios");
3510
4091
  const projectRelative = relative4(root, output);
3511
4092
  if (projectRelative === ".." || projectRelative.startsWith(`..${sep2}`) || isAbsolute2(projectRelative)) {
3512
4093
  throw new TypeError("mobile build --outdir must remain inside the project.");
@@ -3517,7 +4098,7 @@ var developmentTeamArgument = (value) => {
3517
4098
  return;
3518
4099
  const entries = await readdir3(root, { withFileTypes: true });
3519
4100
  const matches = await Promise.all(entries.map(async (entry) => {
3520
- const path = join11(root, entry.name);
4101
+ const path = join12(root, entry.name);
3521
4102
  if (entry.isDirectory() && entry.name.endsWith(extension))
3522
4103
  return path;
3523
4104
  if (entry.isFile() && entry.name.endsWith(extension))
@@ -3540,10 +4121,10 @@ var developmentTeamArgument = (value) => {
3540
4121
  throw new TypeError("iOS build number must be a positive integer.");
3541
4122
  return value;
3542
4123
  }, installRelease = async (artifactPath, metadata, outputRoot) => {
3543
- const releaseRoot = join11(outputRoot, metadata.releaseId);
3544
- const destination = join11(releaseRoot, "App.ipa");
4124
+ const releaseRoot = join12(outputRoot, metadata.releaseId);
4125
+ const destination = join12(releaseRoot, "App.ipa");
3545
4126
  if (await pathExists3(releaseRoot)) {
3546
- const value = JSON.parse(await readFile6(join11(releaseRoot, "release.json"), "utf8"));
4127
+ const value = JSON.parse(await readFile6(join12(releaseRoot, "release.json"), "utf8"));
3547
4128
  if (!isRecord2(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
3548
4129
  throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
3549
4130
  }
@@ -3562,15 +4143,15 @@ var developmentTeamArgument = (value) => {
3562
4143
  releaseRoot
3563
4144
  };
3564
4145
  }
3565
- await mkdir4(dirname5(releaseRoot), { recursive: true });
3566
- const staging = await mkdtemp3(join11(dirname5(releaseRoot), ".ios-stage-"));
4146
+ await mkdir4(dirname6(releaseRoot), { recursive: true });
4147
+ const staging = await mkdtemp3(join12(dirname6(releaseRoot), ".ios-stage-"));
3567
4148
  try {
3568
- await copyFile2(artifactPath, join11(staging, "App.ipa"));
4149
+ await copyFile2(artifactPath, join12(staging, "App.ipa"));
3569
4150
  const complete = {
3570
4151
  ...metadata,
3571
4152
  artifact: "App.ipa"
3572
4153
  };
3573
- await writeFile4(join11(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
4154
+ await writeFile4(join12(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
3574
4155
  `, { flag: "wx" });
3575
4156
  await rename4(staging, releaseRoot);
3576
4157
  return { artifactPath: destination, metadata: complete, releaseRoot };
@@ -3587,22 +4168,22 @@ var developmentTeamArgument = (value) => {
3587
4168
  const marketingVersion = options.config.iosVersion;
3588
4169
  if (!marketingVersion)
3589
4170
  throw new TypeError("iOS release builds require mobile.ios.version in absolutejs.config.ts.");
3590
- const manifest = requireManifest(JSON.parse(await readFile6(join11(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
4171
+ const manifest = requireManifest(JSON.parse(await readFile6(join12(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
3591
4172
  if (manifest.appId !== options.config.appId)
3592
4173
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
3593
- const nativeDirectory = join11(options.config.nativeProjectDirectory, "ios");
4174
+ const nativeDirectory = join12(options.config.nativeProjectDirectory, "ios");
3594
4175
  let buildNumber = requireBuildNumber(options.buildNumber);
3595
4176
  if (options.prepareBuildNumber) {
3596
4177
  const nativeFingerprint = await fingerprintAbsoluteIosNativeProject(nativeDirectory);
3597
4178
  const buildIdentity = createHash4("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}\x00${marketingVersion}`).digest("hex");
3598
4179
  buildNumber = requireBuildNumber(await options.prepareBuildNumber(buildIdentity));
3599
4180
  }
3600
- const stagingParent = resolve7(options.projectRoot, ".absolutejs/mobile");
4181
+ const stagingParent = resolve8(options.projectRoot, ".absolutejs/mobile");
3601
4182
  await mkdir4(stagingParent, { recursive: true });
3602
- const staging = await mkdtemp3(join11(stagingParent, ".ios-build-"));
3603
- const archivePath = join11(staging, "App.xcarchive");
3604
- const exportPath = join11(staging, "export");
3605
- const exportPlist = join11(staging, "ExportOptions.plist");
4183
+ const staging = await mkdtemp3(join12(stagingParent, ".ios-build-"));
4184
+ const archivePath = join12(staging, "App.xcarchive");
4185
+ const exportPath = join12(staging, "export");
4186
+ const exportPlist = join12(staging, "ExportOptions.plist");
3606
4187
  await mkdir4(exportPath, { recursive: true });
3607
4188
  await writeFile4(exportPlist, exportOptions());
3608
4189
  const run = options.run ?? defaultRun2;
@@ -3616,7 +4197,7 @@ var developmentTeamArgument = (value) => {
3616
4197
  const archiveExit = await run([
3617
4198
  "xcodebuild",
3618
4199
  "-workspace",
3619
- join11(nativeDirectory, "App", "App.xcworkspace"),
4200
+ join12(nativeDirectory, "App", "App.xcworkspace"),
3620
4201
  "-scheme",
3621
4202
  "App",
3622
4203
  "-configuration",
@@ -3630,7 +4211,7 @@ var developmentTeamArgument = (value) => {
3630
4211
  ], { cwd: nativeDirectory });
3631
4212
  if (archiveExit !== 0)
3632
4213
  throw new TypeError("Xcode failed to archive the iOS app.");
3633
- const archivedApp = await findByExtension(join11(archivePath, "Products", "Applications"), ".app");
4214
+ const archivedApp = await findByExtension(join12(archivePath, "Products", "Applications"), ".app");
3634
4215
  const capture = options.capture ?? defaultCapture2;
3635
4216
  const signed = archivedApp ? capture([
3636
4217
  "codesign",
@@ -3704,7 +4285,7 @@ import {
3704
4285
  writeFile as writeFile5
3705
4286
  } from "fs/promises";
3706
4287
  import { isIP as isIP2 } from "net";
3707
- import { dirname as dirname6, isAbsolute as isAbsolute3, join as join12, relative as relative5, resolve as resolve8, sep as sep3 } from "path";
4288
+ import { dirname as dirname7, isAbsolute as isAbsolute3, join as join13, relative as relative5, resolve as resolve9, sep as sep3 } from "path";
3708
4289
  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) => {
3709
4290
  try {
3710
4291
  await access7(path);
@@ -3922,15 +4503,15 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
3922
4503
  const leftPro = left.name.includes("Pro") ? 1 : 0;
3923
4504
  return rightPro - leftPro;
3924
4505
  })[0], journalPaths2 = (projectRoot) => {
3925
- const root = join12(projectRoot, ".absolutejs", "mobile", "ios-dev-session");
4506
+ const root = join13(projectRoot, ".absolutejs", "mobile", "ios-dev-session");
3926
4507
  return {
3927
- configBackup: join12(root, "capacitor-config.backup"),
3928
- infoBackup: join12(root, "Info.plist.backup"),
3929
- journal: join12(root, "journal.json"),
4508
+ configBackup: join13(root, "capacitor-config.backup"),
4509
+ infoBackup: join13(root, "Info.plist.backup"),
4510
+ journal: join13(root, "journal.json"),
3930
4511
  root
3931
4512
  };
3932
- }, nativeCachePath2 = (projectRoot) => join12(projectRoot, ".absolutejs", "mobile", "ios-native-cache.json"), isInside2 = (root, path) => {
3933
- const value = relative5(resolve8(root), resolve8(path));
4513
+ }, nativeCachePath2 = (projectRoot) => join13(projectRoot, ".absolutejs", "mobile", "ios-native-cache.json"), isInside2 = (root, path) => {
4514
+ const value = relative5(resolve9(root), resolve9(path));
3934
4515
  return value === "" || !value.startsWith(`..${sep3}`) && value !== ".." && !isAbsolute3(value);
3935
4516
  }, parseJournal2 = (value) => {
3936
4517
  if (!isRecord3(value) || value.format !== DEV_JOURNAL_FORMAT2)
@@ -3986,8 +4567,8 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
3986
4567
  }, writeDevProjection = async (project, port, https, serverHost = "localhost") => {
3987
4568
  const paths = journalPaths2(project.projectRoot);
3988
4569
  await repairAbsoluteIosDevSession(project.projectRoot);
3989
- const nativeConfigPath = join12(project.nativeDirectory, "App", "App", "capacitor.config.json");
3990
- const infoPath = join12(project.nativeDirectory, "App", "App", "Info.plist");
4570
+ const nativeConfigPath = join13(project.nativeDirectory, "App", "App", "capacitor.config.json");
4571
+ const infoPath = join13(project.nativeDirectory, "App", "App", "Info.plist");
3991
4572
  const [configSource, infoSource] = await Promise.all([
3992
4573
  readFile7(nativeConfigPath, "utf8"),
3993
4574
  readFile7(infoPath, "utf8")
@@ -4043,7 +4624,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4043
4624
  }, readNativeCache2 = (projectRoot) => readFile7(nativeCachePath2(projectRoot), "utf8").then((source) => parseNativeCache2(JSON.parse(source))).catch(() => null), writeNativeCache2 = async (projectRoot, cache) => {
4044
4625
  const destination = nativeCachePath2(projectRoot);
4045
4626
  const temporary = `${destination}.${process.pid}.${randomUUID3()}.tmp`;
4046
- await mkdir5(dirname6(destination), { recursive: true });
4627
+ await mkdir5(dirname7(destination), { recursive: true });
4047
4628
  try {
4048
4629
  await writeFile5(temporary, `${JSON.stringify(cache, null, "\t")}
4049
4630
  `, {
@@ -4148,12 +4729,12 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4148
4729
  ]);
4149
4730
  return result.exitCode === 0 && result.stdout.includes(project.config.appId);
4150
4731
  }, buildIosDebugApp = async (project, udid, fingerprint, run, signal) => {
4151
- const derivedDataPath = join12(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash5("sha256").update(project.config.appId).digest("hex").slice(0, 16));
4732
+ const derivedDataPath = join13(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash5("sha256").update(project.config.appId).digest("hex").slice(0, 16));
4152
4733
  await mkdir5(derivedDataPath, { recursive: true });
4153
4734
  await requireSuccess2([
4154
4735
  project.xcodebuild,
4155
4736
  "-workspace",
4156
- join12(project.nativeDirectory, "App", "App.xcworkspace"),
4737
+ join13(project.nativeDirectory, "App", "App.xcworkspace"),
4157
4738
  "-scheme",
4158
4739
  "App",
4159
4740
  "-configuration",
@@ -4164,17 +4745,17 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4164
4745
  derivedDataPath,
4165
4746
  "build"
4166
4747
  ], "iOS simulator build", run, { cwd: project.nativeDirectory, signal });
4167
- const appPath = join12(derivedDataPath, "Build", "Products", "Debug-iphonesimulator", "App.app");
4748
+ const appPath = join13(derivedDataPath, "Build", "Products", "Debug-iphonesimulator", "App.app");
4168
4749
  if (!await pathExists4(appPath))
4169
4750
  throw new Error(`Xcode did not produce the simulator app at ${appPath}.`);
4170
4751
  return appPath;
4171
4752
  }, buildPhysicalIosDebugApp = async (project, identifier, run, signal) => {
4172
- const derivedDataPath = join12(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash5("sha256").update(project.config.appId).digest("hex").slice(0, 16));
4753
+ const derivedDataPath = join13(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash5("sha256").update(project.config.appId).digest("hex").slice(0, 16));
4173
4754
  await mkdir5(derivedDataPath, { recursive: true });
4174
4755
  await requireSuccess2([
4175
4756
  project.xcodebuild,
4176
4757
  "-workspace",
4177
- join12(project.nativeDirectory, "App", "App.xcworkspace"),
4758
+ join13(project.nativeDirectory, "App", "App.xcworkspace"),
4178
4759
  "-scheme",
4179
4760
  "App",
4180
4761
  "-configuration",
@@ -4186,7 +4767,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4186
4767
  "-allowProvisioningUpdates",
4187
4768
  "build"
4188
4769
  ], "iOS physical-device build (configure automatic signing and a Development Team in Xcode if this is the first run)", run, { cwd: project.nativeDirectory, signal });
4189
- const appPath = join12(derivedDataPath, "Build", "Products", "Debug-iphoneos", "App.app");
4770
+ const appPath = join13(derivedDataPath, "Build", "Products", "Debug-iphoneos", "App.app");
4190
4771
  if (!await pathExists4(appPath))
4191
4772
  throw new Error(`Xcode did not produce the physical-device app at ${appPath}.`);
4192
4773
  return appPath;
@@ -4297,7 +4878,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4297
4878
  if (detectAbsoluteMobileHost() !== "macos")
4298
4879
  throw new Error("iOS development requires macOS and Xcode.");
4299
4880
  const target = options.target ?? "simulator";
4300
- const projectRoot = resolve8(options.projectRoot);
4881
+ const projectRoot = resolve9(options.projectRoot);
4301
4882
  const checks = await inspectAbsoluteMobileToolchain({ host: "macos" });
4302
4883
  const failed = checks.filter((check) => check.platform === "ios" && !(target === "device" && check.id === "ios.runtime") && (check.status === "fail" || check.status === "warn"));
4303
4884
  if (failed.length > 0)
@@ -4306,16 +4887,16 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4306
4887
  const xcodebuild = checks.find((check) => check.id === "ios.xcodebuild")?.path;
4307
4888
  if (!xcrun || !xcodebuild)
4308
4889
  throw new Error("Xcode tools disappeared after readiness checks.");
4309
- const cap = join12(projectRoot, "node_modules", ".bin", "cap");
4890
+ const cap = join13(projectRoot, "node_modules", ".bin", "cap");
4310
4891
  if (!await pathExists4(cap))
4311
4892
  throw new Error("Capacitor CLI is not installed. Run absolute mobile init first.");
4312
4893
  await writeAbsoluteCapacitorConfig(config, { projectRoot });
4313
4894
  await mkdir5(config.bundleDirectory, { recursive: true });
4314
- const placeholder = join12(config.bundleDirectory, "index.html");
4895
+ const placeholder = join13(config.bundleDirectory, "index.html");
4315
4896
  if (!await pathExists4(placeholder))
4316
4897
  await writeFile5(placeholder, `<!doctype html><title>AbsoluteJS mobile development</title>
4317
4898
  `);
4318
- const nativeDirectory = join12(config.nativeProjectDirectory, "ios");
4899
+ const nativeDirectory = join13(config.nativeProjectDirectory, "ios");
4319
4900
  if (!await pathExists4(nativeDirectory)) {
4320
4901
  if (!options.createNativeProject)
4321
4902
  throw new Error("iOS native project has not been created.");
@@ -4540,10 +5121,10 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4540
5121
  screenshot: async (destination) => {
4541
5122
  if (deviceIdentifier)
4542
5123
  throw new Error("Physical iOS screenshots are captured in Xcode Device Hub; the CLI never records a device screen automatically.");
4543
- const resolved = resolve8(project.projectRoot, destination);
5124
+ const resolved = resolve9(project.projectRoot, destination);
4544
5125
  if (!isInside2(project.projectRoot, resolved))
4545
5126
  throw new Error("iOS screenshot destination must remain inside the project.");
4546
- await mkdir5(dirname6(resolved), { recursive: true });
5127
+ await mkdir5(dirname7(resolved), { recursive: true });
4547
5128
  await requireSuccess2([
4548
5129
  project.xcrun,
4549
5130
  "simctl",
@@ -4602,15 +5183,15 @@ import { chmod, mkdir as mkdir6, readFile as readFile8, rename as rename6, write
4602
5183
  import { homedir as homedir4 } from "os";
4603
5184
  import { isIP as isIP3 } from "net";
4604
5185
  import {
4605
- dirname as dirname7,
5186
+ dirname as dirname8,
4606
5187
  isAbsolute as isAbsolute4,
4607
- join as join13,
5188
+ join as join14,
4608
5189
  posix,
4609
5190
  relative as relative6,
4610
5191
  resolve as resolvePath,
4611
5192
  sep as sep4
4612
5193
  } from "path";
4613
- var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join13(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
5194
+ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join14(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
4614
5195
  format: PROFILE_FORMAT,
4615
5196
  profiles: {}
4616
5197
  }), loadStore = async (path = defaultProfilePath()) => {
@@ -4631,7 +5212,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4631
5212
  throw error;
4632
5213
  }
4633
5214
  }, saveStore = async (store, path = defaultProfilePath()) => {
4634
- await mkdir6(dirname7(path), { recursive: true });
5215
+ await mkdir6(dirname8(path), { recursive: true });
4635
5216
  const temporary = `${path}.${randomUUID4()}.tmp`;
4636
5217
  await writeFile6(temporary, `${JSON.stringify(store, null, 2)}
4637
5218
  `, {
@@ -4733,10 +5314,10 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4733
5314
  "/bin/sh -lc",
4734
5315
  shellQuote(script)
4735
5316
  ]);
4736
- const host = requireRemoteSuccess(result, "Remote Mac LAN address discovery").trim();
4737
- if (isIP3(host) === 0)
5317
+ const host2 = requireRemoteSuccess(result, "Remote Mac LAN address discovery").trim();
5318
+ if (isIP3(host2) === 0)
4738
5319
  throw new Error("The Remote Mac did not report a device-reachable LAN address.");
4739
- return host;
5320
+ return host2;
4740
5321
  }, listAbsoluteRemoteMacProfiles = async (profilePath) => {
4741
5322
  const store = await loadStore(profilePath);
4742
5323
  return {
@@ -4786,9 +5367,9 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4786
5367
  throw new TypeError("Remote Expo execution requires mobile.engine: expo.");
4787
5368
  return createAbsoluteRemoteIosDevProject(config, projectRoot, profile);
4788
5369
  }, createAbsoluteRemoteIosDevProject = (config, projectRoot, profile) => ({
4789
- cap: join13(resolvePath(projectRoot), "node_modules", ".bin", "cap"),
5370
+ cap: join14(resolvePath(projectRoot), "node_modules", ".bin", "cap"),
4790
5371
  config,
4791
- nativeDirectory: join13(config.nativeProjectDirectory, "ios"),
5372
+ nativeDirectory: join14(config.nativeProjectDirectory, "ios"),
4792
5373
  profile,
4793
5374
  projectRoot: resolvePath(projectRoot),
4794
5375
  remote: true,
@@ -4835,8 +5416,8 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4835
5416
  return { ...artifact, remotePath, uploaded: true };
4836
5417
  }, materializeAbsoluteRemoteMacAgent = async (projectRoot) => {
4837
5418
  const shippedCandidates = [
4838
- join13(import.meta.dir, "remoteMacAgentEntry.js"),
4839
- join13(import.meta.dir, "..", "mobile", "remoteMacAgentEntry.js")
5419
+ join14(import.meta.dir, "remoteMacAgentEntry.js"),
5420
+ join14(import.meta.dir, "..", "mobile", "remoteMacAgentEntry.js")
4840
5421
  ];
4841
5422
  let path;
4842
5423
  for (const candidate of shippedCandidates) {
@@ -4847,13 +5428,13 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4847
5428
  }
4848
5429
  if (!path) {
4849
5430
  const sourceCandidates = [
4850
- join13(import.meta.dir, "remoteMacAgentEntry.ts"),
4851
- join13(import.meta.dir, "..", "..", "src", "mobile", "remoteMacAgentEntry.ts")
5431
+ join14(import.meta.dir, "remoteMacAgentEntry.ts"),
5432
+ join14(import.meta.dir, "..", "..", "src", "mobile", "remoteMacAgentEntry.ts")
4852
5433
  ];
4853
5434
  const source = await sourceCandidates.reduce(async (found, candidate) => await found ?? (await Bun.file(candidate).exists() ? candidate : undefined), Promise.resolve(undefined));
4854
5435
  if (!source)
4855
5436
  throw new Error("The AbsoluteJS installation does not contain its remote Mac agent artifact.");
4856
- const outdir = join13(resolvePath(projectRoot), ".absolutejs", "mobile", "remote-agent");
5437
+ const outdir = join14(resolvePath(projectRoot), ".absolutejs", "mobile", "remote-agent");
4857
5438
  await mkdir6(outdir, { recursive: true });
4858
5439
  const result = await Bun.build({
4859
5440
  entrypoints: [source],
@@ -4863,7 +5444,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4863
5444
  });
4864
5445
  if (!result.success)
4865
5446
  throw new AggregateError(result.logs, "Failed to build the AbsoluteJS remote Mac agent.");
4866
- path = join13(outdir, "remoteMacAgentEntry.js");
5447
+ path = join14(outdir, "remoteMacAgentEntry.js");
4867
5448
  }
4868
5449
  const bytes = await Bun.file(path).arrayBuffer();
4869
5450
  const sha256 = createHash6("sha256").update(new Uint8Array(bytes)).digest("hex");
@@ -5084,8 +5665,8 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
5084
5665
  const pending = new Map;
5085
5666
  let resolveReady;
5086
5667
  let rejectReady;
5087
- const readyPromise = new Promise((resolve9, reject) => {
5088
- resolveReady = resolve9;
5668
+ const readyPromise = new Promise((resolve10, reject) => {
5669
+ resolveReady = resolve10;
5089
5670
  rejectReady = reject;
5090
5671
  });
5091
5672
  const handleEvent = (event) => {
@@ -5186,7 +5767,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
5186
5767
  options.log?.(`Remote Mac connected (${options.project.profile.name}); agent ${agent.uploaded ? "uploaded" : "cache hit"}, project synced, and ${expo ? "Expo " : ""}iOS ready in ${totalDuration.toFixed(2)}ms.`);
5187
5768
  const request = (commandName) => {
5188
5769
  const id = randomUUID4();
5189
- const response = new Promise((resolve9, reject) => pending.set(id, { reject, resolve: resolve9 }));
5770
+ const response = new Promise((resolve10, reject) => pending.set(id, { reject, resolve: resolve10 }));
5190
5771
  process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION })}
5191
5772
  `);
5192
5773
  const flush = async () => {
@@ -5206,7 +5787,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
5206
5787
  if (closed)
5207
5788
  return;
5208
5789
  closed = true;
5209
- const timeout = () => new Promise((resolve9) => setTimeout(() => resolve9("timeout"), REMOTE_SESSION_CLOSE_TIMEOUT_MS));
5790
+ const timeout = () => new Promise((resolve10) => setTimeout(() => resolve10("timeout"), REMOTE_SESSION_CLOSE_TIMEOUT_MS));
5210
5791
  await Promise.race([
5211
5792
  request("close").catch(() => {
5212
5793
  return;
@@ -5257,7 +5838,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
5257
5838
  const targetRelative = relative6(options.project.projectRoot, target);
5258
5839
  if (targetRelative.startsWith("..") || isAbsolute4(targetRelative))
5259
5840
  throw new Error("iOS screenshot must remain inside the project.");
5260
- await mkdir6(dirname7(target), { recursive: true });
5841
+ await mkdir6(dirname8(target), { recursive: true });
5261
5842
  await writeFile6(target, Buffer.from(result.data, "base64"));
5262
5843
  return target;
5263
5844
  },
@@ -5296,32 +5877,32 @@ import {
5296
5877
  copyFileSync,
5297
5878
  existsSync as existsSync4,
5298
5879
  mkdirSync as mkdirSync4,
5299
- readFileSync as readFileSync7,
5880
+ readFileSync as readFileSync8,
5300
5881
  rmSync
5301
5882
  } from "fs";
5302
5883
  import { X509Certificate as X509Certificate2 } from "crypto";
5303
5884
  import { isIP as isIP4 } from "net";
5304
5885
  import { platform as platform2 } from "os";
5305
- import { join as join14 } from "path";
5886
+ import { join as join15 } from "path";
5306
5887
  var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE_HOSTS, CERTIFICATE_HOSTNAME_PATTERN, devLog = (msg) => console.log(`\x1B[2m${new Date().toLocaleTimeString()}\x1B[0m \x1B[36m[dev]\x1B[0m ${msg}`), devWarn = (msg) => console.log(`\x1B[2m${new Date().toLocaleTimeString()}\x1B[0m \x1B[33m[dev]\x1B[0m \x1B[33m${msg}\x1B[0m`), certFilesExist = () => existsSync4(CERT_PATH) && existsSync4(KEY_PATH), normalizeDevCertificateHosts = (hosts = []) => {
5307
5888
  const normalized = new Set(DEFAULT_CERTIFICATE_HOSTS);
5308
- for (const host of hosts) {
5309
- const value = host.trim().toLowerCase();
5889
+ for (const host2 of hosts) {
5890
+ const value = host2.trim().toLowerCase();
5310
5891
  if (!value || value === "0.0.0.0" || value === "::")
5311
5892
  continue;
5312
5893
  if (isIP4(value) === 0 && !CERTIFICATE_HOSTNAME_PATTERN.test(value)) {
5313
- throw new TypeError(`Invalid development certificate host: ${host}`);
5894
+ throw new TypeError(`Invalid development certificate host: ${host2}`);
5314
5895
  }
5315
5896
  normalized.add(value);
5316
5897
  }
5317
5898
  return [...normalized];
5318
5899
  }, certificateIsUsable = (hosts) => {
5319
5900
  try {
5320
- const certPem = readFileSync7(CERT_PATH, "utf-8");
5901
+ const certPem = readFileSync8(CERT_PATH, "utf-8");
5321
5902
  const certificate = new X509Certificate2(certPem);
5322
5903
  if (new Date(certificate.validTo).getTime() <= Date.now())
5323
5904
  return false;
5324
- return normalizeDevCertificateHosts(hosts).every((host) => isIP4(host) ? certificate.checkIP(host) !== undefined : certificate.checkHost(host) !== undefined);
5905
+ return normalizeDevCertificateHosts(hosts).every((host2) => isIP4(host2) ? certificate.checkIP(host2) !== undefined : certificate.checkHost(host2) !== undefined);
5325
5906
  } catch {
5326
5907
  return false;
5327
5908
  }
@@ -5349,7 +5930,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5349
5930
  throw new Error(`mkcert failed: ${err}`);
5350
5931
  }
5351
5932
  }, generateSelfSigned = (hosts = []) => {
5352
- const subjectAlternativeNames = normalizeDevCertificateHosts(hosts).map((host) => `${isIP4(host) ? "IP" : "DNS"}:${host}`).join(",");
5933
+ const subjectAlternativeNames = normalizeDevCertificateHosts(hosts).map((host2) => `${isIP4(host2) ? "IP" : "DNS"}:${host2}`).join(",");
5353
5934
  const proc = Bun.spawnSync([
5354
5935
  "openssl",
5355
5936
  "req",
@@ -5402,8 +5983,8 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5402
5983
  return null;
5403
5984
  try {
5404
5985
  return {
5405
- cert: readFileSync7(paths.cert, "utf-8"),
5406
- key: readFileSync7(paths.key, "utf-8")
5986
+ cert: readFileSync8(paths.cert, "utf-8"),
5987
+ key: readFileSync8(paths.key, "utf-8")
5407
5988
  };
5408
5989
  } catch {
5409
5990
  return null;
@@ -5499,7 +6080,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5499
6080
  if (platform2() !== "linux")
5500
6081
  return false;
5501
6082
  try {
5502
- return /microsoft|wsl/i.test(readFileSync7("/proc/version", "utf-8"));
6083
+ return /microsoft|wsl/i.test(readFileSync8("/proc/version", "utf-8"));
5503
6084
  } catch {
5504
6085
  return false;
5505
6086
  }
@@ -5515,7 +6096,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5515
6096
  }
5516
6097
  }, mkcertCaRoot = () => runCapture(["mkcert", "-CAROOT"]), getDevCertificateAuthorityPath = () => {
5517
6098
  const caRoot = hasMkcert() ? mkcertCaRoot() : null;
5518
- const rootCertificate = caRoot ? join14(caRoot, "rootCA.pem") : null;
6099
+ const rootCertificate = caRoot ? join15(caRoot, "rootCA.pem") : null;
5519
6100
  if (rootCertificate && existsSync4(rootCertificate))
5520
6101
  return rootCertificate;
5521
6102
  if (certFilesExist())
@@ -5530,13 +6111,13 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5530
6111
  const caRoot = mkcertCaRoot();
5531
6112
  if (!caRoot)
5532
6113
  return false;
5533
- const rootCa = join14(caRoot, "rootCA.pem");
6114
+ const rootCa = join15(caRoot, "rootCA.pem");
5534
6115
  if (!existsSync4(rootCa))
5535
6116
  return false;
5536
6117
  const winTemp = windowsTempDir();
5537
6118
  if (!winTemp)
5538
6119
  return false;
5539
- const staged = join14(winTemp, "absolutejs-mkcert-rootCA.crt");
6120
+ const staged = join15(winTemp, "absolutejs-mkcert-rootCA.crt");
5540
6121
  try {
5541
6122
  copyFileSync(rootCa, staged);
5542
6123
  } catch {
@@ -5572,7 +6153,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5572
6153
  devLog("Trusted the local CA in the Windows store \u2014 Chrome/Edge on Windows now accept dev HTTPS");
5573
6154
  } else {
5574
6155
  const caRoot = mkcertCaRoot();
5575
- const hint = caRoot ? toWindowsPath(join14(caRoot, "rootCA.pem")) : null;
6156
+ const hint = caRoot ? toWindowsPath(join15(caRoot, "rootCA.pem")) : null;
5576
6157
  devWarn("Could not auto-trust the local CA on Windows; Windows browsers may warn.");
5577
6158
  if (hint) {
5578
6159
  console.log(` Run in PowerShell: Import-Certificate -FilePath "${hint}" -CertStoreLocation Cert:\\CurrentUser\\Root`);
@@ -5588,9 +6169,9 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5588
6169
  return true;
5589
6170
  };
5590
6171
  var init_devCert = __esm(() => {
5591
- CERT_DIR = join14(process.cwd(), ".absolutejs");
5592
- CERT_PATH = join14(CERT_DIR, "cert.pem");
5593
- KEY_PATH = join14(CERT_DIR, "key.pem");
6172
+ CERT_DIR = join15(process.cwd(), ".absolutejs");
6173
+ CERT_PATH = join15(CERT_DIR, "cert.pem");
6174
+ KEY_PATH = join15(CERT_DIR, "key.pem");
5594
6175
  DEFAULT_CERTIFICATE_HOSTS = ["localhost", "127.0.0.1", "::1"];
5595
6176
  CERTIFICATE_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])?))*$/u;
5596
6177
  });
@@ -5607,7 +6188,7 @@ __export(exports_eslintChunked, {
5607
6188
  upstreamRef: () => upstreamRef
5608
6189
  });
5609
6190
  import { existsSync as existsSync6 } from "fs";
5610
- import { relative as relative7, resolve as resolve9 } from "path";
6191
+ import { relative as relative7, resolve as resolve10 } from "path";
5611
6192
  var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAULT_REPORT = ".absolutejs/lint-report.txt", CHILD_HEAP_MB = 4096, DJB2_SEED = 5381, DJB2_MULTIPLIER = 33, MS_PER_SECOND = 1000, SUMMARY_RULE_WIDTH = 60, SUMMARY_COUNT_PAD = 5, PORCELAIN_STATUS_WIDTH = 3, RENAME_ARROW = " -> ", ASCII_ESC = 27, LINTABLE_EXTENSIONS, ANSI_COLOR, stripAnsi = (text) => text.replace(ANSI_COLOR, ""), shardOf = (path, shards) => [...path].reduce((accumulator, character) => (Math.imul(accumulator, DJB2_MULTIPLIER) ^ character.charCodeAt(0)) >>> 0, DJB2_SEED) % shards, gitLines = (cmd, cwd) => Bun.spawnSync(cmd, { cwd }).stdout.toString().split(`
5612
6193
  `).map((line) => line.trimEnd()).filter(Boolean), applyChangedBase = (parsed, base) => {
5613
6194
  parsed.changedOnly = true;
@@ -5619,7 +6200,7 @@ var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAUL
5619
6200
  }, matchesAnyGlob = (file, globs) => globs.some((pattern) => new Bun.Glob(pattern).match(file)), resolveLintSet = (parsed, cwd) => {
5620
6201
  const visible = gitVisibleFiles(cwd);
5621
6202
  const matched = parsed.globs.length === 0 ? visible.filter((file) => LINTABLE_EXTENSIONS.test(file)) : visible.filter((file) => matchesAnyGlob(file, parsed.globs));
5622
- return matched.filter((file) => existsSync6(resolve9(cwd, file))).sort();
6203
+ return matched.filter((file) => existsSync6(resolve10(cwd, file))).sort();
5623
6204
  }, resolveLintTargets = (args, cwd = process.cwd()) => resolveLintSet(parseChunkedArgs(args), cwd), buildShardChunks = (files, shards, chunkSize) => {
5624
6205
  const shardFiles = Array.from({ length: shards }, () => []);
5625
6206
  for (const file of files)
@@ -5641,7 +6222,7 @@ var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAUL
5641
6222
  "content"
5642
6223
  ];
5643
6224
  const proc = Bun.spawn([
5644
- resolve9(cwd, "node_modules/.bin/eslint"),
6225
+ resolve10(cwd, "node_modules/.bin/eslint"),
5645
6226
  "--color",
5646
6227
  ...hasMaxWarnings ? [] : ["--max-warnings", "0"],
5647
6228
  ...cacheArgs,
@@ -5688,7 +6269,7 @@ var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAUL
5688
6269
  const fingerprint = createEslintCacheFingerprint(cwd);
5689
6270
  for (let shard = 0;shard < parsed.shards; shard++)
5690
6271
  prepareEslintCache({
5691
- cacheLocation: relative7(cwd, resolve9(cwd, `${cachePrefix}${shard}`)),
6272
+ cacheLocation: relative7(cwd, resolve10(cwd, `${cachePrefix}${shard}`)),
5692
6273
  cwd,
5693
6274
  fingerprint
5694
6275
  });
@@ -5729,7 +6310,7 @@ var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAUL
5729
6310
  const header = `eslint report \u2014 ${files.length} files, ${totalChunks} chunks, ${elapsed}s
5730
6311
  ${"=".repeat(SUMMARY_RULE_WIDTH)}
5731
6312
  `;
5732
- await Bun.write(resolve9(cwd, parsed.outFile), header + report + summary);
6313
+ await Bun.write(resolve10(cwd, parsed.outFile), header + report + summary);
5733
6314
  console.log(summary);
5734
6315
  console.log(`Full report written to ${parsed.outFile}`);
5735
6316
  if (failedChunks > 0) {
@@ -5818,12 +6399,12 @@ import { createHash as createHash7 } from "crypto";
5818
6399
  import {
5819
6400
  existsSync as existsSync7,
5820
6401
  mkdirSync as mkdirSync5,
5821
- readFileSync as readFileSync9,
6402
+ readFileSync as readFileSync10,
5822
6403
  renameSync,
5823
6404
  rmSync as rmSync3,
5824
6405
  writeFileSync as writeFileSync5
5825
6406
  } from "fs";
5826
- import { dirname as dirname8, relative as relative8, resolve as resolve10 } from "path";
6407
+ import { dirname as dirname9, relative as relative8, resolve as resolve11 } from "path";
5827
6408
  var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION = "1", CACHE_FINGERPRINT_SUFFIX = ".fingerprint", flagValue = (args, flag) => {
5828
6409
  const assignment = args.find((arg) => arg.startsWith(`${flag}=`));
5829
6410
  if (assignment)
@@ -5847,20 +6428,20 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5847
6428
  return false;
5848
6429
  }, findEslintConfigPath = (cwd = process.cwd()) => {
5849
6430
  for (const name of CONFIG_CANDIDATES) {
5850
- const candidate = resolve10(cwd, name);
6431
+ const candidate = resolve11(cwd, name);
5851
6432
  if (existsSync7(candidate))
5852
6433
  return candidate;
5853
6434
  }
5854
6435
  return null;
5855
6436
  }, fingerprintLocation = (cacheLocation, cwd) => {
5856
- const absolute = resolve10(cwd, cacheLocation);
5857
- return /[\\/]$/.test(cacheLocation) ? resolve10(absolute, CACHE_FINGERPRINT_SUFFIX.slice(1)) : `${absolute}${CACHE_FINGERPRINT_SUFFIX}`;
6437
+ const absolute = resolve11(cwd, cacheLocation);
6438
+ return /[\\/]$/.test(cacheLocation) ? resolve11(absolute, CACHE_FINGERPRINT_SUFFIX.slice(1)) : `${absolute}${CACHE_FINGERPRINT_SUFFIX}`;
5858
6439
  }, addFileToFingerprint = (hash, path, label) => {
5859
6440
  if (!existsSync7(path))
5860
6441
  return;
5861
6442
  hash.update(label);
5862
6443
  hash.update("\x00");
5863
- hash.update(readFileSync9(path));
6444
+ hash.update(readFileSync10(path));
5864
6445
  hash.update("\x00");
5865
6446
  }, packageNameFor = (specifier) => {
5866
6447
  if (specifier.startsWith("@"))
@@ -5870,7 +6451,7 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5870
6451
  }, configPackageNames = (configPath2) => {
5871
6452
  if (!configPath2)
5872
6453
  return [];
5873
- const source = readFileSync9(configPath2, "utf-8");
6454
+ const source = readFileSync10(configPath2, "utf-8");
5874
6455
  const names = new Set;
5875
6456
  for (const match of source.matchAll(/(?:from\s+|import\s*(?:\(\s*)?|require\s*\(\s*)(['"])([^'".][^'"]*)\1/g)) {
5876
6457
  const [, , specifier] = match;
@@ -5889,11 +6470,11 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5889
6470
  return Object.keys(value);
5890
6471
  });
5891
6472
  }, lintDependencyNames = (cwd, configPath2) => {
5892
- const manifestPath = resolve10(cwd, "package.json");
6473
+ const manifestPath = resolve11(cwd, "package.json");
5893
6474
  if (!existsSync7(manifestPath))
5894
6475
  return configPackageNames(configPath2);
5895
6476
  try {
5896
- const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
6477
+ const manifest = JSON.parse(readFileSync10(manifestPath, "utf-8"));
5897
6478
  const lintPackages = manifestDependencyNames(manifest).filter((name) => /eslint|typescript/.test(name));
5898
6479
  return [
5899
6480
  ...new Set([...lintPackages, ...configPackageNames(configPath2)])
@@ -5904,10 +6485,10 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5904
6485
  }, findInstalledManifest = (cwd, dependency) => {
5905
6486
  let directory = cwd;
5906
6487
  while (true) {
5907
- const candidate = resolve10(directory, "node_modules", dependency, "package.json");
6488
+ const candidate = resolve11(directory, "node_modules", dependency, "package.json");
5908
6489
  if (existsSync7(candidate))
5909
6490
  return candidate;
5910
- const parent = dirname8(directory);
6491
+ const parent = dirname9(directory);
5911
6492
  if (parent === directory)
5912
6493
  return null;
5913
6494
  directory = parent;
@@ -5930,17 +6511,17 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5930
6511
  addFileToFingerprint(hash, configPath2, relative8(cwd, configPath2));
5931
6512
  return hash.digest("hex");
5932
6513
  }, writeFingerprint = (path, fingerprint) => {
5933
- mkdirSync5(dirname8(path), { recursive: true });
6514
+ mkdirSync5(dirname9(path), { recursive: true });
5934
6515
  const temporary = `${path}.${process.pid}.tmp`;
5935
6516
  writeFileSync5(temporary, `${fingerprint}
5936
6517
  `);
5937
6518
  renameSync(temporary, path);
5938
6519
  }, prepareEslintCache = (options) => {
5939
6520
  const cwd = options.cwd ?? process.cwd();
5940
- const cachePath = resolve10(cwd, options.cacheLocation);
6521
+ const cachePath = resolve11(cwd, options.cacheLocation);
5941
6522
  const metadataPath = fingerprintLocation(options.cacheLocation, cwd);
5942
6523
  const fingerprint = options.fingerprint ?? createEslintCacheFingerprint(cwd);
5943
- const prior = existsSync7(metadataPath) ? readFileSync9(metadataPath, "utf-8").trim() : null;
6524
+ const prior = existsSync7(metadataPath) ? readFileSync10(metadataPath, "utf-8").trim() : null;
5944
6525
  if (prior === fingerprint)
5945
6526
  return false;
5946
6527
  rmSync3(cachePath, { force: true, recursive: true });
@@ -6028,7 +6609,7 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
6028
6609
  return;
6029
6610
  let source;
6030
6611
  try {
6031
- source = readFileSync9(configPath2, "utf-8");
6612
+ source = readFileSync10(configPath2, "utf-8");
6032
6613
  } catch {
6033
6614
  return;
6034
6615
  }
@@ -6067,7 +6648,7 @@ Detected at: ${configPath2}${reset}`);
6067
6648
  return `${minutes}m ${seconds}s`;
6068
6649
  }, handleClearCache = (cacheLocation, cwd = process.cwd()) => {
6069
6650
  try {
6070
- const cachePath = resolve10(cwd, cacheLocation);
6651
+ const cachePath = resolve11(cwd, cacheLocation);
6071
6652
  const metadataPath = fingerprintLocation(cacheLocation, cwd);
6072
6653
  rmSync3(cachePath, { force: true, recursive: true });
6073
6654
  rmSync3(metadataPath, { force: true, recursive: true });
@@ -6096,7 +6677,7 @@ Detected at: ${configPath2}${reset}`);
6096
6677
  return;
6097
6678
  }
6098
6679
  if (args.includes("--chunked")) {
6099
- if (!existsSync7(resolve10("node_modules", ".bin", "eslint"))) {
6680
+ if (!existsSync7(resolve11("node_modules", ".bin", "eslint"))) {
6100
6681
  console.error("\x1B[31m\u2717\x1B[0m ESLint is not installed in this project. Add it (and a flat `eslint.config.*`): bun add -d eslint");
6101
6682
  process.exit(1);
6102
6683
  }
@@ -6104,7 +6685,7 @@ Detected at: ${configPath2}${reset}`);
6104
6685
  await eslintChunked2(args);
6105
6686
  return;
6106
6687
  }
6107
- if (!existsSync7(resolve10("node_modules", ".bin", "eslint"))) {
6688
+ if (!existsSync7(resolve11("node_modules", ".bin", "eslint"))) {
6108
6689
  console.error("\x1B[31m\u2717\x1B[0m ESLint is not installed in this project. Add it (and a flat `eslint.config.*`): bun add -d eslint");
6109
6690
  process.exit(1);
6110
6691
  }
@@ -6281,21 +6862,21 @@ var stripStringsAndComments = (source) => {
6281
6862
  };
6282
6863
 
6283
6864
  // src/core/islandManifest.ts
6284
- var toIslandFrameworkSegment = (framework) => framework[0]?.toUpperCase() + framework.slice(1), getIslandManifestKey = (framework, component) => `Island${toIslandFrameworkSegment(framework)}${component}`;
6865
+ var toIslandFrameworkSegment = (framework) => framework[0]?.toUpperCase() + framework.slice(1), getIslandManifestKey = (framework, component2) => `Island${toIslandFrameworkSegment(framework)}${component2}`;
6285
6866
 
6286
6867
  // src/core/islands.ts
6287
- var isRecord4 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component) => {
6288
- if (!isIslandComponentDefinition(component))
6868
+ var isRecord4 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component2) => {
6869
+ if (!isIslandComponentDefinition(component2))
6289
6870
  return null;
6290
6871
  return {
6291
- export: component.export,
6292
- source: component.source
6872
+ export: component2.export,
6873
+ source: component2.source
6293
6874
  };
6294
6875
  }, isIslandComponentDefinition = (value) => isRecord4(value) && ("component" in value) && ("source" in value) && typeof value.source === "string";
6295
6876
  var init_islands = () => {};
6296
6877
 
6297
6878
  // src/build/islandEntries.ts
6298
- import { dirname as dirname9, extname, join as join17, relative as relative9, resolve as resolve12 } from "path";
6879
+ import { dirname as dirname10, extname, join as join18, relative as relative9, resolve as resolve13 } from "path";
6299
6880
  import ts from "typescript";
6300
6881
  var frameworks, isRecord5 = (value) => typeof value === "object" && value !== null, resolveRegistryExport = (mod) => {
6301
6882
  if (isRecord5(mod.islandRegistry))
@@ -6307,7 +6888,7 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6307
6888
  if (sourcePath.startsWith("file://")) {
6308
6889
  return new URL(sourcePath).pathname;
6309
6890
  }
6310
- return resolve12(dirname9(registryPath), sourcePath);
6891
+ return resolve13(dirname10(registryPath), sourcePath);
6311
6892
  }, getObjectPropertyName = (name) => {
6312
6893
  if (ts.isIdentifier(name) || ts.isStringLiteral(name)) {
6313
6894
  return name.text;
@@ -6350,9 +6931,9 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6350
6931
  component: reference.source,
6351
6932
  export: reference.export,
6352
6933
  source: reference.source
6353
- }), addRegistryEntries = (frameworkNode, framework, imports, definitions, registry) => {
6354
- const frameworkRegistry = registry[framework] ?? {};
6355
- registry[framework] = frameworkRegistry;
6934
+ }), addRegistryEntries = (frameworkNode, framework, imports, definitions, registry2) => {
6935
+ const frameworkRegistry = registry2[framework] ?? {};
6936
+ registry2[framework] = frameworkRegistry;
6356
6937
  for (const property of frameworkNode.properties) {
6357
6938
  if (!ts.isPropertyAssignment(property) && !ts.isShorthandPropertyAssignment(property))
6358
6939
  continue;
@@ -6372,7 +6953,7 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6372
6953
  framework
6373
6954
  });
6374
6955
  }
6375
- }, processDefineIslandRegistry = (node, imports, definitions, registry) => {
6956
+ }, processDefineIslandRegistry = (node, imports, definitions, registry2) => {
6376
6957
  const [firstArg] = node.arguments;
6377
6958
  if (!firstArg || !ts.isObjectLiteralExpression(firstArg))
6378
6959
  return;
@@ -6393,13 +6974,13 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6393
6974
  continue;
6394
6975
  if (!ts.isObjectLiteralExpression(property.initializer))
6395
6976
  continue;
6396
- addRegistryEntries(property.initializer, framework, imports, definitions, registry);
6977
+ addRegistryEntries(property.initializer, framework, imports, definitions, registry2);
6397
6978
  }
6398
- }, walkRegistryNode = (node, imports, registryFactoryNames, registryNamespaceNames, definitions, registry) => {
6979
+ }, walkRegistryNode = (node, imports, registryFactoryNames, registryNamespaceNames, definitions, registry2) => {
6399
6980
  if (ts.isCallExpression(node) && isDefineIslandRegistryCall(node.expression, registryFactoryNames, registryNamespaceNames)) {
6400
- processDefineIslandRegistry(node, imports, definitions, registry);
6981
+ processDefineIslandRegistry(node, imports, definitions, registry2);
6401
6982
  }
6402
- ts.forEachChild(node, (child) => walkRegistryNode(child, imports, registryFactoryNames, registryNamespaceNames, definitions, registry));
6983
+ ts.forEachChild(node, (child) => walkRegistryNode(child, imports, registryFactoryNames, registryNamespaceNames, definitions, registry2));
6403
6984
  }, isDefineIslandRegistryCall = (expression, registryFactoryNames, registryNamespaceNames) => {
6404
6985
  if (ts.isIdentifier(expression)) {
6405
6986
  return registryFactoryNames.has(expression.text);
@@ -6437,34 +7018,34 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6437
7018
  const registryFactoryNames = new Set(["defineIslandRegistry"]);
6438
7019
  const registryNamespaceNames = new Set;
6439
7020
  const definitions = [];
6440
- const registry = {};
7021
+ const registry2 = {};
6441
7022
  collectImportDeclarations(sourceFile, registryPath, imports, registryFactoryNames, registryNamespaceNames);
6442
- walkRegistryNode(sourceFile, imports, registryFactoryNames, registryNamespaceNames, definitions, registry);
7023
+ walkRegistryNode(sourceFile, imports, registryFactoryNames, registryNamespaceNames, definitions, registry2);
6443
7024
  return {
6444
7025
  definitions,
6445
7026
  hasNamedExport: hasIslandRegistryNamedExport(sourceFile),
6446
- registry
7027
+ registry: registry2
6447
7028
  };
6448
7029
  }, loadDynamicIslandRegistryBuildInfo = async (resolvedRegistryPath) => {
6449
7030
  const registryModule = await import(resolvedRegistryPath);
6450
- const registry = resolveRegistryExport(registryModule);
7031
+ const registry2 = resolveRegistryExport(registryModule);
6451
7032
  const definitions = frameworks.flatMap((framework) => {
6452
- const frameworkRegistry = registry[framework];
7033
+ const frameworkRegistry = registry2[framework];
6453
7034
  if (!isRecord5(frameworkRegistry))
6454
7035
  return [];
6455
- return Object.entries(frameworkRegistry).map(([component, value]) => ({
7036
+ return Object.entries(frameworkRegistry).map(([component2, value]) => ({
6456
7037
  buildReference: getIslandBuildReference(value),
6457
- component,
7038
+ component: component2,
6458
7039
  framework
6459
7040
  }));
6460
7041
  });
6461
7042
  return {
6462
7043
  definitions,
6463
7044
  hasNamedExport: isRecord5(registryModule.islandRegistry),
6464
- registry
7045
+ registry: registry2
6465
7046
  };
6466
7047
  }, loadIslandRegistryBuildInfo = async (registryPath) => {
6467
- const resolvedRegistryPath = resolve12(registryPath);
7048
+ const resolvedRegistryPath = resolve13(registryPath);
6468
7049
  const registrySource = Bun.file(resolvedRegistryPath);
6469
7050
  const registrySourceText = await registrySource.text();
6470
7051
  const parsedInfo = parseIslandRegistryBuildInfo(registrySourceText, resolvedRegistryPath);
@@ -6998,7 +7579,7 @@ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-
6998
7579
  // src/mobile/buildRelease.ts
6999
7580
  import { createHash as createHash9 } from "crypto";
7000
7581
  import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
7001
- import { basename as basename7, dirname as dirname10, extname as extname3, join as join18, relative as relative10, resolve as resolve13 } from "path";
7582
+ import { basename as basename7, dirname as dirname11, extname as extname3, join as join19, relative as relative10, resolve as resolve14 } from "path";
7002
7583
  var sha256 = (bytes) => createHash9("sha256").update(bytes).digest("hex"), STATIC_SCRIPT_PATTERN, rewriteStaticScriptPaths = (source, manifest) => source.replace(STATIC_SCRIPT_PATTERN, (match, prefix, path, suffix) => {
7003
7584
  if (path.endsWith("/htmx.min.js"))
7004
7585
  return match;
@@ -7006,12 +7587,12 @@ var sha256 = (bytes) => createHash9("sha256").update(bytes).digest("hex"), STATI
7006
7587
  const builtPath = manifest[key];
7007
7588
  return builtPath ? `${prefix}${builtPath}${suffix}` : match;
7008
7589
  }), readPageMetadata = (route) => parseAbsoluteMobileBuildPageMetadata(route.hooks?.detail?.[ABSOLUTE_MOBILE_ROUTE_DETAIL]), resolveAssetPath = (buildDirectory, assetPath) => {
7009
- const resolvedBuildDirectory = resolve13(buildDirectory);
7010
- const resolvedAsset = resolve13(assetPath);
7590
+ const resolvedBuildDirectory = resolve14(buildDirectory);
7591
+ const resolvedAsset = resolve14(assetPath);
7011
7592
  if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
7012
7593
  return resolvedAsset;
7013
7594
  }
7014
- return join18(buildDirectory, assetPath.replace(/^\/+/, ""));
7595
+ return join19(buildDirectory, assetPath.replace(/^\/+/, ""));
7015
7596
  }, pageFor = async (metadata, manifest, buildDirectory) => {
7016
7597
  const assetPath = manifest[metadata.bundleKey];
7017
7598
  if (!assetPath) {
@@ -7022,8 +7603,8 @@ var sha256 = (bytes) => createHash9("sha256").update(bytes).digest("hex"), STATI
7022
7603
  const source = await readFile9(resolvedAssetPath, "utf8");
7023
7604
  const rewritten = rewriteStaticScriptPaths(source, manifest);
7024
7605
  const documentHash = sha256(new TextEncoder().encode(rewritten));
7025
- resolvedAssetPath = join18(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
7026
- await mkdir8(dirname10(resolvedAssetPath), { recursive: true });
7606
+ resolvedAssetPath = join19(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
7607
+ await mkdir8(dirname11(resolvedAssetPath), { recursive: true });
7027
7608
  await writeFile7(resolvedAssetPath, rewritten);
7028
7609
  }
7029
7610
  const pageAssetKey = metadata.bundleKey.replace(/Index$/u, "");
@@ -7036,8 +7617,8 @@ var sha256 = (bytes) => createHash9("sha256").update(bytes).digest("hex"), STATI
7036
7617
  readFile9(resolvedAssetPath),
7037
7618
  resolvedStylePath ? readFile9(resolvedStylePath) : undefined
7038
7619
  ]);
7039
- const bundlePath = `/${relative10(resolve13(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
7040
- const styleBundlePath = resolvedStylePath ? `/${relative10(resolve13(buildDirectory), resolvedStylePath).replaceAll("\\", "/")}` : undefined;
7620
+ const bundlePath = `/${relative10(resolve14(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
7621
+ const styleBundlePath = resolvedStylePath ? `/${relative10(resolve14(buildDirectory), resolvedStylePath).replaceAll("\\", "/")}` : undefined;
7041
7622
  return {
7042
7623
  bundleHash: sha256(bytes),
7043
7624
  bundlePath,
@@ -7200,7 +7781,7 @@ var init_pageProtocol = __esm(() => {
7200
7781
 
7201
7782
  // src/mobile/client.ts
7202
7783
  var frameworks5, upgradeReasons;
7203
- var init_client = __esm(() => {
7784
+ var init_client2 = __esm(() => {
7204
7785
  init_pageProtocol();
7205
7786
  frameworks5 = new Set([
7206
7787
  "angular",
@@ -7222,7 +7803,7 @@ var init_client = __esm(() => {
7222
7803
  // src/mobile/transport.ts
7223
7804
  var ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT = 1;
7224
7805
  var init_transport = __esm(() => {
7225
- init_client();
7806
+ init_client2();
7226
7807
  init_pageProtocol();
7227
7808
  init_routeMatcher();
7228
7809
  });
@@ -7239,34 +7820,39 @@ import {
7239
7820
  writeFile as writeFile8
7240
7821
  } from "fs/promises";
7241
7822
  import { existsSync as existsSync9 } from "fs";
7242
- import { basename as basename8, dirname as dirname11, extname as extname4, join as join19, relative as relative11, resolve as resolve14 } from "path";
7823
+ import { basename as basename8, dirname as dirname12, extname as extname4, join as join20, relative as relative11, resolve as resolve15 } from "path";
7243
7824
  var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-mobile-bootstrap.js", INDEX_FILE = "index.html", CLIENT_CSS_DEPENDENCY_PATTERN, CLIENT_MARKUP_DEPENDENCY_PATTERN, CAPACITOR_CLIENT_FRAMEWORKS, CLIENT_ASSET_DIRECTORIES, errorHasCode = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code, shellBootstrapModule = () => {
7244
- const candidate = ["js", "ts"].map((extension) => join19(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync9);
7825
+ const candidate = ["js", "ts"].map((extension) => join20(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync9);
7245
7826
  if (candidate)
7246
7827
  return candidate;
7247
7828
  throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
7248
7829
  }, shellAuthModule = () => {
7249
- const candidate = ["js", "ts"].map((extension) => join19(import.meta.dir, `shellAuth.${extension}`)).find(existsSync9);
7830
+ const candidate = ["js", "ts"].map((extension) => join20(import.meta.dir, `shellAuth.${extension}`)).find(existsSync9);
7250
7831
  if (candidate)
7251
7832
  return candidate;
7252
7833
  throw new TypeError("AbsoluteJS mobile auth shell module is missing.");
7253
7834
  }, shellExpoAuthModule = () => {
7254
- const candidate = ["js", "ts"].map((extension) => join19(import.meta.dir, `shellExpoAuth.${extension}`)).find(existsSync9);
7835
+ const candidate = ["js", "ts"].map((extension) => join20(import.meta.dir, `shellExpoAuth.${extension}`)).find(existsSync9);
7255
7836
  if (candidate)
7256
7837
  return candidate;
7257
7838
  throw new TypeError("AbsoluteJS Expo auth bridge module is missing.");
7258
7839
  }, shellSyncModule = () => {
7259
- const candidate = ["js", "ts"].map((extension) => join19(import.meta.dir, `shellSync.${extension}`)).find(existsSync9);
7840
+ const candidate = ["js", "ts"].map((extension) => join20(import.meta.dir, `shellSync.${extension}`)).find(existsSync9);
7260
7841
  if (candidate)
7261
7842
  return candidate;
7262
7843
  throw new TypeError("AbsoluteJS mobile Sync shell module is missing.");
7844
+ }, shellExpoSyncModule = () => {
7845
+ const candidate = ["js", "ts"].map((extension) => join20(import.meta.dir, `shellExpoSync.${extension}`)).find(existsSync9);
7846
+ if (candidate)
7847
+ return candidate;
7848
+ throw new TypeError("AbsoluteJS Expo Sync bridge module is missing.");
7263
7849
  }, shellPushModule = () => {
7264
- const candidate = ["js", "ts"].map((extension) => join19(import.meta.dir, `shellPush.${extension}`)).find(existsSync9);
7850
+ const candidate = ["js", "ts"].map((extension) => join20(import.meta.dir, `shellPush.${extension}`)).find(existsSync9);
7265
7851
  if (candidate)
7266
7852
  return candidate;
7267
7853
  throw new TypeError("AbsoluteJS mobile push shell module is missing.");
7268
7854
  }, shellExpoDevicesModule = () => {
7269
- const candidate = ["js", "ts"].map((extension) => join19(import.meta.dir, `shellExpoDevices.${extension}`)).find(existsSync9);
7855
+ const candidate = ["js", "ts"].map((extension) => join20(import.meta.dir, `shellExpoDevices.${extension}`)).find(existsSync9);
7270
7856
  if (candidate)
7271
7857
  return candidate;
7272
7858
  throw new TypeError("AbsoluteJS Expo device bridge module is missing.");
@@ -7297,8 +7883,8 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
7297
7883
  </body>
7298
7884
  </html>
7299
7885
  `, sourceAssetPath = (buildDirectory, bundlePath) => {
7300
- const root = resolve14(buildDirectory);
7301
- const asset = resolve14(root, bundlePath.replace(/^\/+/, ""));
7886
+ const root = resolve15(buildDirectory);
7887
+ const asset = resolve15(root, bundlePath.replace(/^\/+/, ""));
7302
7888
  if (!asset.startsWith(`${root}/`)) {
7303
7889
  throw new TypeError("Mobile page bundle escaped the build directory.");
7304
7890
  }
@@ -7313,15 +7899,15 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
7313
7899
  const segments = specifier.split("/");
7314
7900
  const packageName = specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? "";
7315
7901
  const subpath = specifier.slice(packageName.length);
7316
- const packageDirectory = join19(resolve14(projectRoot), "node_modules", packageName);
7317
- const manifest = JSON.parse(await readFile10(join19(packageDirectory, "package.json"), "utf8"));
7902
+ const packageDirectory = join20(resolve15(projectRoot), "node_modules", packageName);
7903
+ const manifest = JSON.parse(await readFile10(join20(packageDirectory, "package.json"), "utf8"));
7318
7904
  const exports = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "exports") : undefined;
7319
7905
  const entry = typeof exports === "object" && exports !== null ? Reflect.get(exports, subpath ? `.${subpath}` : ".") : undefined;
7320
7906
  const target = importEntryTarget(entry);
7321
7907
  if (typeof target !== "string" || !target.startsWith("./"))
7322
7908
  throw new TypeError(`${specifier} does not publish an import entry.`);
7323
- const resolved = resolve14(packageDirectory, target);
7324
- if (!resolved.startsWith(`${resolve14(packageDirectory)}/`))
7909
+ const resolved = resolve15(packageDirectory, target);
7910
+ if (!resolved.startsWith(`${resolve15(packageDirectory)}/`))
7325
7911
  throw new TypeError(`${specifier} has an unsafe import entry.`);
7326
7912
  return resolved;
7327
7913
  }, buildShellBootstrap = async (staging, auth, sync, storagePrefix, engine, deviceCapabilities, projectRoot) => {
@@ -7329,10 +7915,11 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
7329
7915
  const shellCapabilities = capacitor ? deviceCapabilities.capabilities : [];
7330
7916
  const modulePath = shellBootstrapModule();
7331
7917
  const authFactory = capacitor ? "createAbsoluteMobileShellAuth" : "createAbsoluteExpoShellAuth";
7918
+ const syncFactory = capacitor ? "installAbsoluteMobileShellSync" : "installAbsoluteExpoShellSync";
7332
7919
  const authImport = auth ? `import { ${authFactory} } from ${JSON.stringify(capacitor ? shellAuthModule() : shellExpoAuthModule())};
7333
7920
  ` : "";
7334
- const options = auth ? `{ createAuth: ${authFactory}${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }` : "";
7335
- const syncImport = sync ? `import { installAbsoluteMobileShellSync } from ${JSON.stringify(shellSyncModule())};
7921
+ const options = auth ? `{ createAuth: ${authFactory}${sync ? `, installSync: ${syncFactory}` : ""} }` : "";
7922
+ const syncImport = sync ? `import { ${syncFactory} } from ${JSON.stringify(capacitor ? shellSyncModule() : shellExpoSyncModule())};
7336
7923
  ` : "";
7337
7924
  const pushIndex = shellCapabilities.indexOf("pushNotifications");
7338
7925
  const push = pushIndex !== -1;
@@ -7341,778 +7928,356 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
7341
7928
  const capabilityImports = (await Promise.all(shellCapabilities.map(async (name, index) => {
7342
7929
  const provider = deviceCapabilities.providers[name];
7343
7930
  if (!provider)
7344
- throw new TypeError(`Missing device capability provider ${name}.`);
7345
- return `import { ${provider.factory} as absoluteDeviceCapability${index} } from ${JSON.stringify(await resolveProjectImport(projectRoot, provider.module))};`;
7346
- }))).join(`
7347
- `);
7348
- const pushSetup = push ? `const absoluteMobilePush = createAbsoluteMobileShellPush();
7349
- const absoluteMobilePushCapability = absoluteDeviceCapability${pushIndex}(absoluteMobilePush.capabilityOptions);
7350
- ` : "";
7351
- const capabilityOptions = shellCapabilities.map((name, index) => `${JSON.stringify(name)}: ${name === "pushNotifications" ? "absoluteMobilePushCapability" : `absoluteDeviceCapability${index}()`}`).join(", ");
7352
- const entryPath = join19(staging, ".absolute-mobile-entry.ts");
7353
- const baseAdapterModule = capacitor ? await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor") : shellExpoDevicesModule();
7354
- const adapterImport = capacitor ? `import { installCapacitorDeviceAdapterIfNative } from ${JSON.stringify(baseAdapterModule)};` : `import { createAbsoluteExpoBridgeFetch, installAbsoluteExpoWebDeviceAdapter } from ${JSON.stringify(baseAdapterModule)};`;
7355
- const adapterInstall = capacitor ? `installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });` : "installAbsoluteExpoWebDeviceAdapter();";
7356
- let shellOptions = auth ? options : "{ createFetch: createAbsoluteExpoBridgeFetch }";
7357
- if (capacitor)
7358
- shellOptions = options;
7359
- if (push) {
7360
- shellOptions = `{ createAuth: (config, options) => createAbsoluteMobileShellAuth(config, options), beforeSignOut: absoluteMobilePush.beforeSignOut, connectPush: (auth) => absoluteMobilePush.connect(auth, absoluteMobilePushCapability)${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }`;
7361
- }
7362
- await writeFile8(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
7363
- ${adapterImport}
7364
- ${authImport}${syncImport}${pushImport}${capabilityImports}
7365
- ${pushSetup}${adapterInstall}
7366
- void startAbsoluteMobileShell(${shellOptions});
7367
- `);
7368
- const build = await Bun.build({
7369
- entrypoints: [entryPath],
7370
- minify: true,
7371
- outdir: staging,
7372
- target: "browser"
7373
- });
7374
- if (!build.success || build.outputs.length !== 1) {
7375
- throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
7376
- }
7377
- await rename7(build.outputs[0]?.path ?? "", join19(staging, BOOTSTRAP_FILE));
7378
- await rm6(entryPath, { force: true });
7379
- }, removePreviousBundle = async (backup, moved) => {
7380
- if (!moved)
7381
- return;
7382
- await rm6(backup, { force: true, recursive: true });
7383
- }, restorePreviousBundle = async (backup, destination, moved) => {
7384
- if (!moved)
7385
- return;
7386
- await rename7(backup, destination);
7387
- }, installBundle = async (staging, destination) => {
7388
- const backup = `${destination}.previous-${crypto.randomUUID()}`;
7389
- let movedPrevious = false;
7390
- try {
7391
- await rename7(destination, backup);
7392
- movedPrevious = true;
7393
- } catch (error) {
7394
- if (!errorHasCode(error, "ENOENT"))
7395
- throw error;
7396
- }
7397
- try {
7398
- await rename7(staging, destination);
7399
- await removePreviousBundle(backup, movedPrevious);
7400
- } catch (error) {
7401
- await restorePreviousBundle(backup, destination, movedPrevious);
7402
- throw error;
7403
- }
7404
- }, copyClientPage = async (page, buildDirectory, staging, copiedDependencies) => {
7405
- if (!CAPACITOR_CLIENT_FRAMEWORKS.has(page.framework)) {
7406
- throw new TypeError(`Capacitor client rendering does not yet support ${page.framework} page ${page.pageId}.`);
7407
- }
7408
- const extension = extname4(page.bundlePath) || ".js";
7409
- const localBundlePath = `./pages/${page.bundleHash}${extension}`;
7410
- const source = sourceAssetPath(buildDirectory, page.bundlePath);
7411
- await copyFile4(source, join19(staging, localBundlePath));
7412
- await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
7413
- let localStylePath;
7414
- if (page.styleBundlePath && page.styleBundleHash) {
7415
- const styleExtension = extname4(page.styleBundlePath) || ".css";
7416
- localStylePath = `./styles/${page.styleBundleHash}${styleExtension}`;
7417
- const styleSource = sourceAssetPath(buildDirectory, page.styleBundlePath);
7418
- await mkdir9(dirname11(join19(staging, localStylePath)), {
7419
- recursive: true
7420
- });
7421
- await copyFile4(styleSource, join19(staging, localStylePath));
7422
- await copyAbsoluteClientDependencies(styleSource, buildDirectory, staging, copiedDependencies);
7423
- }
7424
- return {
7425
- ...page,
7426
- localBundlePath,
7427
- ...localStylePath ? { localStylePath } : {}
7428
- };
7429
- }, absoluteClientImports = async (sourcePath, buildDirectory) => {
7430
- const source = await readFile10(sourcePath, "utf8");
7431
- const extension = extname4(sourcePath).toLowerCase();
7432
- let scriptLoader;
7433
- if (extension === ".tsx")
7434
- scriptLoader = "tsx";
7435
- else if (extension === ".ts")
7436
- scriptLoader = "ts";
7437
- else if (extension === ".jsx")
7438
- scriptLoader = "jsx";
7439
- else if ([".js", ".mjs", ".cjs"].includes(extension))
7440
- scriptLoader = "js";
7441
- const scriptImports = scriptLoader ? new Bun.Transpiler({ loader: scriptLoader }).scanImports(source).map(({ path }) => path) : [];
7442
- const cssImports = extension === ".css" ? [...source.matchAll(CLIENT_CSS_DEPENDENCY_PATTERN)].flatMap((match) => match[1] ?? []) : [];
7443
- const markupImports = extension === ".html" ? [...source.matchAll(CLIENT_MARKUP_DEPENDENCY_PATTERN)].flatMap((match) => match[1] ?? []) : [];
7444
- return [...scriptImports, ...cssImports, ...markupImports].flatMap((specifier) => {
7445
- if (!specifier)
7446
- return [];
7447
- if (!specifier.startsWith("/") && !specifier.startsWith("./") && !specifier.startsWith("../")) {
7448
- return [];
7449
- }
7450
- const clean = specifier.split(/[?#]/u, 1)[0] ?? specifier;
7451
- if (clean.startsWith("/"))
7452
- return [clean];
7453
- const resolved = resolve14(dirname11(sourcePath), clean);
7454
- const root = resolve14(buildDirectory);
7455
- const relativePath = relative11(root, resolved).replaceAll("\\", "/");
7456
- if (relativePath === ".." || relativePath.startsWith("../")) {
7457
- throw new TypeError(`Mobile client dependency escaped the build directory: ${specifier}`);
7458
- }
7459
- return [`/${relativePath}`];
7460
- });
7461
- }, copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, copied) => {
7462
- if (copied.has(specifier))
7463
- return;
7464
- copied.add(specifier);
7465
- const source = sourceAssetPath(buildDirectory, specifier);
7466
- const destination = join19(staging, specifier.replace(/^\/+/, ""));
7467
- await mkdir9(dirname11(destination), { recursive: true });
7468
- await copyFile4(source, destination);
7469
- await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
7470
- }, copyAbsoluteClientDependencies = async (sourcePath, buildDirectory, staging, copied) => {
7471
- const dependencies = await absoluteClientImports(sourcePath, buildDirectory);
7472
- await Promise.all(dependencies.map((specifier) => copyAbsoluteClientDependency(specifier, buildDirectory, staging, copied)));
7473
- }, materializeAbsoluteCapacitorWebBundle = async (options) => {
7474
- if (!resolveAbsoluteMobileRoute(options.artifact.routes, new URL(options.config.entry, "https://absolute.invalid").pathname)) {
7475
- throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
7476
- }
7477
- const destination = options.config.bundleDirectory;
7478
- await mkdir9(dirname11(destination), { recursive: true });
7479
- const staging = await mkdtemp4(join19(dirname11(destination), `.${basename8(destination)}.stage-`));
7480
- try {
7481
- const pageDirectory = join19(staging, "pages");
7482
- await mkdir9(pageDirectory, { recursive: true });
7483
- await Promise.all(CLIENT_ASSET_DIRECTORIES.map((directory) => ({
7484
- destination: join19(staging, directory),
7485
- source: join19(options.buildDirectory, directory)
7486
- })).filter(({ source }) => existsSync9(source)).map(({ destination: assetDestination, source }) => cp3(source, assetDestination, { recursive: true })));
7487
- const copiedDependencies = new Set;
7488
- const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
7489
- const manifest = {
7490
- appBuild: options.artifact.appBuild,
7491
- ...options.auth ? { auth: options.auth } : {},
7492
- appId: options.config.appId,
7493
- appName: options.config.appName,
7494
- deepLinkHosts: options.config.deepLinkHosts,
7495
- deepLinkScheme: options.config.deepLinkScheme,
7496
- deviceCapabilities: options.deviceCapabilities.capabilities,
7497
- entry: options.config.entry,
7498
- format: ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
7499
- pages,
7500
- productionOrigin: options.config.productionOrigin,
7501
- routes: options.artifact.routes,
7502
- runtime: options.artifact.runtime,
7503
- ...options.sync ? {
7504
- sync: {
7505
- background: {
7506
- endpoint: new URL("/__absolute/sync/background", options.config.productionOrigin).href,
7507
- intervalMinutes: 15
7508
- },
7509
- socketTickets: true,
7510
- storageSchema: options.syncSchema ?? {
7511
- components: [
7512
- { id: "@absolutejs/app", version: 1 }
7513
- ]
7514
- }
7515
- }
7516
- } : {}
7517
- };
7518
- await Promise.all([
7519
- writeFile8(join19(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
7520
- `),
7521
- writeFile8(join19(staging, INDEX_FILE), indexHtml(options.config.appName, options.config.productionOrigin)),
7522
- buildShellBootstrap(staging, options.auth !== undefined, options.auth !== undefined && options.sync === true, `absolutejs.${options.auth?.clientId ?? options.config.appId}.`, options.config.engine, options.deviceCapabilities, options.projectRoot)
7523
- ]);
7524
- await installBundle(staging, destination);
7525
- return manifest;
7526
- } catch (error) {
7527
- await rm6(staging, { force: true, recursive: true });
7528
- throw error;
7529
- }
7530
- };
7531
- var init_capacitorBundle = __esm(() => {
7532
- init_routeMatcher();
7533
- init_transport();
7534
- CLIENT_CSS_DEPENDENCY_PATTERN = /(?:@import\s+(?:url\(\s*)?|url\(\s*)["']?((?:\/|\.\.\/|\.\/)[^"')\s]+)["']?\s*\)?/gu;
7535
- CLIENT_MARKUP_DEPENDENCY_PATTERN = /<(?:script\b[^>]*\bsrc|link\b[^>]*\bhref|img\b[^>]*\bsrc|source\b[^>]*\bsrcset)\s*=\s*["']((?:\/|\.\.\/|\.\/)[^"',\s]+)/giu;
7536
- CAPACITOR_CLIENT_FRAMEWORKS = new Set([
7537
- "angular",
7538
- "html",
7539
- "htmx",
7540
- "react",
7541
- "svelte",
7542
- "vue"
7543
- ]);
7544
- CLIENT_ASSET_DIRECTORIES = ["assets", "html", "htmx", "indexes"];
7545
- });
7546
-
7547
- // src/mobile/artifactStore.ts
7548
- import { createHash as createHash10 } from "crypto";
7549
- var DEFAULT_MAX_PRODUCER_BYTES = 134217728, SHA_2562 = "sha256", hashBlob = async (blob) => {
7550
- const bytes = new Uint8Array(await blob.arrayBuffer());
7551
- return createHash10(SHA_2562).update(bytes).digest("hex");
7552
- }, verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes = DEFAULT_MAX_PRODUCER_BYTES) => {
7553
- const { artifact, producer } = release;
7554
- if (producer.size !== artifact.producer.bytes || producer.size > maxProducerBytes) {
7555
- throw new TypeError("Mobile compatibility producer size does not match its artifact.");
7556
- }
7557
- if (await hashBlob(producer) !== artifact.producer.bundleHash) {
7558
- throw new TypeError("Mobile compatibility producer hash does not match its artifact.");
7559
- }
7560
- return release;
7561
- };
7562
- var init_artifactStore = __esm(() => {
7563
- init_releaseArtifact();
7564
- });
7565
-
7566
- // src/mobile/materializedBundle.ts
7567
- import { createHash as createHash11 } from "crypto";
7568
- import {
7569
- access as access8,
7570
- mkdir as mkdir10,
7571
- mkdtemp as mkdtemp5,
7572
- readFile as readFile11,
7573
- rename as rename8,
7574
- rm as rm7,
7575
- writeFile as writeFile9
7576
- } from "fs/promises";
7577
- import { dirname as dirname12, join as join20, resolve as resolvePath3 } from "path";
7578
- 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) => {
7579
- const identity = JSON.stringify({
7580
- currentReleaseId,
7581
- releases: releases.map(({ releaseId }) => releaseId)
7582
- });
7583
- return `amb_${createHash11("sha256").update(identity).digest("hex")}`;
7584
- }, parseBundleIndex = (value) => {
7585
- 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)) {
7586
- throw new TypeError("Invalid materialized mobile compatibility bundle.");
7587
- }
7588
- const releases = value.releases.map(parseAbsoluteMobileCompatibilityArtifact);
7589
- const retained = retainAbsoluteMobileCompatibilityArtifacts(releases);
7590
- if (retained.length !== releases.length || retained.some(({ releaseId }, index) => releaseId !== releases[index]?.releaseId)) {
7591
- throw new TypeError("Materialized mobile bundle releases are not a retained generation window.");
7592
- }
7593
- const expectedBundleId = bundleIdFor(value.currentReleaseId, releases);
7594
- if (value.bundleId !== expectedBundleId) {
7595
- throw new TypeError("Materialized mobile bundle integrity failed.");
7931
+ throw new TypeError(`Missing device capability provider ${name}.`);
7932
+ return `import { ${provider.factory} as absoluteDeviceCapability${index} } from ${JSON.stringify(await resolveProjectImport(projectRoot, provider.module))};`;
7933
+ }))).join(`
7934
+ `);
7935
+ const pushSetup = push ? `const absoluteMobilePush = createAbsoluteMobileShellPush();
7936
+ const absoluteMobilePushCapability = absoluteDeviceCapability${pushIndex}(absoluteMobilePush.capabilityOptions);
7937
+ ` : "";
7938
+ const capabilityOptions = shellCapabilities.map((name, index) => `${JSON.stringify(name)}: ${name === "pushNotifications" ? "absoluteMobilePushCapability" : `absoluteDeviceCapability${index}()`}`).join(", ");
7939
+ const entryPath = join20(staging, ".absolute-mobile-entry.ts");
7940
+ const baseAdapterModule = capacitor ? await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor") : shellExpoDevicesModule();
7941
+ const adapterImport = capacitor ? `import { installCapacitorDeviceAdapterIfNative } from ${JSON.stringify(baseAdapterModule)};` : `import { createAbsoluteExpoBridgeFetch, installAbsoluteExpoWebDeviceAdapter } from ${JSON.stringify(baseAdapterModule)};`;
7942
+ const adapterInstall = capacitor ? `installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });` : "installAbsoluteExpoWebDeviceAdapter();";
7943
+ let shellOptions = auth ? options : "{ createFetch: createAbsoluteExpoBridgeFetch }";
7944
+ if (capacitor)
7945
+ shellOptions = options;
7946
+ if (push) {
7947
+ shellOptions = `{ createAuth: (config, options) => createAbsoluteMobileShellAuth(config, options), beforeSignOut: absoluteMobilePush.beforeSignOut, connectPush: (auth) => absoluteMobilePush.connect(auth, absoluteMobilePushCapability)${sync ? `, installSync: ${syncFactory}` : ""} }`;
7596
7948
  }
7597
- if (releases[0]?.releaseId !== value.currentReleaseId) {
7598
- throw new TypeError("Materialized mobile bundle current release is not its newest generation.");
7949
+ await writeFile8(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
7950
+ ${adapterImport}
7951
+ ${authImport}${syncImport}${pushImport}${capabilityImports}
7952
+ ${pushSetup}${adapterInstall}
7953
+ void startAbsoluteMobileShell(${shellOptions});
7954
+ `);
7955
+ const build = await Bun.build({
7956
+ entrypoints: [entryPath],
7957
+ minify: true,
7958
+ outdir: staging,
7959
+ target: "browser"
7960
+ });
7961
+ if (!build.success || build.outputs.length !== 1) {
7962
+ throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
7599
7963
  }
7600
- return {
7601
- bundleId: value.bundleId,
7602
- currentReleaseId: value.currentReleaseId,
7603
- format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
7604
- releases
7605
- };
7606
- }, writeRelease = async (root, release) => {
7607
- const directory = join20(root, release.artifact.releaseId);
7608
- const producerPath = join20(directory, release.artifact.producer.module);
7609
- await mkdir10(dirname12(producerPath), { recursive: true });
7610
- await Promise.all([
7611
- writeFile9(join20(directory, ARTIFACT_FILE), `${JSON.stringify(release.artifact, null, "\t")}
7612
- `),
7613
- writeFile9(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
7614
- ]);
7615
- }, installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
7616
- const destination = join20(bundlesRoot, bundleId);
7964
+ await rename7(build.outputs[0]?.path ?? "", join20(staging, BOOTSTRAP_FILE));
7965
+ await rm6(entryPath, { force: true });
7966
+ }, removePreviousBundle = async (backup, moved) => {
7967
+ if (!moved)
7968
+ return;
7969
+ await rm6(backup, { force: true, recursive: true });
7970
+ }, restorePreviousBundle = async (backup, destination, moved) => {
7971
+ if (!moved)
7972
+ return;
7973
+ await rename7(backup, destination);
7974
+ }, installBundle = async (staging, destination) => {
7975
+ const backup = `${destination}.previous-${crypto.randomUUID()}`;
7976
+ let movedPrevious = false;
7617
7977
  try {
7618
- await access8(destination);
7619
- return destination;
7978
+ await rename7(destination, backup);
7979
+ movedPrevious = true;
7620
7980
  } catch (error) {
7621
- if (!errorHasCode2(error, "ENOENT"))
7981
+ if (!errorHasCode(error, "ENOENT"))
7622
7982
  throw error;
7623
7983
  }
7624
- const staging = await mkdtemp5(join20(bundlesRoot, ".stage-"));
7625
7984
  try {
7626
- await Promise.all(releases.map((release) => writeRelease(staging, release)));
7627
- await rename8(staging, destination);
7985
+ await rename7(staging, destination);
7986
+ await removePreviousBundle(backup, movedPrevious);
7628
7987
  } catch (error) {
7629
- await rm7(staging, { force: true, recursive: true });
7630
- if (errorHasCode2(error, "EEXIST") || errorHasCode2(error, "ENOTEMPTY")) {
7631
- return destination;
7632
- }
7988
+ await restorePreviousBundle(backup, destination, movedPrevious);
7633
7989
  throw error;
7634
7990
  }
7635
- return destination;
7636
- }, materializeAbsoluteMobileCompatibilityBundle = async (input) => {
7637
- const releases = await Promise.all(input.releases.map((release) => verifyAbsoluteMobileCompatibilityProducer(release)));
7638
- const artifacts = retainAbsoluteMobileCompatibilityArtifacts(releases.map(({ artifact }) => artifact));
7639
- if (artifacts.length !== releases.length) {
7640
- throw new TypeError("Materialization input must already contain only retained releases.");
7991
+ }, copyClientPage = async (page, buildDirectory, staging, copiedDependencies) => {
7992
+ if (!CAPACITOR_CLIENT_FRAMEWORKS.has(page.framework)) {
7993
+ throw new TypeError(`Capacitor client rendering does not yet support ${page.framework} page ${page.pageId}.`);
7641
7994
  }
7642
- if (artifacts[0]?.releaseId !== input.currentReleaseId) {
7643
- throw new TypeError("Materialization current release must be the newest generation.");
7995
+ const extension = extname4(page.bundlePath) || ".js";
7996
+ const localBundlePath = `./pages/${page.bundleHash}${extension}`;
7997
+ const source = sourceAssetPath(buildDirectory, page.bundlePath);
7998
+ await copyFile4(source, join20(staging, localBundlePath));
7999
+ await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
8000
+ let localStylePath;
8001
+ if (page.styleBundlePath && page.styleBundleHash) {
8002
+ const styleExtension = extname4(page.styleBundlePath) || ".css";
8003
+ localStylePath = `./styles/${page.styleBundleHash}${styleExtension}`;
8004
+ const styleSource = sourceAssetPath(buildDirectory, page.styleBundlePath);
8005
+ await mkdir9(dirname12(join20(staging, localStylePath)), {
8006
+ recursive: true
8007
+ });
8008
+ await copyFile4(styleSource, join20(staging, localStylePath));
8009
+ await copyAbsoluteClientDependencies(styleSource, buildDirectory, staging, copiedDependencies);
7644
8010
  }
7645
- const orderedReleases = artifacts.map((artifact) => {
7646
- const release = releases.find((candidate) => candidate.artifact.releaseId === artifact.releaseId);
7647
- if (!release) {
7648
- throw new TypeError("Materialization input is missing a producer.");
7649
- }
7650
- return release;
7651
- });
7652
- const root = resolvePath3(input.root);
7653
- const bundlesRoot = join20(root, BUNDLES_DIRECTORY);
7654
- await mkdir10(bundlesRoot, { recursive: true });
7655
- const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
7656
- await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
7657
- const index = {
7658
- bundleId,
7659
- currentReleaseId: input.currentReleaseId,
7660
- format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
7661
- releases: artifacts
8011
+ return {
8012
+ ...page,
8013
+ localBundlePath,
8014
+ ...localStylePath ? { localStylePath } : {}
7662
8015
  };
7663
- const pointerPath = join20(root, CURRENT_BUNDLE_FILE);
7664
- const temporaryPointerPath = join20(root, `.current-${crypto.randomUUID()}.json`);
7665
- await writeFile9(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
7666
- `, { flag: "wx" });
7667
- await rename8(temporaryPointerPath, pointerPath);
7668
- return index;
7669
- }, readAbsoluteMobileMaterializedReleases = async (root) => {
7670
- const resolvedRoot = resolvePath3(root);
7671
- try {
7672
- const serialized = await readFile11(join20(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
7673
- const parsed = JSON.parse(serialized);
7674
- const index = parseBundleIndex(parsed);
7675
- const bundleRoot = join20(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
7676
- return Promise.all(index.releases.map(async (artifact) => {
7677
- const producer = Bun.file(join20(bundleRoot, artifact.releaseId, artifact.producer.module));
7678
- await verifyAbsoluteMobileCompatibilityProducer({
7679
- artifact,
7680
- producer
7681
- });
7682
- return { artifact, producer };
7683
- }));
7684
- } catch (error) {
7685
- if (errorHasCode2(error, "ENOENT"))
7686
- return [];
7687
- throw error;
7688
- }
7689
- };
7690
- var init_materializedBundle = __esm(() => {
7691
- init_artifactStore();
7692
- init_releaseArtifact();
7693
- BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
7694
- });
7695
-
7696
- // node_modules/@absolutejs/sync/dist/client/index.js
7697
- var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations")) && Array.isArray(Reflect.get(value, "clients")), registry, SyncLocalDataPolicyError, SyncLocalStoreSchemaError, positiveVersion = (value, label) => {
7698
- if (!Number.isSafeInteger(value) || value < 1)
7699
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", `${label} must be a positive safe integer`);
7700
- return value;
7701
- }, isSchemaBundle = (schema) => ("components" in schema), validatePolicyMatch = (match, label) => {
7702
- if (match.length === 0 || match.trim() !== match || /^\*+$/.test(match) || match.includes("**"))
7703
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.match must be an exact name or a non-empty glob without adjacent wildcards.`);
7704
- }, validateSyncLocalDataPolicy = (policy, label = "localData") => {
7705
- if (policy.maxBytesPerNamespace !== undefined && (!Number.isSafeInteger(policy.maxBytesPerNamespace) || policy.maxBytesPerNamespace < 1))
7706
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.maxBytesPerNamespace must be a positive safe integer.`);
7707
- for (const [index, rule] of (policy.collections ?? []).entries()) {
7708
- validatePolicyMatch(rule.match, `${label}.collections[${index}]`);
7709
- if (rule.maxAgeMs !== undefined && (!Number.isSafeInteger(rule.maxAgeMs) || rule.maxAgeMs < 1))
7710
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}].maxAgeMs must be a positive safe integer.`);
7711
- if (rule.persistence === "memory-only" && rule.protection === "required")
7712
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] cannot require at-rest protection when it is memory-only.`);
7713
- if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
7714
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] declares ${rule.sensitivity} data without required protection or memory-only persistence.`);
7715
- }
7716
- for (const [index, rule] of (policy.mutations ?? []).entries()) {
7717
- validatePolicyMatch(rule.match, `${label}.mutations[${index}]`);
7718
- if (rule.conflict !== undefined && rule.conflict.strategy !== "client-wins" && rule.conflict.strategy !== "manual" && rule.conflict.strategy !== "server-wins")
7719
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.strategy is invalid.`);
7720
- if (rule.conflict?.maxAttempts !== undefined && (!Number.isSafeInteger(rule.conflict.maxAttempts) || rule.conflict.maxAttempts < 1))
7721
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts must be a positive safe integer.`);
7722
- if (rule.conflict?.maxAttempts !== undefined && rule.conflict.strategy !== "client-wins")
7723
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts is only valid for client-wins.`);
7724
- if (rule.persistence === "memory-only" && rule.protection === "required")
7725
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] cannot require at-rest protection when it is memory-only.`);
7726
- if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
7727
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] declares ${rule.sensitivity} arguments without required protection.`);
7728
- }
7729
- return policy;
7730
- }, normalizeSyncLocalSchemaComponents = (schema = { version: 1 }) => {
7731
- const components = isSchemaBundle(schema) ? [...schema.components] : [{ ...schema, id: "@absolutejs/app" }];
7732
- const ids = new Set;
7733
- for (const component of components) {
7734
- if (typeof component.id !== "string" || component.id.trim() !== component.id || component.id.length === 0)
7735
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Sync schema component id must be non-empty and trimmed");
7736
- if (ids.has(component.id))
7737
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync schema component "${component.id}" is declared more than once`);
7738
- ids.add(component.id);
7739
- if (component.localData)
7740
- validateSyncLocalDataPolicy(component.localData, `${component.id}.localData`);
7741
- }
7742
- return components.sort((a, b) => a.id.localeCompare(b.id));
7743
- }, resolveSyncLocalSchemaComponents = (storedVersions, schema = { version: 1 }) => {
7744
- const components = normalizeSyncLocalSchemaComponents(schema).map((component) => {
7745
- const current = resolveSyncLocalMigrations(component.version, component);
7746
- return {
7747
- id: component.id,
7748
- ...resolveSyncLocalMigrations(storedVersions[component.id] ?? current.minimumCompatibleVersion, component)
7749
- };
7750
- });
7751
- const active = new Set(components.map((component) => component.id));
7752
- const orphanedComponents = Object.keys(storedVersions).filter((id) => !active.has(id)).sort();
7753
- return { components, orphanedComponents };
7754
- }, resolveSyncLocalMigrations = (storedVersion, schema = { version: 1 }) => {
7755
- positiveVersion(storedVersion, "Stored Sync schema version");
7756
- const targetVersion = positiveVersion(schema.version, "Target Sync schema version");
7757
- const migrations = [...schema.migrations ?? []].sort((a, b) => a.toVersion - b.toVersion);
7758
- const versions = new Set;
7759
- for (const migration of migrations) {
7760
- positiveVersion(migration.toVersion, "Sync migration toVersion");
7761
- if (versions.has(migration.toVersion))
7762
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync migration ${migration.toVersion} is declared more than once`);
7763
- versions.add(migration.toVersion);
7764
- }
7765
- const inferredMinimum = migrations[0] ? migrations[0].toVersion - 1 : targetVersion;
7766
- const minimumCompatibleVersion = positiveVersion(schema.minimumCompatibleVersion ?? inferredMinimum, "Minimum compatible Sync schema version");
7767
- if (minimumCompatibleVersion > targetVersion)
7768
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Minimum compatible Sync schema version cannot exceed its target");
7769
- if (storedVersion > targetVersion)
7770
- throw new SyncLocalStoreSchemaError("SCHEMA_TOO_NEW", `Stored Sync schema ${storedVersion} is newer than this runtime's schema ${targetVersion}`, { storedVersion, targetVersion });
7771
- if (storedVersion < minimumCompatibleVersion)
7772
- throw new SyncLocalStoreSchemaError("SCHEMA_TOO_OLD", `Stored Sync schema ${storedVersion} is older than the minimum compatible schema ${minimumCompatibleVersion}`, { storedVersion, targetVersion });
7773
- const steps = [];
7774
- for (let version2 = storedVersion + 1;version2 <= targetVersion; version2++) {
7775
- const migration = migrations.find((candidate) => candidate.toVersion === version2);
7776
- if (migration === undefined)
7777
- throw new SyncLocalStoreSchemaError("MIGRATION_MISSING", `Sync migration ${version2 - 1} -> ${version2} is missing`, { storedVersion, targetVersion });
7778
- steps.push(migration);
7779
- }
7780
- return { minimumCompatibleVersion, steps, targetVersion };
7781
- };
7782
- var init_client2 = __esm(() => {
7783
- RUNTIME_TRANSPORT = Symbol.for("@absolutejs/sync/client-runtime-transport");
7784
- host = globalThis;
7785
- registry = (() => {
7786
- const existing = host[RUNTIME_TRANSPORT];
7787
- if (isRegistry(existing))
7788
- return existing;
7789
- if (typeof existing === "object" && existing !== null && Array.isArray(Reflect.get(existing, "installations"))) {
7790
- Reflect.set(existing, "clients", []);
7791
- return existing;
7792
- }
7793
- const created = { clients: [], installations: [] };
7794
- Object.defineProperty(host, RUNTIME_TRANSPORT, {
7795
- configurable: false,
7796
- enumerable: false,
7797
- value: created,
7798
- writable: false
7799
- });
7800
- return created;
7801
- })();
7802
- SyncLocalDataPolicyError = class SyncLocalDataPolicyError extends Error {
7803
- code;
7804
- constructor(code, message) {
7805
- super(message);
7806
- this.name = "SyncLocalDataPolicyError";
7807
- this.code = code;
8016
+ }, absoluteClientImports = async (sourcePath, buildDirectory) => {
8017
+ const source = await readFile10(sourcePath, "utf8");
8018
+ const extension = extname4(sourcePath).toLowerCase();
8019
+ let scriptLoader;
8020
+ if (extension === ".tsx")
8021
+ scriptLoader = "tsx";
8022
+ else if (extension === ".ts")
8023
+ scriptLoader = "ts";
8024
+ else if (extension === ".jsx")
8025
+ scriptLoader = "jsx";
8026
+ else if ([".js", ".mjs", ".cjs"].includes(extension))
8027
+ scriptLoader = "js";
8028
+ const scriptImports = scriptLoader ? new Bun.Transpiler({ loader: scriptLoader }).scanImports(source).map(({ path }) => path) : [];
8029
+ const cssImports = extension === ".css" ? [...source.matchAll(CLIENT_CSS_DEPENDENCY_PATTERN)].flatMap((match) => match[1] ?? []) : [];
8030
+ const markupImports = extension === ".html" ? [...source.matchAll(CLIENT_MARKUP_DEPENDENCY_PATTERN)].flatMap((match) => match[1] ?? []) : [];
8031
+ return [...scriptImports, ...cssImports, ...markupImports].flatMap((specifier) => {
8032
+ if (!specifier)
8033
+ return [];
8034
+ if (!specifier.startsWith("/") && !specifier.startsWith("./") && !specifier.startsWith("../")) {
8035
+ return [];
7808
8036
  }
7809
- };
7810
- SyncLocalStoreSchemaError = class SyncLocalStoreSchemaError extends Error {
7811
- code;
7812
- storedVersion;
7813
- targetVersion;
7814
- constructor(code, message, versions = {}) {
7815
- super(message);
7816
- this.name = "SyncLocalStoreSchemaError";
7817
- this.code = code;
7818
- this.storedVersion = versions.storedVersion;
7819
- this.targetVersion = versions.targetVersion;
8037
+ const clean = specifier.split(/[?#]/u, 1)[0] ?? specifier;
8038
+ if (clean.startsWith("/"))
8039
+ return [clean];
8040
+ const resolved = resolve15(dirname12(sourcePath), clean);
8041
+ const root = resolve15(buildDirectory);
8042
+ const relativePath = relative11(root, resolved).replaceAll("\\", "/");
8043
+ if (relativePath === ".." || relativePath.startsWith("../")) {
8044
+ throw new TypeError(`Mobile client dependency escaped the build directory: ${specifier}`);
7820
8045
  }
7821
- };
7822
- });
7823
-
7824
- // src/mobile/syncSchema.ts
7825
- import { readFileSync as readFileSync11 } from "fs";
7826
- import { dirname as dirname13, join as join21, resolve as resolve15 } from "path";
7827
- var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value), manifestAt = (path) => {
7828
- try {
7829
- const value = JSON.parse(readFileSync11(path, "utf8"));
7830
- return object(value) ? value : undefined;
7831
- } catch {
7832
- return;
7833
- }
7834
- }, localSchemaMetadata = (manifest) => {
7835
- const absolutejs = Reflect.get(manifest, "absolutejs");
7836
- if (!object(absolutejs))
7837
- return;
7838
- const sync = Reflect.get(absolutejs, "sync");
7839
- if (!object(sync))
8046
+ return [`/${relativePath}`];
8047
+ });
8048
+ }, copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, copied) => {
8049
+ if (copied.has(specifier))
7840
8050
  return;
7841
- return Reflect.get(sync, "localSchema");
7842
- }, packageManifestPath = (projectRoot, packageName) => {
7843
- let directory = resolve15(projectRoot);
7844
- while (true) {
7845
- const candidate = join21(directory, "node_modules", packageName, "package.json");
7846
- const manifest = manifestAt(candidate);
7847
- if (manifest && Reflect.get(manifest, "name") === packageName)
7848
- return candidate;
7849
- const parent = dirname13(directory);
7850
- if (parent === directory)
7851
- return;
7852
- directory = parent;
8051
+ copied.add(specifier);
8052
+ const source = sourceAssetPath(buildDirectory, specifier);
8053
+ const destination = join20(staging, specifier.replace(/^\/+/, ""));
8054
+ await mkdir9(dirname12(destination), { recursive: true });
8055
+ await copyFile4(source, destination);
8056
+ await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
8057
+ }, copyAbsoluteClientDependencies = async (sourcePath, buildDirectory, staging, copied) => {
8058
+ const dependencies = await absoluteClientImports(sourcePath, buildDirectory);
8059
+ await Promise.all(dependencies.map((specifier) => copyAbsoluteClientDependency(specifier, buildDirectory, staging, copied)));
8060
+ }, materializeAbsoluteCapacitorWebBundle = async (options) => {
8061
+ if (!resolveAbsoluteMobileRoute(options.artifact.routes, new URL(options.config.entry, "https://absolute.invalid").pathname)) {
8062
+ throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
7853
8063
  }
7854
- }, metadataError = (id, detail) => new TypeError(`Invalid AbsoluteJS Sync schema metadata for ${id}: ${detail}`), positiveVersion2 = (value, id, field) => {
7855
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
7856
- throw metadataError(id, `${field} must be a positive safe integer.`);
7857
- return value;
7858
- }, nonEmpty = (value, id, field) => {
7859
- if (typeof value !== "string" || value.trim() !== value || value.length === 0)
7860
- throw metadataError(id, `${field} must be a non-empty trimmed string.`);
7861
- return value;
7862
- }, requireObject = (value, id, detail) => {
7863
- if (!object(value))
7864
- throw metadataError(id, detail);
7865
- return value;
7866
- }, unknownField = (record, key) => record[key], normalizeJsonValue = (value, id, field) => {
7867
- if (value === null || typeof value === "string" || typeof value === "boolean")
7868
- return value;
7869
- if (typeof value === "number" && Number.isFinite(value))
7870
- return value;
7871
- if (Array.isArray(value))
7872
- return value.map((entry) => normalizeJsonValue(entry, id, field));
7873
- if (object(value))
7874
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
7875
- key,
7876
- normalizeJsonValue(entry, id, field)
7877
- ]));
7878
- throw metadataError(id, `${field} must be JSON-safe.`);
7879
- }, operation = (value, id, index) => {
7880
- const record = requireObject(value, id, `migration operation ${index} must be an object.`);
7881
- const type = Reflect.get(record, "type");
7882
- const collection = nonEmpty(Reflect.get(record, "collection"), id, `operation ${index}.collection`);
7883
- if (type === "delete-collection")
7884
- return { collection, type };
7885
- if (type === "rename-field")
7886
- return {
7887
- collection,
7888
- from: nonEmpty(Reflect.get(record, "from"), id, `operation ${index}.from`),
7889
- to: nonEmpty(Reflect.get(record, "to"), id, `operation ${index}.to`),
7890
- type
7891
- };
7892
- const field = nonEmpty(Reflect.get(record, "field"), id, `operation ${index}.field`);
7893
- if (type === "remove-field")
7894
- return { collection, field, type };
7895
- if (type === "set-default")
7896
- return {
7897
- collection,
7898
- field,
7899
- type,
7900
- value: normalizeJsonValue(Reflect.get(record, "value"), id, `operation ${index}.value`)
7901
- };
7902
- throw metadataError(id, `operation ${index}.type is not supported.`);
7903
- }, migration = (value, id, index) => {
7904
- const record = requireObject(value, id, `migration ${index} must be an object.`);
7905
- const allowed = new Set(["operations", "toVersion"]);
7906
- const unsupported = Object.keys(record).find((key) => !allowed.has(key));
7907
- if (unsupported)
7908
- throw metadataError(id, `migration ${index}.${unsupported} is not declarative metadata.`);
7909
- const declaredOperations = Reflect.get(record, "operations");
7910
- if (declaredOperations !== undefined && !Array.isArray(declaredOperations))
7911
- throw metadataError(id, `migration ${index}.operations must be an array.`);
7912
- const operations = Array.isArray(declaredOperations) ? declaredOperations : [];
7913
- return {
7914
- operations: operations.map((entry, operationIndex) => operation(entry, id, operationIndex)),
7915
- toVersion: positiveVersion2(Reflect.get(record, "toVersion"), id, `migration ${index}.toVersion`)
7916
- };
7917
- }, localDataPolicy = (value, id) => {
7918
- const record = requireObject(value, id, "localData must be an object.");
7919
- const allowed = new Set([
7920
- "collections",
7921
- "maxBytesPerNamespace",
7922
- "mutations"
7923
- ]);
7924
- const unsupported = Object.keys(record).find((key) => !allowed.has(key));
7925
- if (unsupported)
7926
- throw metadataError(id, `localData.${unsupported} is not supported.`);
7927
- const collectionRules = Reflect.get(record, "collections");
7928
- const mutationRules = Reflect.get(record, "mutations");
7929
- if (collectionRules !== undefined && !Array.isArray(collectionRules))
7930
- throw metadataError(id, "localData.collections must be an array.");
7931
- if (mutationRules !== undefined && !Array.isArray(mutationRules))
7932
- throw metadataError(id, "localData.mutations must be an array.");
7933
- const collections = Array.isArray(collectionRules) ? collectionRules.map((entry, index) => {
7934
- const rule = requireObject(entry, id, `localData.collections[${index}] must be an object.`);
7935
- const allowedRuleKeys = new Set([
7936
- "evictionPriority",
7937
- "match",
7938
- "maxAgeMs",
7939
- "onProtectionUnavailable",
7940
- "persistence",
7941
- "protection",
7942
- "sensitivity"
7943
- ]);
7944
- const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
7945
- if (unsupportedRuleKey)
7946
- throw metadataError(id, `localData.collections[${index}].${unsupportedRuleKey} is not supported.`);
7947
- const match = nonEmpty(Reflect.get(rule, "match"), id, `localData.collections[${index}].match`);
7948
- const persistence = unknownField(rule, "persistence");
7949
- const sensitivity = unknownField(rule, "sensitivity");
7950
- const protection = unknownField(rule, "protection");
7951
- const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
7952
- const evictionPriority = unknownField(rule, "evictionPriority");
7953
- const maxAge = unknownField(rule, "maxAgeMs");
7954
- if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
7955
- throw metadataError(id, `localData.collections[${index}].persistence is invalid.`);
7956
- if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
7957
- throw metadataError(id, `localData.collections[${index}].sensitivity is invalid.`);
7958
- if (protection !== undefined && protection !== "none" && protection !== "required")
7959
- throw metadataError(id, `localData.collections[${index}].protection is invalid.`);
7960
- if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
7961
- throw metadataError(id, `localData.collections[${index}].onProtectionUnavailable is invalid.`);
7962
- if (evictionPriority !== undefined && evictionPriority !== "critical" && evictionPriority !== "normal" && evictionPriority !== "disposable")
7963
- throw metadataError(id, `localData.collections[${index}].evictionPriority is invalid.`);
7964
- return {
7965
- match,
7966
- ...sensitivity ? { sensitivity } : {},
7967
- ...persistence ? { persistence } : {},
7968
- ...protection ? { protection } : {},
7969
- ...onProtectionUnavailable ? {
7970
- onProtectionUnavailable
7971
- } : {},
7972
- ...evictionPriority ? { evictionPriority } : {},
7973
- ...maxAge === undefined ? {} : {
7974
- maxAgeMs: positiveVersion2(maxAge, id, `localData.collections[${index}].maxAgeMs`)
7975
- }
7976
- };
7977
- }) : undefined;
7978
- const mutations = Array.isArray(mutationRules) ? mutationRules.map((entry, index) => {
7979
- const rule = requireObject(entry, id, `localData.mutations[${index}] must be an object.`);
7980
- const allowedRuleKeys = new Set([
7981
- "conflict",
7982
- "match",
7983
- "onProtectionUnavailable",
7984
- "persistence",
7985
- "protection",
7986
- "sensitivity"
7987
- ]);
7988
- const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
7989
- if (unsupportedRuleKey)
7990
- throw metadataError(id, `localData.mutations[${index}].${unsupportedRuleKey} is not supported.`);
7991
- const protection = unknownField(rule, "protection");
7992
- const sensitivity = unknownField(rule, "sensitivity");
7993
- const persistence = unknownField(rule, "persistence");
7994
- const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
7995
- const declaredConflict = unknownField(rule, "conflict");
7996
- let conflict;
7997
- if (declaredConflict !== undefined) {
7998
- const conflictRecord = requireObject(declaredConflict, id, `localData.mutations[${index}].conflict must be an object.`);
7999
- const unsupportedConflictKey = Object.keys(conflictRecord).find((key) => key !== "maxAttempts" && key !== "strategy");
8000
- if (unsupportedConflictKey)
8001
- throw metadataError(id, `localData.mutations[${index}].conflict.${unsupportedConflictKey} is not supported.`);
8002
- const strategy = unknownField(conflictRecord, "strategy");
8003
- if (strategy !== "client-wins" && strategy !== "manual" && strategy !== "server-wins")
8004
- throw metadataError(id, `localData.mutations[${index}].conflict.strategy is invalid.`);
8005
- const maxAttempts = unknownField(conflictRecord, "maxAttempts");
8006
- if (maxAttempts !== undefined && strategy !== "client-wins")
8007
- throw metadataError(id, `localData.mutations[${index}].conflict.maxAttempts requires client-wins.`);
8008
- conflict = {
8009
- strategy,
8010
- ...maxAttempts === undefined ? {} : {
8011
- maxAttempts: positiveVersion2(maxAttempts, id, `localData.mutations[${index}].conflict.maxAttempts`)
8064
+ const destination = options.config.bundleDirectory;
8065
+ await mkdir9(dirname12(destination), { recursive: true });
8066
+ const staging = await mkdtemp4(join20(dirname12(destination), `.${basename8(destination)}.stage-`));
8067
+ try {
8068
+ const pageDirectory = join20(staging, "pages");
8069
+ await mkdir9(pageDirectory, { recursive: true });
8070
+ await Promise.all(CLIENT_ASSET_DIRECTORIES.map((directory) => ({
8071
+ destination: join20(staging, directory),
8072
+ source: join20(options.buildDirectory, directory)
8073
+ })).filter(({ source }) => existsSync9(source)).map(({ destination: assetDestination, source }) => cp3(source, assetDestination, { recursive: true })));
8074
+ const copiedDependencies = new Set;
8075
+ const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
8076
+ const manifest = {
8077
+ appBuild: options.artifact.appBuild,
8078
+ ...options.auth ? { auth: options.auth } : {},
8079
+ appId: options.config.appId,
8080
+ appName: options.config.appName,
8081
+ deepLinkHosts: options.config.deepLinkHosts,
8082
+ deepLinkScheme: options.config.deepLinkScheme,
8083
+ deviceCapabilities: options.deviceCapabilities.capabilities,
8084
+ entry: options.config.entry,
8085
+ format: ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
8086
+ pages,
8087
+ productionOrigin: options.config.productionOrigin,
8088
+ routes: options.artifact.routes,
8089
+ runtime: options.artifact.runtime,
8090
+ ...options.sync ? {
8091
+ sync: {
8092
+ background: {
8093
+ endpoint: new URL("/__absolute/sync/background", options.config.productionOrigin).href,
8094
+ intervalMinutes: 15
8095
+ },
8096
+ socketTickets: true,
8097
+ storageSchema: options.syncSchema ?? {
8098
+ components: [
8099
+ { id: "@absolutejs/app", version: 1 }
8100
+ ]
8101
+ }
8012
8102
  }
8013
- };
8014
- }
8015
- if (protection !== undefined && protection !== "none" && protection !== "required")
8016
- throw metadataError(id, `localData.mutations[${index}].protection is invalid.`);
8017
- if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
8018
- throw metadataError(id, `localData.mutations[${index}].sensitivity is invalid.`);
8019
- if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
8020
- throw metadataError(id, `localData.mutations[${index}].onProtectionUnavailable is invalid.`);
8021
- if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
8022
- throw metadataError(id, `localData.mutations[${index}].persistence is invalid.`);
8023
- return {
8024
- match: nonEmpty(Reflect.get(rule, "match"), id, `localData.mutations[${index}].match`),
8025
- ...conflict ? { conflict } : {},
8026
- ...sensitivity ? { sensitivity } : {},
8027
- ...onProtectionUnavailable ? { onProtectionUnavailable } : {},
8028
- ...persistence ? {
8029
- persistence
8030
- } : {},
8031
- ...protection ? { protection } : {}
8103
+ } : {}
8032
8104
  };
8033
- }) : undefined;
8034
- const quota = Reflect.get(record, "maxBytesPerNamespace");
8105
+ await Promise.all([
8106
+ writeFile8(join20(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
8107
+ `),
8108
+ writeFile8(join20(staging, INDEX_FILE), indexHtml(options.config.appName, options.config.productionOrigin)),
8109
+ buildShellBootstrap(staging, options.auth !== undefined, options.auth !== undefined && options.sync === true, `absolutejs.${options.auth?.clientId ?? options.config.appId}.`, options.config.engine, options.deviceCapabilities, options.projectRoot)
8110
+ ]);
8111
+ await installBundle(staging, destination);
8112
+ return manifest;
8113
+ } catch (error) {
8114
+ await rm6(staging, { force: true, recursive: true });
8115
+ throw error;
8116
+ }
8117
+ };
8118
+ var init_capacitorBundle = __esm(() => {
8119
+ init_routeMatcher();
8120
+ init_transport();
8121
+ CLIENT_CSS_DEPENDENCY_PATTERN = /(?:@import\s+(?:url\(\s*)?|url\(\s*)["']?((?:\/|\.\.\/|\.\/)[^"')\s]+)["']?\s*\)?/gu;
8122
+ CLIENT_MARKUP_DEPENDENCY_PATTERN = /<(?:script\b[^>]*\bsrc|link\b[^>]*\bhref|img\b[^>]*\bsrc|source\b[^>]*\bsrcset)\s*=\s*["']((?:\/|\.\.\/|\.\/)[^"',\s]+)/giu;
8123
+ CAPACITOR_CLIENT_FRAMEWORKS = new Set([
8124
+ "angular",
8125
+ "html",
8126
+ "htmx",
8127
+ "react",
8128
+ "svelte",
8129
+ "vue"
8130
+ ]);
8131
+ CLIENT_ASSET_DIRECTORIES = ["assets", "html", "htmx", "indexes"];
8132
+ });
8133
+
8134
+ // src/mobile/artifactStore.ts
8135
+ import { createHash as createHash10 } from "crypto";
8136
+ var DEFAULT_MAX_PRODUCER_BYTES = 134217728, SHA_2562 = "sha256", hashBlob = async (blob) => {
8137
+ const bytes = new Uint8Array(await blob.arrayBuffer());
8138
+ return createHash10(SHA_2562).update(bytes).digest("hex");
8139
+ }, verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes = DEFAULT_MAX_PRODUCER_BYTES) => {
8140
+ const { artifact, producer } = release;
8141
+ if (producer.size !== artifact.producer.bytes || producer.size > maxProducerBytes) {
8142
+ throw new TypeError("Mobile compatibility producer size does not match its artifact.");
8143
+ }
8144
+ if (await hashBlob(producer) !== artifact.producer.bundleHash) {
8145
+ throw new TypeError("Mobile compatibility producer hash does not match its artifact.");
8146
+ }
8147
+ return release;
8148
+ };
8149
+ var init_artifactStore = __esm(() => {
8150
+ init_releaseArtifact();
8151
+ });
8152
+
8153
+ // src/mobile/materializedBundle.ts
8154
+ import { createHash as createHash11 } from "crypto";
8155
+ import {
8156
+ access as access8,
8157
+ mkdir as mkdir10,
8158
+ mkdtemp as mkdtemp5,
8159
+ readFile as readFile11,
8160
+ rename as rename8,
8161
+ rm as rm7,
8162
+ writeFile as writeFile9
8163
+ } from "fs/promises";
8164
+ import { dirname as dirname13, join as join21, resolve as resolvePath3 } from "path";
8165
+ 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) => {
8166
+ const identity = JSON.stringify({
8167
+ currentReleaseId,
8168
+ releases: releases.map(({ releaseId }) => releaseId)
8169
+ });
8170
+ return `amb_${createHash11("sha256").update(identity).digest("hex")}`;
8171
+ }, parseBundleIndex = (value) => {
8172
+ 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)) {
8173
+ throw new TypeError("Invalid materialized mobile compatibility bundle.");
8174
+ }
8175
+ const releases = value.releases.map(parseAbsoluteMobileCompatibilityArtifact);
8176
+ const retained = retainAbsoluteMobileCompatibilityArtifacts(releases);
8177
+ if (retained.length !== releases.length || retained.some(({ releaseId }, index) => releaseId !== releases[index]?.releaseId)) {
8178
+ throw new TypeError("Materialized mobile bundle releases are not a retained generation window.");
8179
+ }
8180
+ const expectedBundleId = bundleIdFor(value.currentReleaseId, releases);
8181
+ if (value.bundleId !== expectedBundleId) {
8182
+ throw new TypeError("Materialized mobile bundle integrity failed.");
8183
+ }
8184
+ if (releases[0]?.releaseId !== value.currentReleaseId) {
8185
+ throw new TypeError("Materialized mobile bundle current release is not its newest generation.");
8186
+ }
8035
8187
  return {
8036
- ...collections ? { collections } : {},
8037
- ...mutations ? { mutations } : {},
8038
- ...quota === undefined ? {} : {
8039
- maxBytesPerNamespace: positiveVersion2(quota, id, "localData.maxBytesPerNamespace")
8040
- }
8188
+ bundleId: value.bundleId,
8189
+ currentReleaseId: value.currentReleaseId,
8190
+ format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
8191
+ releases
8041
8192
  };
8042
- }, component = (id, value) => {
8043
- const record = requireObject(value, id, "localSchema must be an object.");
8044
- const allowed = new Set([
8045
- "localData",
8046
- "migrations",
8047
- "minimumCompatibleVersion",
8048
- "version"
8193
+ }, writeRelease = async (root, release) => {
8194
+ const directory = join21(root, release.artifact.releaseId);
8195
+ const producerPath = join21(directory, release.artifact.producer.module);
8196
+ await mkdir10(dirname13(producerPath), { recursive: true });
8197
+ await Promise.all([
8198
+ writeFile9(join21(directory, ARTIFACT_FILE), `${JSON.stringify(release.artifact, null, "\t")}
8199
+ `),
8200
+ writeFile9(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
8049
8201
  ]);
8050
- const unsupported = Object.keys(record).find((key) => !allowed.has(key));
8051
- if (unsupported)
8052
- throw metadataError(id, `${unsupported} is not supported.`);
8053
- const version2 = positiveVersion2(Reflect.get(record, "version"), id, "version");
8054
- const declaredMinimum = Reflect.get(record, "minimumCompatibleVersion");
8055
- const minimumCompatibleVersion = declaredMinimum === undefined ? Math.max(1, version2 - 2) : positiveVersion2(declaredMinimum, id, "minimumCompatibleVersion");
8056
- const declaredMigrations = Reflect.get(record, "migrations");
8057
- const declaredLocalData = Reflect.get(record, "localData");
8058
- if (declaredMigrations !== undefined && !Array.isArray(declaredMigrations))
8059
- throw metadataError(id, "migrations must be an array.");
8060
- const migrations = Array.isArray(declaredMigrations) ? declaredMigrations : undefined;
8061
- return {
8062
- id,
8063
- ...declaredLocalData === undefined ? {} : { localData: localDataPolicy(declaredLocalData, id) },
8064
- minimumCompatibleVersion,
8065
- ...Array.isArray(migrations) ? {
8066
- migrations: migrations.map((entry, index) => migration(entry, id, index))
8067
- } : {},
8068
- version: version2
8202
+ }, installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
8203
+ const destination = join21(bundlesRoot, bundleId);
8204
+ try {
8205
+ await access8(destination);
8206
+ return destination;
8207
+ } catch (error) {
8208
+ if (!errorHasCode2(error, "ENOENT"))
8209
+ throw error;
8210
+ }
8211
+ const staging = await mkdtemp5(join21(bundlesRoot, ".stage-"));
8212
+ try {
8213
+ await Promise.all(releases.map((release) => writeRelease(staging, release)));
8214
+ await rename8(staging, destination);
8215
+ } catch (error) {
8216
+ await rm7(staging, { force: true, recursive: true });
8217
+ if (errorHasCode2(error, "EEXIST") || errorHasCode2(error, "ENOTEMPTY")) {
8218
+ return destination;
8219
+ }
8220
+ throw error;
8221
+ }
8222
+ return destination;
8223
+ }, materializeAbsoluteMobileCompatibilityBundle = async (input) => {
8224
+ const releases = await Promise.all(input.releases.map((release) => verifyAbsoluteMobileCompatibilityProducer(release)));
8225
+ const artifacts = retainAbsoluteMobileCompatibilityArtifacts(releases.map(({ artifact }) => artifact));
8226
+ if (artifacts.length !== releases.length) {
8227
+ throw new TypeError("Materialization input must already contain only retained releases.");
8228
+ }
8229
+ if (artifacts[0]?.releaseId !== input.currentReleaseId) {
8230
+ throw new TypeError("Materialization current release must be the newest generation.");
8231
+ }
8232
+ const orderedReleases = artifacts.map((artifact) => {
8233
+ const release = releases.find((candidate) => candidate.artifact.releaseId === artifact.releaseId);
8234
+ if (!release) {
8235
+ throw new TypeError("Materialization input is missing a producer.");
8236
+ }
8237
+ return release;
8238
+ });
8239
+ const root = resolvePath3(input.root);
8240
+ const bundlesRoot = join21(root, BUNDLES_DIRECTORY);
8241
+ await mkdir10(bundlesRoot, { recursive: true });
8242
+ const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
8243
+ await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
8244
+ const index = {
8245
+ bundleId,
8246
+ currentReleaseId: input.currentReleaseId,
8247
+ format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
8248
+ releases: artifacts
8069
8249
  };
8070
- }, dependencyNames = (manifest) => [
8071
- Reflect.get(manifest, "dependencies"),
8072
- Reflect.get(manifest, "optionalDependencies"),
8073
- Reflect.get(manifest, "devDependencies"),
8074
- Reflect.get(manifest, "peerDependencies")
8075
- ].flatMap((dependencies) => object(dependencies) ? Object.keys(dependencies) : []).filter((name, index, names) => names.indexOf(name) === index).sort(), discoverAbsoluteSyncSchema = (projectRoot) => {
8076
- const appManifestPath = join21(resolve15(projectRoot), "package.json");
8077
- const appManifest = manifestAt(appManifestPath);
8078
- if (!appManifest)
8079
- return {
8080
- components: [
8081
- {
8082
- id: "@absolutejs/app",
8083
- minimumCompatibleVersion: 1,
8084
- version: 1
8085
- }
8086
- ],
8087
- sources: []
8088
- };
8089
- const appMetadata = localSchemaMetadata(appManifest);
8090
- const components = [
8091
- appMetadata === undefined ? { id: "@absolutejs/app", minimumCompatibleVersion: 1, version: 1 } : component("@absolutejs/app", appMetadata)
8092
- ];
8093
- const sources = [
8094
- { id: "@absolutejs/app", manifestPath: appManifestPath }
8095
- ];
8096
- for (const name of dependencyNames(appManifest)) {
8097
- const manifestPath = packageManifestPath(projectRoot, name);
8098
- if (!manifestPath)
8099
- continue;
8100
- const manifest = manifestAt(manifestPath);
8101
- if (!manifest)
8102
- continue;
8103
- const metadata = localSchemaMetadata(manifest);
8104
- if (metadata === undefined)
8105
- continue;
8106
- components.push(component(name, metadata));
8107
- sources.push({ id: name, manifestPath });
8250
+ const pointerPath = join21(root, CURRENT_BUNDLE_FILE);
8251
+ const temporaryPointerPath = join21(root, `.current-${crypto.randomUUID()}.json`);
8252
+ await writeFile9(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
8253
+ `, { flag: "wx" });
8254
+ await rename8(temporaryPointerPath, pointerPath);
8255
+ return index;
8256
+ }, readAbsoluteMobileMaterializedReleases = async (root) => {
8257
+ const resolvedRoot = resolvePath3(root);
8258
+ try {
8259
+ const serialized = await readFile11(join21(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
8260
+ const parsed = JSON.parse(serialized);
8261
+ const index = parseBundleIndex(parsed);
8262
+ const bundleRoot = join21(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
8263
+ return Promise.all(index.releases.map(async (artifact) => {
8264
+ const producer = Bun.file(join21(bundleRoot, artifact.releaseId, artifact.producer.module));
8265
+ await verifyAbsoluteMobileCompatibilityProducer({
8266
+ artifact,
8267
+ producer
8268
+ });
8269
+ return { artifact, producer };
8270
+ }));
8271
+ } catch (error) {
8272
+ if (errorHasCode2(error, "ENOENT"))
8273
+ return [];
8274
+ throw error;
8108
8275
  }
8109
- components.sort((left, right) => left.id.localeCompare(right.id));
8110
- sources.sort((left, right) => left.id.localeCompare(right.id));
8111
- resolveSyncLocalSchemaComponents({}, { components });
8112
- return { components, sources };
8113
8276
  };
8114
- var init_syncSchema = __esm(() => {
8115
- init_client2();
8277
+ var init_materializedBundle = __esm(() => {
8278
+ init_artifactStore();
8279
+ init_releaseArtifact();
8280
+ BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
8116
8281
  });
8117
8282
 
8118
8283
  // src/mobile/deviceCapabilities.ts
@@ -8440,9 +8605,6 @@ var isElysiaApp = (value) => typeof value === "object" && value !== null && type
8440
8605
  });
8441
8606
  const auth = resolveAbsoluteMobileAuthManifest(options.projectRoot, mobile);
8442
8607
  const sync = auth !== undefined && projectUsesAbsoluteSync(options.projectRoot);
8443
- if (mobile.engine === "expo" && sync) {
8444
- throw new TypeError("Expo mobile Sync is not released yet. Auth and authenticated HTTP are available, but @absolutejs/sync requires @absolutejs/sync-expo for shared durable state.");
8445
- }
8446
8608
  const syncSchema = sync ? discoverAbsoluteSyncSchema(options.projectRoot) : undefined;
8447
8609
  const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot);
8448
8610
  const usesPush = deviceCapabilities.capabilities.includes("pushNotifications");
@@ -23054,12 +23216,12 @@ import { spawn as nodeSpawn } from "child_process";
23054
23216
  import {
23055
23217
  createWriteStream,
23056
23218
  existsSync as existsSync5,
23057
- readFileSync as readFileSync8,
23219
+ readFileSync as readFileSync9,
23058
23220
  rmSync as rmSync2,
23059
23221
  writeFileSync as writeFileSync4
23060
23222
  } from "fs";
23061
23223
  import { tmpdir as tmpdir2 } from "os";
23062
- import { join as join15, resolve as resolvePath2 } from "path";
23224
+ import { join as join16, resolve as resolvePath2 } from "path";
23063
23225
 
23064
23226
  // src/dev/tunnel/client.ts
23065
23227
  var RECONNECT_DELAY_MS = 2000;
@@ -23569,7 +23731,7 @@ init_expoProject();
23569
23731
  init_iosPhysicalDeviceTransport();
23570
23732
  import { spawn } from "child_process";
23571
23733
  import { access as access2 } from "fs/promises";
23572
- import { join as join7 } from "path";
23734
+ import { join as join8 } from "path";
23573
23735
  var METRO_READY_TIMEOUT_MS = 60000;
23574
23736
  var PROCESS_CLOSE_TIMEOUT_MS = 2000;
23575
23737
  var commandEnvironment = (options) => ({
@@ -23582,7 +23744,7 @@ var commandEnvironment = (options) => ({
23582
23744
  ...options.metroHost ? { REACT_NATIVE_PACKAGER_HOSTNAME: options.metroHost } : {}
23583
23745
  });
23584
23746
  var absoluteExpoExecutable = async (project) => {
23585
- const executable = join7(project, "node_modules", ".bin", "expo");
23747
+ const executable = join8(project, "node_modules", ".bin", "expo");
23586
23748
  try {
23587
23749
  await access2(executable);
23588
23750
  return executable;
@@ -23678,18 +23840,18 @@ var stopProcess = async (process2) => {
23678
23840
  return;
23679
23841
  process2.kill("SIGTERM");
23680
23842
  await Promise.race([
23681
- new Promise((resolve4) => process2.once("exit", () => resolve4())),
23682
- new Promise((resolve4) => setTimeout(resolve4, PROCESS_CLOSE_TIMEOUT_MS))
23843
+ new Promise((resolve5) => process2.once("exit", () => resolve5())),
23844
+ new Promise((resolve5) => setTimeout(resolve5, PROCESS_CLOSE_TIMEOUT_MS))
23683
23845
  ]);
23684
23846
  if (process2.exitCode === null)
23685
23847
  process2.kill("SIGKILL");
23686
23848
  };
23687
- var waitForExit = (process2) => new Promise((resolve4) => {
23849
+ var waitForExit = (process2) => new Promise((resolve5) => {
23688
23850
  if (process2.exitCode !== null) {
23689
- resolve4(process2.exitCode);
23851
+ resolve5(process2.exitCode);
23690
23852
  return;
23691
23853
  }
23692
- process2.once("exit", (code) => resolve4(code ?? 1));
23854
+ process2.once("exit", (code) => resolve5(code ?? 1));
23693
23855
  });
23694
23856
  var runUtilityCommand = async (run, command, args, options) => {
23695
23857
  const child = run(command, args, {
@@ -23769,9 +23931,9 @@ var startAbsoluteExpoDevSession = async (options) => {
23769
23931
  }) : undefined;
23770
23932
  let metroReady = false;
23771
23933
  let resolveMetro;
23772
- const metroPromise = new Promise((resolve4, reject) => {
23934
+ const metroPromise = new Promise((resolve5, reject) => {
23773
23935
  if (!metro) {
23774
- resolve4();
23936
+ resolve5();
23775
23937
  return;
23776
23938
  }
23777
23939
  const timeout = setTimeout(() => {
@@ -23779,7 +23941,7 @@ var startAbsoluteExpoDevSession = async (options) => {
23779
23941
  }, METRO_READY_TIMEOUT_MS);
23780
23942
  resolveMetro = () => {
23781
23943
  clearTimeout(timeout);
23782
- resolve4();
23944
+ resolve5();
23783
23945
  };
23784
23946
  metro.once("exit", (code) => {
23785
23947
  if (!metroReady) {
@@ -24237,9 +24399,9 @@ var setupHttpsCert = async (hosts = []) => {
24237
24399
  await setupCertWithPrompt(ensureDevCert2, setupMkcert2, hosts);
24238
24400
  return getDevCertificateAuthorityPath2();
24239
24401
  };
24240
- var mobileReachableHost = (host) => {
24241
- if (host !== "0.0.0.0" && host !== "::")
24242
- return host;
24402
+ var mobileReachableHost = (host2) => {
24403
+ if (host2 !== "0.0.0.0" && host2 !== "::")
24404
+ return host2;
24243
24405
  const address = getLocalIPAddress();
24244
24406
  if (address === "localhost") {
24245
24407
  throw new Error("No LAN address is available for the selected physical device. Connect this computer to the device network or set dev.host explicitly.");
@@ -24334,7 +24496,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24334
24496
  const generated = await writeAbsoluteExpoProject(normalized, {
24335
24497
  projectRoot: process.cwd()
24336
24498
  });
24337
- const dependenciesReady = existsSync5(join15(generated.path, "node_modules", "expo", "package.json")) && existsSync5(join15(generated.path, "node_modules", "expo-dev-client", "package.json"));
24499
+ const dependenciesReady = existsSync5(join16(generated.path, "node_modules", "expo", "package.json")) && existsSync5(join16(generated.path, "node_modules", "expo-dev-client", "package.json"));
24338
24500
  if (!dependenciesReady) {
24339
24501
  const install = await confirmPrompt("Expo development dependencies are missing. Install the pinned SDK 57 development client now?");
24340
24502
  if (!install) {
@@ -24344,7 +24506,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24344
24506
  cwd: generated.path,
24345
24507
  stdio: "inherit"
24346
24508
  });
24347
- const installExit = await new Promise((resolve9) => installProcess.once("exit", (code) => resolve9(code ?? 1)));
24509
+ const installExit = await new Promise((resolve10) => installProcess.once("exit", (code) => resolve10(code ?? 1)));
24348
24510
  if (installExit !== 0) {
24349
24511
  throw new TypeError(`Expo dependency installation exited with status ${installExit}.`);
24350
24512
  }
@@ -24413,7 +24575,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24413
24575
  }
24414
24576
  }
24415
24577
  if (ready) {
24416
- const nativeDirectory = join15(normalized.nativeProjectDirectory, "android");
24578
+ const nativeDirectory = join16(normalized.nativeProjectDirectory, "android");
24417
24579
  let createNativeProject = false;
24418
24580
  if (!existsSync5(nativeDirectory)) {
24419
24581
  createNativeProject = await confirmPrompt("Create the managed Capacitor Android project now?");
@@ -24433,7 +24595,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24433
24595
  if (!remote) {
24434
24596
  console.log(cliTag("\x1B[33m", "iOS target skipped. Pair a Mac with `absolute mobile pair mac <name> <user@host>`."));
24435
24597
  } else {
24436
- const nativeDirectory = join15(normalized.nativeProjectDirectory, "ios");
24598
+ const nativeDirectory = join16(normalized.nativeProjectDirectory, "ios");
24437
24599
  if (!existsSync5(nativeDirectory)) {
24438
24600
  console.log(cliTag("\x1B[33m", "The iOS project is missing. Run `absolute mobile init` before remote development."));
24439
24601
  } else {
@@ -24454,7 +24616,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24454
24616
  }
24455
24617
  }
24456
24618
  if (ready) {
24457
- const nativeDirectory = join15(normalized.nativeProjectDirectory, "ios");
24619
+ const nativeDirectory = join16(normalized.nativeProjectDirectory, "ios");
24458
24620
  let createNativeProject = false;
24459
24621
  if (!existsSync5(nativeDirectory)) {
24460
24622
  createNativeProject = await confirmPrompt("Create the managed Capacitor iOS project now?");
@@ -24806,9 +24968,9 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24806
24968
  iosDevStart = null;
24807
24969
  });
24808
24970
  };
24809
- const absoluteDevOrigin = (host) => {
24971
+ const absoluteDevOrigin = (host2) => {
24810
24972
  const url = new URL(`${httpsEnabled ? "https" : "http"}://localhost`);
24811
- url.hostname = host;
24973
+ url.hostname = host2;
24812
24974
  url.port = String(port);
24813
24975
  return url.origin;
24814
24976
  };
@@ -25074,8 +25236,8 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25074
25236
  });
25075
25237
  if (probe && probe.port !== port) {
25076
25238
  const { port: nextPort } = probe;
25077
- const { host } = refreshedDevConfig;
25078
- const displayHost = host === "0.0.0.0" ? "localhost" : host;
25239
+ const { host: host2 } = refreshedDevConfig;
25240
+ const displayHost = host2 === "0.0.0.0" ? "localhost" : host2;
25079
25241
  console.log(cliTag("\x1B[36m", `Port changed in config \u2014 switching to http://${displayHost}:${nextPort}/`));
25080
25242
  port = nextPort;
25081
25243
  updateLockMetadata(buildDirectory, { port });
@@ -25090,7 +25252,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25090
25252
  for (const name of candidates) {
25091
25253
  let text;
25092
25254
  try {
25093
- text = readFileSync8(resolvePath2(process.cwd(), name), "utf8");
25255
+ text = readFileSync9(resolvePath2(process.cwd(), name), "utf8");
25094
25256
  } catch {
25095
25257
  continue;
25096
25258
  }
@@ -25112,7 +25274,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25112
25274
  }
25113
25275
  return merged;
25114
25276
  };
25115
- const heapPreloadPath = join15(tmpdir2(), `absolute-heap-${process.pid}.ts`);
25277
+ const heapPreloadPath = join16(tmpdir2(), `absolute-heap-${process.pid}.ts`);
25116
25278
  let heapSnapshotEnabled = false;
25117
25279
  try {
25118
25280
  writeFileSync4(heapPreloadPath, DEV_CHILD_PRELOAD);
@@ -25211,9 +25373,9 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25211
25373
  };
25212
25374
  try {
25213
25375
  const { watch: watch3 } = await import("fs");
25214
- const { dirname: dirname8 } = await import("path");
25376
+ const { dirname: dirname9 } = await import("path");
25215
25377
  const absServerEntry = resolvePath2(serverEntry);
25216
- const serverEntryDir = dirname8(absServerEntry);
25378
+ const serverEntryDir = dirname9(absServerEntry);
25217
25379
  const ROOT_RESTART_DENY = new Set([
25218
25380
  "build",
25219
25381
  "dist",
@@ -25245,7 +25407,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25245
25407
  if (now - last < 100)
25246
25408
  return;
25247
25409
  recentlyHandled.set(filename, now);
25248
- scheduleServerRestart(join15(serverEntryDir, filename));
25410
+ scheduleServerRestart(join16(serverEntryDir, filename));
25249
25411
  };
25250
25412
  const recoveryScan = async () => {
25251
25413
  let entries;
@@ -25264,7 +25426,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25264
25426
  continue;
25265
25427
  let fileStat;
25266
25428
  try {
25267
- fileStat = statSync(join15(serverEntryDir, entry.name));
25429
+ fileStat = statSync(join16(serverEntryDir, entry.name));
25268
25430
  } catch {
25269
25431
  continue;
25270
25432
  }
@@ -25652,9 +25814,9 @@ init_eslint();
25652
25814
  init_constants();
25653
25815
  init_utils();
25654
25816
  import { execSync as execSync2 } from "child_process";
25655
- import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
25817
+ import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
25656
25818
  import { arch as arch2, cpus, platform as platform3, totalmem, version } from "os";
25657
- import { resolve as resolve11 } from "path";
25819
+ import { resolve as resolve12 } from "path";
25658
25820
  var bold = (str) => `\x1B[1m${str}\x1B[0m`;
25659
25821
  var getBinaryVersion = (binary, flag = "--version") => {
25660
25822
  try {
@@ -25674,7 +25836,7 @@ var getPackageVersion = (packageName) => {
25674
25836
  const pkgPath = __require.resolve(`${packageName}/package.json`, {
25675
25837
  paths: [process.cwd()]
25676
25838
  });
25677
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
25839
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
25678
25840
  const ver = pkg.version;
25679
25841
  return ver;
25680
25842
  } catch {
@@ -25684,8 +25846,8 @@ var getPackageVersion = (packageName) => {
25684
25846
  var getAbsoluteVersion = () => {
25685
25847
  try {
25686
25848
  const candidates = [
25687
- resolve11(import.meta.dir, "..", "..", "package.json"),
25688
- resolve11(import.meta.dir, "..", "..", "..", "package.json")
25849
+ resolve12(import.meta.dir, "..", "..", "package.json"),
25850
+ resolve12(import.meta.dir, "..", "..", "..", "package.json")
25689
25851
  ];
25690
25852
  const pkgPath = candidates.find((candidate) => existsSync8(candidate));
25691
25853
  if (pkgPath)
@@ -25696,7 +25858,7 @@ var getAbsoluteVersion = () => {
25696
25858
  return getPackageVersion("@absolutejs/absolute");
25697
25859
  };
25698
25860
  var readPackageVersion = (pkgPath) => {
25699
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
25861
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
25700
25862
  const ver = pkg.version;
25701
25863
  return ver;
25702
25864
  };
@@ -25798,7 +25960,7 @@ var info = () => {
25798
25960
  // src/cli/cache.ts
25799
25961
  init_constants();
25800
25962
  import { mkdir as mkdir7 } from "fs/promises";
25801
- import { join as join16 } from "path";
25963
+ import { join as join17 } from "path";
25802
25964
  var {Glob } = globalThis.Bun;
25803
25965
  var CACHE_DIR = ".absolutejs";
25804
25966
  var MAX_FILES_PER_BATCH = 200;
@@ -25850,7 +26012,7 @@ var hashFiles = async (paths) => {
25850
26012
  };
25851
26013
  var loadCache = async (tool) => {
25852
26014
  try {
25853
- const path = join16(CACHE_DIR, `${tool}.cache.json`);
26015
+ const path = join17(CACHE_DIR, `${tool}.cache.json`);
25854
26016
  const data = await Bun.file(path).json();
25855
26017
  const result = data;
25856
26018
  return result;
@@ -25897,7 +26059,7 @@ var runTool = async (adapter, args) => {
25897
26059
  };
25898
26060
  var saveCache = async (tool, data) => {
25899
26061
  await mkdir7(CACHE_DIR, { recursive: true });
25900
- const path = join16(CACHE_DIR, `${tool}.cache.json`);
26062
+ const path = join17(CACHE_DIR, `${tool}.cache.json`);
25901
26063
  await Bun.write(path, JSON.stringify(data, null, "\t"));
25902
26064
  };
25903
26065