@absolutejs/absolute 0.20.0-beta.43 → 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.
Files changed (77) hide show
  1. package/dist/angular/browser.js +378 -361
  2. package/dist/angular/browser.js.map +2 -2
  3. package/dist/angular/components/core/streamingSlotRegistrar.js +7 -7
  4. package/dist/angular/components/core/streamingSlotRegistry.js +4 -4
  5. package/dist/angular/index.js +300 -294
  6. package/dist/angular/index.js.map +2 -2
  7. package/dist/angular/server.js +54 -50
  8. package/dist/angular/server.js.map +2 -2
  9. package/dist/build.js +201 -204
  10. package/dist/build.js.map +3 -3
  11. package/dist/cli/config/client.js +12 -12
  12. package/dist/cli/config/server.js +1096 -1090
  13. package/dist/cli/index.js +1573 -1297
  14. package/dist/client/index.js +29 -25
  15. package/dist/client/index.js.map +2 -2
  16. package/dist/core/streamingSlotRegistrar.js +15 -13
  17. package/dist/core/streamingSlotRegistrar.js.map +2 -2
  18. package/dist/core/streamingSlotRegistry.js +11 -9
  19. package/dist/core/streamingSlotRegistry.js.map +2 -2
  20. package/dist/image-client/imageClient.js +7 -7
  21. package/dist/index.js +810 -733
  22. package/dist/index.js.map +7 -6
  23. package/dist/islands/browser.js +11 -9
  24. package/dist/islands/browser.js.map +2 -2
  25. package/dist/islands/index.js +39 -35
  26. package/dist/islands/index.js.map +2 -2
  27. package/dist/mobile/browser.js +23 -22
  28. package/dist/mobile/browser.js.map +3 -3
  29. package/dist/mobile/index.js +739 -426
  30. package/dist/mobile/index.js.map +11 -11
  31. package/dist/mobile/remoteMacAgentEntry.js +227 -37
  32. package/dist/mobile/shellBootstrap.js +0 -1
  33. package/dist/mobile/shellExpoAuth.js +184 -0
  34. package/dist/mobile/shellExpoDevices.js +4 -5
  35. package/dist/mobile/shellExpoSync.js +104 -0
  36. package/dist/mobile/shellSync.js +4 -4
  37. package/dist/react/browser.js +17 -15
  38. package/dist/react/browser.js.map +2 -2
  39. package/dist/react/components/browser/index.js +107 -107
  40. package/dist/react/components/index.js +118 -116
  41. package/dist/react/components/index.js.map +2 -2
  42. package/dist/react/hooks/index.js +11 -9
  43. package/dist/react/hooks/index.js.map +2 -2
  44. package/dist/react/index.js +53 -49
  45. package/dist/react/index.js.map +2 -2
  46. package/dist/react/jsxDevRuntimeCompat.js +11 -9
  47. package/dist/react/jsxDevRuntimeCompat.js.map +2 -2
  48. package/dist/react/jsxRuntimeCompat.js +11 -9
  49. package/dist/react/jsxRuntimeCompat.js.map +2 -2
  50. package/dist/react/router/browser.js +9 -7
  51. package/dist/react/router/browser.js.map +2 -2
  52. package/dist/react/router/index.js +9 -7
  53. package/dist/react/router/index.js.map +2 -2
  54. package/dist/react/server.js +12 -10
  55. package/dist/react/server.js.map +2 -2
  56. package/dist/src/mobile/devDeviceAdapter.d.ts +1 -1
  57. package/dist/src/mobile/expoBridge.d.ts +12 -12
  58. package/dist/src/mobile/nativeAuth.d.ts +6 -0
  59. package/dist/src/mobile/shellExpoAuth.d.ts +12 -0
  60. package/dist/src/mobile/shellExpoSync.d.ts +18 -0
  61. package/dist/svelte/browser.js +12 -10
  62. package/dist/svelte/browser.js.map +2 -2
  63. package/dist/svelte/index.js +47 -43
  64. package/dist/svelte/index.js.map +2 -2
  65. package/dist/svelte/server.js +29 -27
  66. package/dist/svelte/server.js.map +2 -2
  67. package/dist/vue/browser.js +14 -12
  68. package/dist/vue/browser.js.map +2 -2
  69. package/dist/vue/components/Image.js +9 -7
  70. package/dist/vue/components/Image.js.map +2 -2
  71. package/dist/vue/components/index.js +11 -9
  72. package/dist/vue/components/index.js.map +2 -2
  73. package/dist/vue/index.js +52 -48
  74. package/dist/vue/index.js.map +2 -2
  75. package/dist/vue/server.js +14 -12
  76. package/dist/vue/server.js.map +2 -2
  77. package/package.json +3 -2
package/dist/cli/index.js CHANGED
@@ -847,122 +847,755 @@ var init_config = __esm(() => {
847
847
  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])?))*$/;
848
848
  });
849
849
 
850
- // src/mobile/expoProject.ts
851
- import {
852
- access,
853
- cp,
854
- mkdir,
855
- mkdtemp,
856
- readdir,
857
- readFile,
858
- rename,
859
- rm,
860
- writeFile
861
- } from "fs/promises";
862
- import { createHash } from "crypto";
863
- import { basename as basename2, dirname as dirname3, join as join5, relative, resolve as resolve3 } from "path";
864
- var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
865
- `, EXPO_ASSET_EXTENSION = ".absasset", EXPO_PROJECT_MARKER = ".absolutejs-expo-project", exists = async (path) => {
850
+ // src/mobile/nativeAuth.ts
851
+ import { readFileSync as readFileSync5 } from "fs";
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_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) => {
866
854
  try {
867
- await access(path);
868
- return true;
855
+ return JSON.parse(readFileSync5(join5(projectRoot, "package.json"), "utf8"));
869
856
  } catch {
857
+ return;
858
+ }
859
+ }, packageManifestHas = (manifest, packageName) => {
860
+ if (typeof manifest !== "object" || manifest === null)
870
861
  return false;
862
+ return [
863
+ Reflect.get(manifest, "dependencies"),
864
+ Reflect.get(manifest, "devDependencies"),
865
+ Reflect.get(manifest, "optionalDependencies"),
866
+ Reflect.get(manifest, "peerDependencies")
867
+ ].some((dependencies) => typeof dependencies === "object" && dependencies !== null && Object.hasOwn(dependencies, packageName));
868
+ }, createAbsoluteMobileAuthManifest = (config) => {
869
+ const scheme = config.deepLinkScheme ?? config.appId.toLowerCase();
870
+ return {
871
+ clientId: `absolutejs-native:${config.appId}`,
872
+ issuer: config.productionOrigin,
873
+ redirectUri: `${scheme}://auth/callback`,
874
+ scopes: [...ABSOLUTE_NATIVE_AUTH_SCOPES]
875
+ };
876
+ }, installAbsoluteMobileAuthEnvironment = (projectRoot, config) => {
877
+ const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
878
+ const serialized = serializeAbsoluteMobileAuthEnvironment(config, auth);
879
+ if (serialized === undefined)
880
+ delete process.env[ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV];
881
+ else
882
+ process.env[ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV] = serialized;
883
+ return auth;
884
+ }, projectUsesAbsoluteAuth = (projectRoot) => packageManifestHas(readPackageManifest(projectRoot), ABSOLUTE_AUTH_PACKAGE), projectUsesAbsoluteSync = (projectRoot) => packageManifestHas(readPackageManifest(projectRoot), ABSOLUTE_SYNC_PACKAGE), resolveAbsoluteMobileAuthManifest = (projectRoot, config) => projectUsesAbsoluteAuth(projectRoot) ? createAbsoluteMobileAuthManifest(config) : undefined, serializeAbsoluteMobileAuthEnvironment = (config, auth) => auth === undefined ? undefined : JSON.stringify([
885
+ {
886
+ ...auth,
887
+ name: `${config.appName} native app`
871
888
  }
872
- }, portableRelative = (from, destination) => {
873
- const value = relative(from, destination).replaceAll("\\", "/");
874
- return value.startsWith(".") ? value : `./${value}`;
875
- }, routeSegments = (route) => route.split("/").filter(Boolean).map((segment) => {
876
- if (segment.startsWith(":"))
877
- return `[${segment.slice(1)}]`;
878
- if (segment === "." || segment === ".." || !/^[A-Za-z0-9._~-]+$/u.test(segment)) {
879
- throw new TypeError(`Expo native route ${route} contains an unsupported segment ${segment}.`);
889
+ ]);
890
+ var init_nativeAuth = __esm(() => {
891
+ ABSOLUTE_NATIVE_AUTH_SCOPES = ["openid", "profile"];
892
+ });
893
+
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.`);
880
913
  }
881
- return segment;
882
- }), routeFile = (project, route) => join5(project, "app", ...routeSegments(route), "index.tsx"), expoPackage = () => ({
883
- dependencies: {
884
- expo: "~57.0.9",
885
- "expo-asset": "~57.0.15",
886
- "expo-constants": "~57.0.16",
887
- "expo-dev-client": "~57.0.16",
888
- "expo-file-system": "~57.0.6",
889
- "expo-haptics": "~57.0.2",
890
- "expo-linking": "~57.0.8",
891
- "expo-router": "~57.0.17",
892
- react: "19.2.3",
893
- "react-native": "0.86.3",
894
- "react-native-safe-area-context": "~5.7.0",
895
- "react-native-screens": "4.26.0",
896
- "react-native-webview": "13.16.1"
897
- },
898
- devDependencies: {
899
- "@types/react": "~19.2.2",
900
- typescript: "~6.0.3"
901
- },
902
- main: "expo-router/entry",
903
- name: "absolutejs-expo-shell",
904
- private: true,
905
- scripts: {
906
- android: "expo run:android",
907
- ios: "expo run:ios",
908
- start: "expo start --dev-client"
909
- },
910
- version: "0.0.0"
911
- }), expoAppConfig = (config) => ({
912
- expo: {
913
- android: {
914
- intentFilters: config.deepLinkHosts.map((host) => ({
915
- action: "VIEW",
916
- autoVerify: true,
917
- category: ["BROWSABLE", "DEFAULT"],
918
- data: [{ host, pathPrefix: "/", scheme: "https" }]
919
- })),
920
- package: config.appId
921
- },
922
- experiments: { typedRoutes: true },
923
- ios: {
924
- associatedDomains: config.deepLinkHosts.map((host) => `applinks:${host}`),
925
- bundleIdentifier: config.appId,
926
- ...config.iosVersion ? { buildNumber: config.iosVersion } : {}
927
- },
928
- name: config.appName,
929
- plugins: [
930
- "expo-router",
931
- ["expo-dev-client", { launchMode: "most-recent" }]
932
- ],
933
- runtimeVersion: { policy: "appVersion" },
934
- scheme: config.deepLinkScheme,
935
- slug: config.appId.toLowerCase().replaceAll(".", "-"),
936
- version: config.iosVersion ?? "0.1.0"
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.`);
937
926
  }
938
- }), expoDynamicAppConfig, expoDevelopmentCaPlugin, metroConfig = (projectRoot) => `${EXPO_GENERATED_HEADER}const { getDefaultConfig } = require('expo/metro-config');
939
- const path = require('node:path');
940
-
941
- const projectRoot = __dirname;
942
- const appRoot = ${JSON.stringify(projectRoot)};
943
- const config = getDefaultConfig(projectRoot);
944
- config.resolver.assetExts.push('absasset');
945
- config.resolver.nodeModulesPaths = [
946
- path.join(projectRoot, 'node_modules'),
947
- path.join(appRoot, 'node_modules')
948
- ];
949
- config.watchFolders = [appRoot];
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`);
939
+ }
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
+ };
1021
+ });
950
1022
 
951
- module.exports = config;
952
- `, layoutSource, nativeDiagnosticSource, webHostSource = (config) => {
953
- const nativeRoutes = [
954
- "/__absolute/native",
955
- ...Object.keys(config.expoNativeRoutes)
956
- ];
957
- return `${EXPO_GENERATED_HEADER}import * as Haptics from 'expo-haptics';
958
- import * as Linking from 'expo-linking';
959
- import { router, usePathname } from 'expo-router';
960
- import { useEffect, useRef, useState } from 'react';
961
- import { ActivityIndicator, BackHandler, Platform, StyleSheet, View } from 'react-native';
962
- import { WebView, type WebViewMessageEvent } from 'react-native-webview';
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)
1587
+ ];
1588
+ return `${EXPO_GENERATED_HEADER}import * as Haptics from 'expo-haptics';
1589
+ import * as Linking from 'expo-linking';
1590
+ import { router, usePathname } from 'expo-router';
1591
+ import { useEffect, useRef, useState } from 'react';
1592
+ import { ActivityIndicator, BackHandler, Platform, StyleSheet, View } from 'react-native';
1593
+ import { WebView, type WebViewMessageEvent } from 'react-native-webview';
963
1594
  import { materializeAbsoluteWebBundle } from './webAssets';
1595
+ ${auth ? "import { absoluteExpoAuth, getAbsoluteExpoAuthPrincipal, startAbsoluteExpoAuth } from './AbsoluteAuth';" : ""}
1596
+ ${sync ? "import { createAbsoluteExpoSyncBridge, startAbsoluteExpoSync } from './AbsoluteSync';" : ""}
964
1597
 
965
- const BRIDGE_FORMAT = 1;
1598
+ const BRIDGE_FORMAT = 3;
966
1599
  const MAX_MESSAGE_BYTES = 64 * 1024;
967
1600
  const MAX_HTTP_BODY_BYTES = 48 * 1024;
968
1601
  const NATIVE_ROUTES = new Set(${JSON.stringify(nativeRoutes)});
@@ -971,6 +1604,8 @@ const DEV_ORIGIN = Platform.OS === 'android'
971
1604
  ? process.env.EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN
972
1605
  : process.env.EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN;
973
1606
  const HMR_TARGET = Platform.OS === 'android' ? 'expo-android' : 'expo-ios';
1607
+ const AUTH_ENABLED = ${auth ? "true" : "false"};
1608
+ const SYNC_ENABLED = ${sync ? "true" : "false"};
974
1609
 
975
1610
  const bridgeBootstrap = (path: string) => {
976
1611
  const initialPath = DEV_ORIGIN
@@ -978,6 +1613,7 @@ const bridgeBootstrap = (path: string) => {
978
1613
  : JSON.stringify(path);
979
1614
  return \`(() => {
980
1615
  const pending = new Map();
1616
+ const listeners = new Map();
981
1617
  let sequence = 0;
982
1618
  let currentPath = \${initialPath};
983
1619
  const send = value => {
@@ -987,6 +1623,10 @@ const bridgeBootstrap = (path: string) => {
987
1623
  };
988
1624
  globalThis.__absoluteExpoReceive = source => {
989
1625
  const message = JSON.parse(source);
1626
+ if (message.kind === 'event') {
1627
+ for (const listener of listeners.get(message.event) || []) listener(message.payload || {});
1628
+ return;
1629
+ }
990
1630
  const operation = pending.get(message.id);
991
1631
  if (!operation) return;
992
1632
  pending.delete(message.id);
@@ -994,9 +1634,18 @@ const bridgeBootstrap = (path: string) => {
994
1634
  message.error ? operation.reject(new Error(message.error.message)) : operation.resolve(message.result);
995
1635
  };
996
1636
  globalThis.__absoluteExpoBridge = {
1637
+ on(event, listener) {
1638
+ const values = listeners.get(event) || new Set();
1639
+ values.add(listener);
1640
+ listeners.set(event, values);
1641
+ return () => {
1642
+ values.delete(listener);
1643
+ if (values.size === 0) listeners.delete(event);
1644
+ };
1645
+ },
997
1646
  request(method, params) {
998
1647
  const id = 'web_' + Date.now().toString(36) + '_' + (++sequence).toString(36);
999
- send({ format: 1, id, kind: 'request', method, params, path: currentPath });
1648
+ send({ format: 3, id, kind: 'request', method, params, path: currentPath });
1000
1649
  return new Promise((resolve, reject) => {
1001
1650
  const timer = setTimeout(() => {
1002
1651
  pending.delete(id);
@@ -1007,7 +1656,7 @@ const bridgeBootstrap = (path: string) => {
1007
1656
  },
1008
1657
  setPath(path) {
1009
1658
  currentPath = path;
1010
- send({ format: 1, kind: 'event', event: 'navigation', path });
1659
+ send({ format: 3, kind: 'event', event: 'navigation', path });
1011
1660
  }
1012
1661
  };
1013
1662
  if (\${DEV_ORIGIN ? 'true' : 'false'}) {
@@ -1015,7 +1664,7 @@ const bridgeBootstrap = (path: string) => {
1015
1664
  const path = location.pathname + location.search + location.hash;
1016
1665
  if (path === currentPath) return;
1017
1666
  currentPath = path;
1018
- send({ format: 1, kind: 'event', event: 'navigation', path });
1667
+ send({ format: 3, kind: 'event', event: 'navigation', path });
1019
1668
  };
1020
1669
  for (const method of ['pushState', 'replaceState']) {
1021
1670
  const original = history[method];
@@ -1034,9 +1683,9 @@ const bridgeBootstrap = (path: string) => {
1034
1683
  const url = new URL(anchor.href, location.href);
1035
1684
  if (!${JSON.stringify(nativeRoutes)}.includes(url.pathname)) return;
1036
1685
  event.preventDefault();
1037
- send({ format: 1, 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 });
1038
1687
  }, true);
1039
- send({ format: 1, kind: 'event', event: 'ready', path: currentPath });
1688
+ send({ format: 3, kind: 'event', event: 'ready', path: currentPath });
1040
1689
  })(); true;\`;
1041
1690
  };
1042
1691
 
@@ -1052,16 +1701,16 @@ const impact = async (params: Record<string, unknown>) => {
1052
1701
  };
1053
1702
 
1054
1703
  const bridgeFetch = async (params: Record<string, unknown>) => {
1055
- if (params.method !== 'GET' || typeof params.url !== 'string' || typeof params.headers !== 'object' || params.headers === null || Array.isArray(params.headers)) throw new Error('Expo bridge HTTP request is invalid.');
1704
+ if (typeof params.method !== 'string' || !['DELETE', 'GET', 'PATCH', 'POST', 'PUT'].includes(params.method) || typeof params.url !== 'string' || typeof params.headers !== 'object' || params.headers === null || Array.isArray(params.headers) || params.body !== undefined && typeof params.body !== 'string') throw new Error('Expo bridge HTTP request is invalid.');
1056
1705
  const url = new URL(params.url);
1057
1706
  if (url.origin !== PRODUCTION_ORIGIN || url.username || url.password) throw new Error('Expo bridge HTTP request left productionOrigin.');
1058
1707
  const headers = new Headers();
1059
1708
  for (const [name, value] of Object.entries(params.headers as Record<string, unknown>)) {
1060
1709
  const normalized = name.toLowerCase();
1061
- if (typeof value !== 'string' || (normalized !== 'accept' && !normalized.startsWith('x-absolute-mobile-'))) throw new Error('Expo bridge HTTP header is not allowed.');
1710
+ if (typeof value !== 'string' || (normalized !== 'accept' && normalized !== 'content-type' && !normalized.startsWith('x-absolute-mobile-'))) throw new Error('Expo bridge HTTP header is not allowed.');
1062
1711
  headers.set(normalized, value);
1063
1712
  }
1064
- const response = await fetch(url, { headers, method: 'GET', redirect: 'manual' });
1713
+ const response = await ${auth ? "absoluteExpoAuth.fetchOptional" : "fetch"}(url, { ...(typeof params.body === 'string' ? { body: params.body } : {}), headers, method: params.method, redirect: 'manual' });
1065
1714
  if (new URL(response.url || url.href).origin !== PRODUCTION_ORIGIN || response.status >= 300 && response.status < 400) throw new Error('Expo bridge HTTP redirects are not allowed.');
1066
1715
  const body = await response.text();
1067
1716
  if (new TextEncoder().encode(body).byteLength > MAX_HTTP_BODY_BYTES) throw new Error('Expo bridge HTTP response exceeds 48 KiB.');
@@ -1073,13 +1722,45 @@ const bridgeFetch = async (params: Record<string, unknown>) => {
1073
1722
  return { body, headers: responseHeaders, status: response.status };
1074
1723
  };
1075
1724
 
1725
+ const authStatus = async () => {
1726
+ ${auth ? `const user = await absoluteExpoAuth.status();
1727
+ const principal = getAbsoluteExpoAuthPrincipal();
1728
+ return { principal: principal ? { namespace: principal.namespace } : null, user };` : "throw new Error('Expo Auth is not configured.');"}
1729
+ };
1730
+
1076
1731
  export function AbsoluteWebHost() {
1077
1732
  const pathname = usePathname() || '/';
1078
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);
1079
1735
  const [indexUri, setIndexUri] = useState<string>();
1080
1736
  const [canGoBack, setCanGoBack] = useState(false);
1737
+ const [runtimeReady, setRuntimeReady] = useState(!AUTH_ENABLED && !SYNC_ENABLED);
1081
1738
  const activeWebPath = useRef(pathname);
1082
1739
 
1740
+ useEffect(() => {
1741
+ if (!AUTH_ENABLED && !SYNC_ENABLED) return;
1742
+ let active = true;
1743
+ ${auth ? `const stop = absoluteExpoAuth.onPrincipalChange(principal => {
1744
+ if (!active) return;
1745
+ const source = JSON.stringify({ event: 'auth.principal', format: BRIDGE_FORMAT, kind: 'event', path: activeWebPath.current, payload: { principal: principal ? { namespace: principal.namespace } : null } });
1746
+ webView.current?.injectJavaScript(\`globalThis.__absoluteExpoReceive(\${JSON.stringify(source)}); true;\`);
1747
+ });
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
+ };` : ""}
1762
+ }, []);
1763
+
1083
1764
  useEffect(() => {
1084
1765
  if (DEV_ORIGIN) {
1085
1766
  const target = new URL(pathname, DEV_ORIGIN);
@@ -1129,6 +1810,19 @@ export function AbsoluteWebHost() {
1129
1810
  respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: null });
1130
1811
  } else if (message.method === 'http.fetch') {
1131
1812
  respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await bridgeFetch(message.params as Record<string, unknown>) });
1813
+ } else if (message.method === 'auth.signIn') {
1814
+ ${auth ? `const params = message.params as Record<string, unknown>;
1815
+ if (typeof params.email !== 'string' || typeof params.signup !== 'boolean') throw new Error('Expo Auth sign-in params are invalid.');
1816
+ await absoluteExpoAuth.signIn({ authorizationParameters: { login_hint: params.email, ...(params.signup ? { screen_hint: 'signup' } : {}) } });
1817
+ respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await authStatus() });` : "throw new Error('Expo Auth is not configured.');"}
1818
+ } else if (message.method === 'auth.signOut') {
1819
+ ${auth ? `await absoluteExpoAuth.signOut();
1820
+ respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: null });` : "throw new Error('Expo Auth is not configured.');"}
1821
+ } else if (message.method === 'auth.status') {
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>) });
1132
1826
  } else {
1133
1827
  throw new Error('Expo bridge method is not allowed.');
1134
1828
  }
@@ -1137,7 +1831,7 @@ export function AbsoluteWebHost() {
1137
1831
  }
1138
1832
  };
1139
1833
 
1140
- if (!indexUri) return <View style={styles.loading}><ActivityIndicator /></View>;
1834
+ if (!indexUri || !runtimeReady) return <View style={styles.loading}><ActivityIndicator /></View>;
1141
1835
  return <WebView
1142
1836
  allowFileAccess
1143
1837
  allowFileAccessFromFileURLs
@@ -1159,10 +1853,32 @@ export function AbsoluteWebHost() {
1159
1853
 
1160
1854
  const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, justifyContent: 'center' }, web: { flex: 1 } });
1161
1855
  `;
1162
- }, webRouteSource, catchAllRouteSource, nativeWrapperSource = (wrapper, module) => `${EXPO_GENERATED_HEADER}export { default } from ${JSON.stringify(portableRelative(dirname3(wrapper), module).replace(/\.(?:[cm]?[jt]sx?)$/u, ""))};
1163
- `, expoTsConfig = (projectRoot, project) => ({
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) => ({
1164
1858
  compilerOptions: {
1165
1859
  paths: {
1860
+ ...auth ? {
1861
+ "@absolutejs/auth": [
1862
+ "./node_modules/@absolutejs/auth/dist/index.d.ts"
1863
+ ],
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/*"
1880
+ ]
1881
+ } : {},
1166
1882
  "*": [
1167
1883
  "./node_modules/*",
1168
1884
  `${portableRelative(project, projectRoot)}/node_modules/*`
@@ -1174,7 +1890,7 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
1174
1890
  },
1175
1891
  extends: "expo/tsconfig.base"
1176
1892
  }), writeManagedFile = async (path, source, force) => {
1177
- await mkdir(dirname3(path), { recursive: true });
1893
+ await mkdir(dirname4(path), { recursive: true });
1178
1894
  if (await exists(path)) {
1179
1895
  const current = await readFile(path, "utf8");
1180
1896
  if (current === source)
@@ -1191,8 +1907,11 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
1191
1907
  `, emptyWebAssetsSource, writeAbsoluteExpoProject = async (config, options) => {
1192
1908
  if (config.engine !== "expo")
1193
1909
  throw new TypeError("Expo project generation requires mobile.engine: expo.");
1194
- const projectRoot = resolve3(options.projectRoot);
1910
+ const projectRoot = resolve4(options.projectRoot);
1195
1911
  const project = config.nativeProjectDirectory;
1912
+ const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
1913
+ const syncEnabled = Boolean(auth && projectUsesAbsoluteSync(projectRoot));
1914
+ const syncSchema = syncEnabled ? { components: discoverAbsoluteSyncSchema(projectRoot).components } : undefined;
1196
1915
  const routeModules = Object.entries(config.expoNativeRoutes);
1197
1916
  const moduleChecks = await Promise.all(routeModules.map(async ([route, module]) => ({
1198
1917
  exists: await exists(module),
@@ -1203,7 +1922,7 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
1203
1922
  if (missing) {
1204
1923
  throw new TypeError(`Expo native route ${missing.route} references missing module ${missing.module}.`);
1205
1924
  }
1206
- const marker = join5(project, EXPO_PROJECT_MARKER);
1925
+ const marker = join7(project, EXPO_PROJECT_MARKER);
1207
1926
  if (await exists(project) && !await exists(marker)) {
1208
1927
  const entries = await readdir(project);
1209
1928
  if (entries.length > 0 && !options.force) {
@@ -1213,47 +1932,63 @@ const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, jus
1213
1932
  await mkdir(project, { recursive: true });
1214
1933
  await writeFile(marker, `format=1
1215
1934
  `);
1935
+ const authEnabled = Boolean(auth);
1216
1936
  const files = new Map([
1217
1937
  [
1218
- join5(project, ".gitignore"),
1938
+ join7(project, ".gitignore"),
1219
1939
  `.expo/
1220
1940
  android/
1221
1941
  ios/
1222
1942
  node_modules/
1223
1943
  `
1224
1944
  ],
1225
- [join5(project, "app.json"), jsonSource(expoAppConfig(config))],
1226
- [join5(project, "app.config.js"), expoDynamicAppConfig],
1227
- [join5(project, "package.json"), jsonSource(expoPackage())],
1228
- [join5(project, "metro.config.js"), metroConfig(projectRoot)],
1229
1945
  [
1230
- join5(project, "plugins", "withAbsoluteDevelopmentCa.js"),
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))
1953
+ ],
1954
+ [join7(project, "metro.config.js"), metroConfig(projectRoot)],
1955
+ [
1956
+ join7(project, "plugins", "withAbsoluteDevelopmentCa.js"),
1231
1957
  expoDevelopmentCaPlugin
1232
1958
  ],
1233
1959
  [
1234
- join5(project, "tsconfig.json"),
1235
- jsonSource(expoTsConfig(projectRoot, project))
1960
+ join7(project, "tsconfig.json"),
1961
+ jsonSource(expoTsConfig(projectRoot, project, authEnabled, syncEnabled))
1962
+ ],
1963
+ [
1964
+ join7(project, "app", "_layout.tsx"),
1965
+ layoutSource(authEnabled, syncEnabled)
1236
1966
  ],
1237
- [join5(project, "app", "_layout.tsx"), layoutSource],
1238
1967
  [
1239
- join5(project, "app", "__absolute", "native", "index.tsx"),
1968
+ join7(project, "app", "__absolute", "native", "index.tsx"),
1240
1969
  nativeDiagnosticSource
1241
1970
  ],
1242
1971
  [
1243
- join5(project, "src", "generated", "AbsoluteWebHost.tsx"),
1244
- webHostSource(config)
1972
+ join7(project, "src", "generated", "AbsoluteWebHost.tsx"),
1973
+ webHostSource(config, auth, syncEnabled)
1245
1974
  ]
1246
1975
  ]);
1247
- const webAssetsPath = join5(project, "src", "generated", "webAssets.ts");
1976
+ if (auth) {
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));
1981
+ }
1982
+ const webAssetsPath = join7(project, "src", "generated", "webAssets.ts");
1248
1983
  if (!await exists(webAssetsPath)) {
1249
1984
  files.set(webAssetsPath, emptyWebAssetsSource);
1250
1985
  }
1251
1986
  if (!config.expoNativeRoutes["/"]) {
1252
- files.set(join5(project, "app", "index.tsx"), webRouteSource);
1987
+ files.set(join7(project, "app", "index.tsx"), webRouteSource);
1253
1988
  }
1254
- files.set(join5(project, "app", "[...absolute].tsx"), catchAllRouteSource);
1989
+ files.set(join7(project, "app", "[...absolute].tsx"), catchAllRouteSource);
1255
1990
  for (const [route, module] of routeModules) {
1256
- const wrapper = route === "/" ? join5(project, "app", "index.tsx") : routeFile(project, route);
1991
+ const wrapper = route === "/" ? join7(project, "app", "index.tsx") : routeFile(project, route);
1257
1992
  files.set(wrapper, nativeWrapperSource(wrapper, module));
1258
1993
  }
1259
1994
  const changes = await Promise.all([...files].map(([path, source]) => writeManagedFile(path, source, true)));
@@ -1262,7 +1997,7 @@ node_modules/
1262
1997
  }, walkFiles = async (root, directory = root) => {
1263
1998
  const entries = await readdir(directory, { withFileTypes: true });
1264
1999
  const nested = await Promise.all(entries.map((entry) => {
1265
- const path = join5(directory, entry.name);
2000
+ const path = join7(directory, entry.name);
1266
2001
  if (entry.isDirectory())
1267
2002
  return walkFiles(root, path);
1268
2003
  if (entry.isFile())
@@ -1323,11 +2058,11 @@ export const materializeAbsoluteWebBundle = async () => {
1323
2058
  `, syncAbsoluteExpoWebAssets = async (config) => {
1324
2059
  if (config.engine !== "expo")
1325
2060
  throw new TypeError("Expo asset sync requires mobile.engine: expo.");
1326
- const marker = join5(config.nativeProjectDirectory, EXPO_PROJECT_MARKER);
2061
+ const marker = join7(config.nativeProjectDirectory, EXPO_PROJECT_MARKER);
1327
2062
  if (!await exists(marker)) {
1328
2063
  throw new TypeError("Expo asset sync requires an AbsoluteJS-managed Expo project. Run mobile init first.");
1329
2064
  }
1330
- const manifestPath = join5(config.bundleDirectory, "absolute-mobile-manifest.json");
2065
+ const manifestPath = join7(config.bundleDirectory, "absolute-mobile-manifest.json");
1331
2066
  const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
1332
2067
  const appBuild = typeof manifest === "object" && manifest !== null && typeof Reflect.get(manifest, "appBuild") === "string" ? String(Reflect.get(manifest, "appBuild")) : undefined;
1333
2068
  if (!appBuild)
@@ -1342,16 +2077,16 @@ export const materializeAbsoluteWebBundle = async () => {
1342
2077
  bundleHash.update("\x00");
1343
2078
  });
1344
2079
  const bundleId = `amexpo_${bundleHash.digest("hex")}`;
1345
- const destination = join5(config.nativeProjectDirectory, "assets", "absolute");
1346
- await mkdir(dirname3(destination), { recursive: true });
1347
- const staging = await mkdtemp(join5(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-`));
1348
2083
  let assets;
1349
2084
  try {
1350
2085
  assets = await Promise.all(files.map(async (source, index) => {
1351
2086
  const name = `${String(index).padStart(6, "0")}${EXPO_ASSET_EXTENSION}`;
1352
- await cp(source, join5(staging, name));
2087
+ await cp(source, join7(staging, name));
1353
2088
  return {
1354
- asset: portableRelative(join5(config.nativeProjectDirectory, "src", "generated"), join5(destination, name)),
2089
+ asset: portableRelative(join7(config.nativeProjectDirectory, "src", "generated"), join7(destination, name)),
1355
2090
  path: relative(config.bundleDirectory, source).replaceAll("\\", "/")
1356
2091
  };
1357
2092
  }));
@@ -1360,11 +2095,13 @@ export const materializeAbsoluteWebBundle = async () => {
1360
2095
  await rm(staging, { force: true, recursive: true });
1361
2096
  throw error;
1362
2097
  }
1363
- const generated = join5(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
2098
+ const generated = join7(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
1364
2099
  await writeManagedFile(generated, assetModuleSource(assets, bundleId), true);
1365
2100
  return { appBuild, assets: assets.length, bundleId, path: destination };
1366
2101
  };
1367
2102
  var init_expoProject = __esm(() => {
2103
+ init_nativeAuth();
2104
+ init_syncSchema();
1368
2105
  expoDynamicAppConfig = `${EXPO_GENERATED_HEADER}const config = require('./app.json');
1369
2106
 
1370
2107
  if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH) {
@@ -1415,12 +2152,6 @@ const withAbsoluteDevelopmentCa = config => {
1415
2152
  };
1416
2153
 
1417
2154
  module.exports = withAbsoluteDevelopmentCa;
1418
- `;
1419
- layoutSource = `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
1420
-
1421
- export default function AbsoluteLayout() {
1422
- return <Stack screenOptions={{ headerShown: false }} />;
1423
- }
1424
2155
  `;
1425
2156
  nativeDiagnosticSource = `${EXPO_GENERATED_HEADER}import * as Haptics from 'expo-haptics';
1426
2157
  import { Link } from 'expo-router';
@@ -1476,14 +2207,14 @@ import {
1476
2207
  isIP
1477
2208
  } from "net";
1478
2209
  import { readFile as readFile2 } from "fs/promises";
1479
- var closeServer = (server) => new Promise((resolve4, reject) => {
2210
+ var closeServer = (server) => new Promise((resolve5, reject) => {
1480
2211
  server.close((error) => {
1481
2212
  if (error)
1482
2213
  reject(error);
1483
2214
  else
1484
- resolve4();
2215
+ resolve5();
1485
2216
  });
1486
- }), listen = (server, port) => new Promise((resolve4, reject) => {
2217
+ }), listen = (server, port) => new Promise((resolve5, reject) => {
1487
2218
  server.once("error", reject);
1488
2219
  server.listen(port, "0.0.0.0", () => {
1489
2220
  server.off("error", reject);
@@ -1492,11 +2223,11 @@ var closeServer = (server) => new Promise((resolve4, reject) => {
1492
2223
  reject(new Error("Could not determine the iOS device helper port."));
1493
2224
  return;
1494
2225
  }
1495
- resolve4(address.port);
2226
+ resolve5(address.port);
1496
2227
  });
1497
2228
  }), findEphemeralPort = async () => {
1498
2229
  const probe = createTcpServer();
1499
- const port = await new Promise((resolve4, reject) => {
2230
+ const port = await new Promise((resolve5, reject) => {
1500
2231
  probe.once("error", reject);
1501
2232
  probe.listen(0, "127.0.0.1", () => {
1502
2233
  const address = probe.address();
@@ -1504,7 +2235,7 @@ var closeServer = (server) => new Promise((resolve4, reject) => {
1504
2235
  reject(new Error("Could not allocate the iOS CA enrollment port."));
1505
2236
  return;
1506
2237
  }
1507
- resolve4(address.port);
2238
+ resolve5(address.port);
1508
2239
  });
1509
2240
  });
1510
2241
  await closeServer(probe);
@@ -1519,9 +2250,9 @@ var closeServer = (server) => new Promise((resolve4, reject) => {
1519
2250
  if (!normalized || normalized.length > 256 || /[\0\r\n]/u.test(normalized))
1520
2251
  throw new TypeError("--ios-device requires a valid Xcode device identifier or name.");
1521
2252
  return normalized;
1522
- }, urlForHost = (protocol, host, port) => {
2253
+ }, urlForHost = (protocol, host2, port) => {
1523
2254
  const url = new URL(`${protocol}://localhost:${port}`);
1524
- const normalizedHost = normalizeAbsoluteIosDeviceHost(host);
2255
+ const normalizedHost = normalizeAbsoluteIosDeviceHost(host2);
1525
2256
  url.hostname = isIP(normalizedHost) === 6 ? `[${normalizedHost}]` : normalizedHost;
1526
2257
  return url;
1527
2258
  }, startAbsoluteIosCaEnrollmentServer = async (options) => {
@@ -1544,73 +2275,29 @@ var closeServer = (server) => new Promise((resolve4, reject) => {
1544
2275
  "Content-Length": String(certificateBytes.byteLength),
1545
2276
  "Content-Type": "application/x-x509-ca-cert",
1546
2277
  "X-Content-Type-Options": "nosniff"
1547
- });
1548
- response.end(certificateBytes);
1549
- });
1550
- const port = await findEphemeralPort();
1551
- await listen(server, port);
1552
- const url = urlForHost("http", options.displayHost, port);
1553
- url.pathname = certificatePath;
1554
- return {
1555
- url: url.href,
1556
- close: () => closeServer(server)
1557
- };
1558
- };
1559
- var init_iosPhysicalDeviceTransport = () => {};
1560
-
1561
- // src/mobile/nativeAuth.ts
1562
- import { readFileSync as readFileSync5 } from "fs";
1563
- import { join as join7 } from "path";
1564
- var ABSOLUTE_AUTH_PACKAGE = "@absolutejs/auth", ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV = "ABSOLUTE_AUTH_NATIVE_CLIENTS", ABSOLUTE_NATIVE_AUTH_SCOPES, ABSOLUTE_SYNC_PACKAGE = "@absolutejs/sync", readPackageManifest = (projectRoot) => {
1565
- try {
1566
- return JSON.parse(readFileSync5(join7(projectRoot, "package.json"), "utf8"));
1567
- } catch {
1568
- return;
1569
- }
1570
- }, packageManifestHas = (manifest, packageName) => {
1571
- if (typeof manifest !== "object" || manifest === null)
1572
- return false;
1573
- return [
1574
- Reflect.get(manifest, "dependencies"),
1575
- Reflect.get(manifest, "devDependencies"),
1576
- Reflect.get(manifest, "optionalDependencies"),
1577
- Reflect.get(manifest, "peerDependencies")
1578
- ].some((dependencies) => typeof dependencies === "object" && dependencies !== null && Object.hasOwn(dependencies, packageName));
1579
- }, createAbsoluteMobileAuthManifest = (config) => {
1580
- const scheme = config.deepLinkScheme ?? config.appId.toLowerCase();
1581
- return {
1582
- clientId: `absolutejs-native:${config.appId}`,
1583
- issuer: config.productionOrigin,
1584
- redirectUri: `${scheme}://auth/callback`,
1585
- scopes: [...ABSOLUTE_NATIVE_AUTH_SCOPES]
1586
- };
1587
- }, installAbsoluteMobileAuthEnvironment = (projectRoot, config) => {
1588
- const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
1589
- const serialized = serializeAbsoluteMobileAuthEnvironment(config, auth);
1590
- if (serialized === undefined)
1591
- delete process.env[ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV];
1592
- else
1593
- process.env[ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV] = serialized;
1594
- return auth;
1595
- }, projectUsesAbsoluteAuth = (projectRoot) => packageManifestHas(readPackageManifest(projectRoot), ABSOLUTE_AUTH_PACKAGE), projectUsesAbsoluteSync = (projectRoot) => packageManifestHas(readPackageManifest(projectRoot), ABSOLUTE_SYNC_PACKAGE), resolveAbsoluteMobileAuthManifest = (projectRoot, config) => projectUsesAbsoluteAuth(projectRoot) ? createAbsoluteMobileAuthManifest(config) : undefined, serializeAbsoluteMobileAuthEnvironment = (config, auth) => auth === undefined ? undefined : JSON.stringify([
1596
- {
1597
- ...auth,
1598
- name: `${config.appName} native app`
1599
- }
1600
- ]);
1601
- var init_nativeAuth = __esm(() => {
1602
- ABSOLUTE_NATIVE_AUTH_SCOPES = ["openid", "profile"];
1603
- });
2278
+ });
2279
+ response.end(certificateBytes);
2280
+ });
2281
+ const port = await findEphemeralPort();
2282
+ await listen(server, port);
2283
+ const url = urlForHost("http", options.displayHost, port);
2284
+ url.pathname = certificatePath;
2285
+ return {
2286
+ url: url.href,
2287
+ close: () => closeServer(server)
2288
+ };
2289
+ };
2290
+ var init_iosPhysicalDeviceTransport = () => {};
1604
2291
 
1605
2292
  // src/cli/utils.ts
1606
2293
  var {$: $2 } = globalThis.Bun;
1607
2294
  import { execSync } from "child_process";
1608
- import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
2295
+ import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
1609
2296
  import { createServer as createServer3 } from "net";
1610
- import { resolve as resolve4 } from "path";
2297
+ import { resolve as resolve5 } from "path";
1611
2298
  var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backend/server.ts", isWSLEnvironment = () => {
1612
2299
  try {
1613
- const release = readFileSync6("/proc/version", "utf-8");
2300
+ const release = readFileSync7("/proc/version", "utf-8");
1614
2301
  return /microsoft|wsl/i.test(release);
1615
2302
  } catch {
1616
2303
  return false;
@@ -1709,7 +2396,7 @@ var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backe
1709
2396
  }, printHint = () => {
1710
2397
  console.log("\x1B[90mpress h + enter to show shortcuts\x1B[0m");
1711
2398
  }, readDbScripts = async () => {
1712
- const pkgPath = resolve4("package.json");
2399
+ const pkgPath = resolve5("package.json");
1713
2400
  if (!existsSync3(pkgPath))
1714
2401
  return null;
1715
2402
  const pkg = await Bun.file(pkgPath).json();
@@ -1744,7 +2431,7 @@ var init_utils = __esm(() => {
1744
2431
  // src/mobile/emulatorDoctor.ts
1745
2432
  import { access as access3 } from "fs/promises";
1746
2433
  import { homedir as homedir3 } from "os";
1747
- import { join as join8 } from "path";
2434
+ import { join as join9 } from "path";
1748
2435
  var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command) => {
1749
2436
  try {
1750
2437
  const result = Bun.spawnSync(command, {
@@ -1787,17 +2474,17 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1787
2474
  } catch {
1788
2475
  return;
1789
2476
  }
1790
- }, absoluteManagedAndroidSdkRoot = (host, env = process.env) => {
1791
- if (host === "windows") {
1792
- 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");
1793
2480
  }
1794
- if (host === "wsl") {
2481
+ if (host2 === "wsl") {
1795
2482
  const localAppData = windowsLocalAppDataFromWsl();
1796
2483
  if (localAppData) {
1797
- return join8(localAppData, "AbsoluteJS", "Android", "Sdk");
2484
+ return join9(localAppData, "AbsoluteJS", "Android", "Sdk");
1798
2485
  }
1799
2486
  }
1800
- return join8(homedir3(), ".absolutejs", "android-sdk");
2487
+ return join9(homedir3(), ".absolutejs", "android-sdk");
1801
2488
  }, pathExists = async (path) => {
1802
2489
  try {
1803
2490
  await access3(path);
@@ -1813,18 +2500,18 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1813
2500
  if (platform2 === "linux" && wsl)
1814
2501
  return "wsl";
1815
2502
  return "linux";
1816
- }, executableNames = (host, name) => {
1817
- if (host === "wsl")
2503
+ }, executableNames = (host2, name) => {
2504
+ if (host2 === "wsl")
1818
2505
  return [`${name}.exe`, `${name}.bat`, name];
1819
- if (host === "windows")
2506
+ if (host2 === "windows")
1820
2507
  return [name, `${name}.exe`, `${name}.bat`];
1821
2508
  return [name];
1822
- }, findExecutable = async (name, paths, options, host) => {
2509
+ }, findExecutable = async (name, paths, options, host2) => {
1823
2510
  const existing = await Promise.all(paths.map(async (path) => await options.exists(path) ? path : undefined));
1824
2511
  const configured = existing.find((path) => path !== undefined);
1825
2512
  if (configured)
1826
2513
  return configured;
1827
- for (const candidate of executableNames(host, name)) {
2514
+ for (const candidate of executableNames(host2, name)) {
1828
2515
  const path = options.which(candidate);
1829
2516
  if (path)
1830
2517
  return path;
@@ -1844,23 +2531,23 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1844
2531
  status: "fail"
1845
2532
  }, inspectAbsoluteMobileToolchain = async (input = {}) => {
1846
2533
  const env = input.env ?? process.env;
1847
- const host = input.host ?? detectAbsoluteMobileHost();
2534
+ const host2 = input.host ?? detectAbsoluteMobileHost();
1848
2535
  const exists2 = input.exists ?? pathExists;
1849
2536
  const which = input.which ?? ((command) => Bun.which(command));
1850
2537
  const capture = input.capture ?? captureCommand;
1851
- const androidRoot = input.androidRoot === null ? undefined : input.androidRoot ?? env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host, env);
1852
- const windowsAndroidTools = host === "windows" || host === "wsl";
1853
- 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;
1854
2541
  const paths = (values) => values.filter((value) => Boolean(value));
1855
2542
  const adb = await findExecutable("adb", paths([
1856
2543
  android(["platform-tools", windowsAndroidTools ? "adb.exe" : "adb"])
1857
- ]), { exists: exists2, which }, host);
2544
+ ]), { exists: exists2, which }, host2);
1858
2545
  const emulator = await findExecutable("emulator", paths([
1859
2546
  android([
1860
2547
  "emulator",
1861
2548
  windowsAndroidTools ? "emulator.exe" : "emulator"
1862
2549
  ])
1863
- ]), { exists: exists2, which }, host);
2550
+ ]), { exists: exists2, which }, host2);
1864
2551
  const sdkmanager = await findExecutable("sdkmanager", paths([
1865
2552
  android([
1866
2553
  "cmdline-tools",
@@ -1868,7 +2555,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1868
2555
  "bin",
1869
2556
  windowsAndroidTools ? "sdkmanager.bat" : "sdkmanager"
1870
2557
  ])
1871
- ]), { exists: exists2, which }, host);
2558
+ ]), { exists: exists2, which }, host2);
1872
2559
  const avdmanager = await findExecutable("avdmanager", paths([
1873
2560
  android([
1874
2561
  "cmdline-tools",
@@ -1876,12 +2563,12 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1876
2563
  "bin",
1877
2564
  windowsAndroidTools ? "avdmanager.bat" : "avdmanager"
1878
2565
  ])
1879
- ]), { exists: exists2, which }, host);
1880
- const java = await findExecutable("java", [], { exists: exists2, which }, host);
2566
+ ]), { exists: exists2, which }, host2);
2567
+ const java = await findExecutable("java", [], { exists: exists2, which }, host2);
1881
2568
  const checks = [
1882
2569
  {
1883
2570
  id: "host",
1884
- label: `Development host: ${host}`,
2571
+ label: `Development host: ${host2}`,
1885
2572
  platform: "host",
1886
2573
  status: "pass"
1887
2574
  },
@@ -1902,7 +2589,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1902
2589
  status: hasManagedAvd ? "pass" : "fail"
1903
2590
  });
1904
2591
  }
1905
- if (host === "wsl") {
2592
+ if (host2 === "wsl") {
1906
2593
  checks.push({
1907
2594
  id: "android.virtualization",
1908
2595
  label: adb?.endsWith(".exe") ? "Windows-host Android bridge available to WSL" : "WSL requires a Windows-host emulator bridge or Linux KVM",
@@ -1910,7 +2597,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1910
2597
  remediation: adb?.endsWith(".exe") ? undefined : "Expose the Windows Android SDK adb.exe to WSL, or enable /dev/kvm for a Linux SDK.",
1911
2598
  status: adb?.endsWith(".exe") ? "pass" : "warn"
1912
2599
  });
1913
- } else if (host === "linux") {
2600
+ } else if (host2 === "linux") {
1914
2601
  const hasKvm = await exists2("/dev/kvm");
1915
2602
  checks.push({
1916
2603
  id: "android.virtualization",
@@ -1920,7 +2607,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1920
2607
  status: hasKvm ? "pass" : "warn"
1921
2608
  });
1922
2609
  }
1923
- if (host !== "macos") {
2610
+ if (host2 !== "macos") {
1924
2611
  checks.push({
1925
2612
  id: "ios.simulator",
1926
2613
  label: "iOS Simulator requires macOS and Xcode",
@@ -1929,8 +2616,8 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
1929
2616
  });
1930
2617
  return checks;
1931
2618
  }
1932
- const xcrun = await findExecutable("xcrun", [], { exists: exists2, which }, host);
1933
- 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);
1934
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."));
1935
2622
  if (xcrun) {
1936
2623
  const runtimes = capture([
@@ -1957,7 +2644,7 @@ var init_emulatorDoctor = __esm(() => {
1957
2644
 
1958
2645
  // src/mobile/capacitorProject.ts
1959
2646
  import { access as access4, readFile as readFile3, rename as rename2, writeFile as writeFile2 } from "fs/promises";
1960
- import { relative as relative2, resolve as resolve5 } from "path";
2647
+ import { relative as relative2, resolve as resolve6 } from "path";
1961
2648
  var CONFIG_FILE = "capacitor.config.ts", portableRelative2 = (root, path) => relative2(root, path).replaceAll("\\", "/"), capacitorConfigSource = (config, projectRoot) => `import type { CapacitorConfig } from '@capacitor/cli';
1962
2649
 
1963
2650
  const config: CapacitorConfig = {
@@ -1981,8 +2668,8 @@ export default config;
1981
2668
  return false;
1982
2669
  }
1983
2670
  }, writeAbsoluteCapacitorConfig = async (config, options) => {
1984
- const projectRoot = resolve5(options.projectRoot);
1985
- const destination = resolve5(projectRoot, CONFIG_FILE);
2671
+ const projectRoot = resolve6(options.projectRoot);
2672
+ const destination = resolve6(projectRoot, CONFIG_FILE);
1986
2673
  const source = capacitorConfigSource(config, projectRoot);
1987
2674
  if (await exists2(destination)) {
1988
2675
  const current = await readFile3(destination, "utf8");
@@ -2015,11 +2702,11 @@ import {
2015
2702
  } from "fs/promises";
2016
2703
  import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
2017
2704
  import {
2018
- dirname as dirname4,
2705
+ dirname as dirname5,
2019
2706
  isAbsolute,
2020
- join as join9,
2707
+ join as join10,
2021
2708
  relative as relative3,
2022
- resolve as resolve6,
2709
+ resolve as resolve7,
2023
2710
  sep,
2024
2711
  win32
2025
2712
  } from "path";
@@ -2189,21 +2876,21 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2189
2876
  return;
2190
2877
  throw new DOMException("Android development startup was cancelled.", "AbortError");
2191
2878
  }, journalPaths = (projectRoot) => {
2192
- const root = join9(projectRoot, ".absolutejs", "mobile", "dev-session");
2879
+ const root = join10(projectRoot, ".absolutejs", "mobile", "dev-session");
2193
2880
  return {
2194
- backup: join9(root, "capacitor.config.backup.json"),
2195
- caBackup: join9(root, "absolutejs_dev_ca.backup.pem"),
2196
- journal: join9(root, "journal.json"),
2197
- manifestBackup: join9(root, "AndroidManifest.backup.xml"),
2198
- 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"),
2199
2886
  root
2200
2887
  };
2201
2888
  }, isInside = (root, path) => {
2202
- const resolvedRoot = resolve6(root);
2203
- const resolvedPath = resolve6(path);
2889
+ const resolvedRoot = resolve7(root);
2890
+ const resolvedPath = resolve7(path);
2204
2891
  const relativePath = relative3(resolvedRoot, resolvedPath);
2205
2892
  return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
2206
- }, 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) => {
2207
2894
  if (!isRecord(value))
2208
2895
  return null;
2209
2896
  const { appId, fingerprint, format, installations } = value;
@@ -2222,7 +2909,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2222
2909
  }, readNativeCache = async (projectRoot) => readFile4(nativeCachePath(projectRoot), "utf8").then((source) => parseNativeCache(JSON.parse(source))).catch(() => null), writeNativeCache = async (projectRoot, cache) => {
2223
2910
  const destination = nativeCachePath(projectRoot);
2224
2911
  const temporary = `${destination}.${process.pid}.${randomUUID2()}.tmp`;
2225
- await mkdir2(dirname4(destination), { recursive: true });
2912
+ await mkdir2(dirname5(destination), { recursive: true });
2226
2913
  try {
2227
2914
  await writeFile3(temporary, `${JSON.stringify(cache, null, "\t")}
2228
2915
  `, {
@@ -2235,11 +2922,11 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2235
2922
  });
2236
2923
  }
2237
2924
  }, nativeDependencySources = async (nativeDirectory) => {
2238
- const settings = await readFile4(join9(nativeDirectory, "capacitor.settings.gradle"), "utf8");
2925
+ const settings = await readFile4(join10(nativeDirectory, "capacitor.settings.gradle"), "utf8");
2239
2926
  const pattern = new RegExp(CAPACITOR_PROJECT_DIRECTORY_PATTERN.source, CAPACITOR_PROJECT_DIRECTORY_PATTERN.flags);
2240
2927
  const dependencies = [...settings.matchAll(pattern)].map((match) => ({
2241
2928
  name: (match[1] ?? "").slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_"),
2242
- source: resolve6(nativeDirectory, match[2] ?? "")
2929
+ source: resolve7(nativeDirectory, match[2] ?? "")
2243
2930
  }));
2244
2931
  if (dependencies.length === 0) {
2245
2932
  throw new Error("Capacitor Android settings did not declare any native dependencies.");
@@ -2275,7 +2962,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2275
2962
  }, collectNativeDirectory = async (root, label, directory, ignorePublicBundle) => {
2276
2963
  const entries = await readdir2(directory, { withFileTypes: true });
2277
2964
  entries.sort((left, right) => left.name.localeCompare(right.name));
2278
- 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)));
2279
2966
  return records.flat();
2280
2967
  }, hashNativeTree = async (root, label, ignorePublicBundle) => {
2281
2968
  const resolvedRoot = await realpath(root);
@@ -2328,7 +3015,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2328
3015
  await rm2(file.path, { force: true });
2329
3016
  return;
2330
3017
  }
2331
- await mkdir2(dirname4(file.path), { recursive: true });
3018
+ await mkdir2(dirname5(file.path), { recursive: true });
2332
3019
  await copyFile(file.backupPath, file.path);
2333
3020
  }, repairAbsoluteAndroidDevSession = async (projectRoot) => {
2334
3021
  const paths = journalPaths(projectRoot);
@@ -2341,11 +3028,11 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2341
3028
  throw new Error(`Refusing unsafe or invalid mobile dev journal at ${paths.journal}.`);
2342
3029
  }
2343
3030
  if (await pathExists2(journal.backupPath)) {
2344
- await mkdir2(dirname4(journal.nativeConfigPath), { recursive: true });
3031
+ await mkdir2(dirname5(journal.nativeConfigPath), { recursive: true });
2345
3032
  await copyFile(journal.backupPath, journal.nativeConfigPath);
2346
3033
  }
2347
3034
  if (journal.manifestBackupPath && journal.nativeManifestPath && await pathExists2(journal.manifestBackupPath)) {
2348
- await mkdir2(dirname4(journal.nativeManifestPath), { recursive: true });
3035
+ await mkdir2(dirname5(journal.nativeManifestPath), { recursive: true });
2349
3036
  await copyFile(journal.manifestBackupPath, journal.nativeManifestPath);
2350
3037
  }
2351
3038
  await Promise.all((journal.projectedFiles ?? []).map(restoreAndroidProjectedFile));
@@ -2404,11 +3091,11 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2404
3091
  await mkdir2(paths.root, { recursive: true });
2405
3092
  await writeFile3(paths.backup, source, { flag: "wx" });
2406
3093
  await writeFile3(paths.manifestBackup, manifestSource, { flag: "wx" });
2407
- const resourceRoot = join9(dirname4(nativeManifestPath), "res");
2408
- 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");
2409
3096
  const existingNetworkConfig = manifestSource.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
2410
3097
  const networkConfigName = existingNetworkConfig ?? "absolutejs_dev_network_security";
2411
- const networkConfigPath = join9(resourceRoot, "xml", `${networkConfigName}.xml`);
3098
+ const networkConfigPath = join10(resourceRoot, "xml", `${networkConfigName}.xml`);
2412
3099
  const backupProjectedFile = async ([path, backupPath]) => {
2413
3100
  if (!await pathExists2(path))
2414
3101
  return { path };
@@ -2445,8 +3132,8 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2445
3132
  `);
2446
3133
  if (https && certificateAuthorityPath) {
2447
3134
  await Promise.all([
2448
- mkdir2(dirname4(caPath), { recursive: true }),
2449
- mkdir2(dirname4(networkConfigPath), { recursive: true })
3135
+ mkdir2(dirname5(caPath), { recursive: true }),
3136
+ mkdir2(dirname5(networkConfigPath), { recursive: true })
2450
3137
  ]);
2451
3138
  await copyFile(certificateAuthorityPath, caPath);
2452
3139
  const existingNetworkConfigSource = existingNetworkConfig ? await readFile4(networkConfigPath, "utf8") : `<?xml version="1.0" encoding="utf-8"?>
@@ -2501,12 +3188,12 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2501
3188
  }
2502
3189
  return result.stdout.trim();
2503
3190
  }, mirroredCapacitorDependencies = async (project, capture) => {
2504
- const settingsPath = join9(project.nativeDirectory, "capacitor.settings.gradle");
3191
+ const settingsPath = join10(project.nativeDirectory, "capacitor.settings.gradle");
2505
3192
  const settings = await readFile4(settingsPath, "utf8");
2506
3193
  const dependencies = [];
2507
3194
  const rewrittenSettings = settings.replace(CAPACITOR_PROJECT_DIRECTORY_PATTERN, (_statement, projectName, sourcePath) => {
2508
3195
  const name = projectName.slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_");
2509
- const source = resolve6(project.nativeDirectory, sourcePath);
3196
+ const source = resolve7(project.nativeDirectory, sourcePath);
2510
3197
  dependencies.push({
2511
3198
  name,
2512
3199
  windowsSource: windowsPathFromWsl(source, capture)
@@ -2547,7 +3234,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2547
3234
  ].join("; ");
2548
3235
  return Buffer.from(source, "utf16le").toString("base64");
2549
3236
  }, gradleArtifactPath = (nativeDirectory, task, windows = false) => {
2550
- const pathJoin = windows ? win32.join : join9;
3237
+ const pathJoin = windows ? win32.join : join10;
2551
3238
  if (task === "assembleDebug") {
2552
3239
  return pathJoin(nativeDirectory, "app", "build", "outputs", "apk", "debug", "app-debug.apk");
2553
3240
  }
@@ -2560,7 +3247,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2560
3247
  if (task !== "assembleRelease" || await pathExists2(primary)) {
2561
3248
  return primary;
2562
3249
  }
2563
- 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");
2564
3251
  }, buildAbsoluteAndroidGradleArtifact = async (options) => {
2565
3252
  const { project, task } = options;
2566
3253
  const capture = options.capture ?? captureCommand2;
@@ -2570,7 +3257,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2570
3257
  if (project.host === "wsl") {
2571
3258
  const windowsSource = windowsPathFromWsl(project.nativeDirectory, capture);
2572
3259
  const buildId = Bun.hash(project.projectRoot).toString(HASH_RADIX);
2573
- const managedBuildDirectory = resolve6(project.androidRoot, "..", "..", "Builds", `${project.config.appId}-${buildId}`);
3260
+ const managedBuildDirectory = resolve7(project.androidRoot, "..", "..", "Builds", `${project.config.appId}-${buildId}`);
2574
3261
  const windowsDirectory = windowsPathFromWsl(managedBuildDirectory, capture);
2575
3262
  const windowsAndroidRoot = windowsPathFromWsl(project.androidRoot, capture);
2576
3263
  const { dependencies, rewrittenSettings } = await mirroredCapacitorDependencies(project, capture);
@@ -2753,10 +3440,10 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2753
3440
  "chromium:I"
2754
3441
  ], { env }, onLine);
2755
3442
  }, prepareAbsoluteAndroidDevProject = async (config, options) => {
2756
- const projectRoot = resolve6(options.projectRoot);
2757
- const host = detectAbsoluteMobileHost();
2758
- const androidRoot = process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host);
2759
- 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 });
2760
3447
  const deviceOnlyChecks = new Set([
2761
3448
  "android.avd",
2762
3449
  "android.avdmanager",
@@ -2772,18 +3459,18 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2772
3459
  if (!adb || options.target !== "device" && !emulator) {
2773
3460
  throw new Error("Android SDK tools disappeared after readiness checks.");
2774
3461
  }
2775
- 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");
2776
3463
  if (!await pathExists2(cap)) {
2777
3464
  throw new Error("Capacitor CLI is not installed. Run absolute mobile init first.");
2778
3465
  }
2779
3466
  await writeAbsoluteCapacitorConfig(config, { projectRoot });
2780
3467
  await mkdir2(config.bundleDirectory, { recursive: true });
2781
- const placeholder = join9(config.bundleDirectory, "index.html");
3468
+ const placeholder = join10(config.bundleDirectory, "index.html");
2782
3469
  if (!await pathExists2(placeholder)) {
2783
3470
  await writeFile3(placeholder, `<!doctype html><title>AbsoluteJS mobile development</title>
2784
3471
  `);
2785
3472
  }
2786
- const nativeDirectory = join9(config.nativeProjectDirectory, "android");
3473
+ const nativeDirectory = join10(config.nativeProjectDirectory, "android");
2787
3474
  if (!await pathExists2(nativeDirectory)) {
2788
3475
  if (!options.createNativeProject) {
2789
3476
  throw new Error("Android native project has not been created.");
@@ -2801,7 +3488,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2801
3488
  cap,
2802
3489
  config,
2803
3490
  ...emulator ? { emulator } : {},
2804
- host,
3491
+ host: host2,
2805
3492
  nativeDirectory,
2806
3493
  projectRoot
2807
3494
  };
@@ -2844,8 +3531,8 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, DEV_JOURNAL_F
2844
3531
  await requireSuccess([project.cap, "sync", "android"], "Capacitor Android synchronization", run, { cwd: project.projectRoot, env, signal: options.signal });
2845
3532
  throwIfAborted(options.signal);
2846
3533
  transition("configuring");
2847
- const nativeConfigPath = join9(project.nativeDirectory, "app", "src", "main", "assets", "capacitor.config.json");
2848
- 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");
2849
3536
  let connectedSerial;
2850
3537
  let nativeLogs = null;
2851
3538
  const closeNativeLogs = async () => {
@@ -3004,7 +3691,7 @@ var init_androidEmulatorController = __esm(() => {
3004
3691
  import { createHash as createHash3 } from "crypto";
3005
3692
  import { cp as cp2, mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile5, rm as rm3 } from "fs/promises";
3006
3693
  import { tmpdir } from "os";
3007
- import { basename as basename3, join as join10 } from "path";
3694
+ import { basename as basename3, join as join11 } from "path";
3008
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 = {}) => {
3009
3696
  const subprocess = Bun.spawn(command, {
3010
3697
  env: options.env,
@@ -3036,33 +3723,33 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3036
3723
  exitCode: result.exitCode,
3037
3724
  stdout: result.stdout.toString()
3038
3725
  };
3039
- }, 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) => {
3040
3727
  const match = /^\/mnt\/([a-z])\/(.*)$/i.exec(path);
3041
3728
  if (!match)
3042
3729
  return path;
3043
3730
  return `${match[1]?.toUpperCase()}:\\${match[2]?.replaceAll("/", "\\")}`;
3044
- }, runnableCommand = (host, path, args) => {
3045
- if (host !== "wsl" || !path.endsWith(".bat"))
3731
+ }, runnableCommand = (host2, path, args) => {
3732
+ if (host2 !== "wsl" || !path.endsWith(".bat"))
3046
3733
  return [path, ...args];
3047
3734
  return ["cmd.exe", "/d", "/c", windowsPath(path), ...args];
3048
- }, systemImageFor = (host, arch2) => {
3049
- 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";
3050
3737
  return `system-images;android-${ANDROID_API};google_apis;${imageArch}`;
3051
3738
  }, planAbsoluteMobileEmulatorInstall = (platform2, input = {}) => {
3052
- const host = input.host ?? detectAbsoluteMobileHost();
3739
+ const host2 = input.host ?? detectAbsoluteMobileHost();
3053
3740
  const env = {
3054
3741
  ...process.env,
3055
3742
  ...input.env
3056
3743
  };
3057
- 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);
3058
3745
  if (platform2 === "ios") {
3059
3746
  return {
3060
3747
  androidRoot,
3061
- host,
3748
+ host: host2,
3062
3749
  platform: platform2,
3063
3750
  steps: [
3064
3751
  {
3065
- 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.",
3066
3753
  id: "ios.runtime",
3067
3754
  label: "Install iOS Simulator runtime"
3068
3755
  }
@@ -3071,7 +3758,7 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3071
3758
  }
3072
3759
  return {
3073
3760
  androidRoot,
3074
- host,
3761
+ host: host2,
3075
3762
  platform: platform2,
3076
3763
  steps: [
3077
3764
  {
@@ -3091,15 +3778,15 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3091
3778
  }
3092
3779
  ]
3093
3780
  };
3094
- }, releaseFor = (host, arch2) => {
3095
- let platform2 = host;
3096
- if (host === "wsl")
3781
+ }, releaseFor = (host2, arch2) => {
3782
+ let platform2 = host2;
3783
+ if (host2 === "wsl")
3097
3784
  platform2 = "windows";
3098
- if (host === "macos")
3785
+ if (host2 === "macos")
3099
3786
  platform2 = "darwin";
3100
3787
  const release = COMMAND_LINE_TOOLS[`${platform2}-${arch2}`];
3101
3788
  if (!release) {
3102
- 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}.`);
3103
3790
  }
3104
3791
  return release;
3105
3792
  }, installCommandLineTools = async (plan, input) => {
@@ -3110,10 +3797,10 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3110
3797
  if (digest !== release.sha256) {
3111
3798
  throw new Error(`Android command-line-tools checksum mismatch: expected ${release.sha256}, received ${digest}.`);
3112
3799
  }
3113
- const temporary = await mkdtemp2(join10(tmpdir(), "absolutejs-android-sdk-"));
3800
+ const temporary = await mkdtemp2(join11(tmpdir(), "absolutejs-android-sdk-"));
3114
3801
  try {
3115
- const archive = join10(temporary, "command-line-tools.zip");
3116
- const extracted = join10(temporary, "extracted");
3802
+ const archive = join11(temporary, "command-line-tools.zip");
3803
+ const extracted = join11(temporary, "extracted");
3117
3804
  await Bun.write(archive, bytes);
3118
3805
  await mkdir3(extracted, { recursive: true });
3119
3806
  const extraction = plan.host === "windows" ? [
@@ -3130,12 +3817,12 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3130
3817
  if (await input.run(extraction) !== 0) {
3131
3818
  throw new Error("Failed to extract Android command-line tools.");
3132
3819
  }
3133
- const destination = join10(plan.androidRoot, "cmdline-tools", "latest");
3134
- await mkdir3(join10(plan.androidRoot, "cmdline-tools"), {
3820
+ const destination = join11(plan.androidRoot, "cmdline-tools", "latest");
3821
+ await mkdir3(join11(plan.androidRoot, "cmdline-tools"), {
3135
3822
  recursive: true
3136
3823
  });
3137
3824
  await rm3(destination, { force: true, recursive: true });
3138
- await cp2(join10(extracted, "cmdline-tools"), destination, {
3825
+ await cp2(join11(extracted, "cmdline-tools"), destination, {
3139
3826
  recursive: true
3140
3827
  });
3141
3828
  } finally {
@@ -3145,9 +3832,9 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3145
3832
  if (await run(command, options) !== 0) {
3146
3833
  throw new Error(`${label} failed.`);
3147
3834
  }
3148
- }, installJavaRuntime = async (host, run, which) => {
3149
- if (host === "windows" || host === "wsl") {
3150
- 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";
3151
3838
  if (!which(winget)) {
3152
3839
  throw new Error("Java 21 is required. Install Temurin 21 or make winget available, then run mobile doctor --fix again.");
3153
3840
  }
@@ -3160,10 +3847,10 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
3160
3847
  "--accept-package-agreements",
3161
3848
  "--accept-source-agreements"
3162
3849
  ];
3163
- 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);
3164
3851
  return;
3165
3852
  }
3166
- if (host === "macos") {
3853
+ if (host2 === "macos") {
3167
3854
  if (!which("brew")) {
3168
3855
  throw new Error("Java 21 is required. Install Homebrew or Temurin 21, then run mobile doctor --fix again.");
3169
3856
  }
@@ -3320,7 +4007,7 @@ import {
3320
4007
  stat,
3321
4008
  writeFile as writeFile4
3322
4009
  } from "fs/promises";
3323
- 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";
3324
4011
  var developmentTeamArgument = (value) => {
3325
4012
  if (value === undefined)
3326
4013
  return;
@@ -3377,7 +4064,7 @@ var developmentTeamArgument = (value) => {
3377
4064
  }, ignoredFingerprintDirectories, fingerprintFiles = async (root, current = root, options = {}) => {
3378
4065
  const entries = await readdir3(current, { withFileTypes: true });
3379
4066
  const nested = await Promise.all(entries.sort((left, right) => left.name.localeCompare(right.name)).map(async (entry) => {
3380
- const path = join11(current, entry.name);
4067
+ const path = join12(current, entry.name);
3381
4068
  const projectRelative = relative4(root, path).replaceAll("\\", "/");
3382
4069
  const ignored = entry.isDirectory() && (ignoredFingerprintDirectories.has(entry.name) || projectRelative === "App/App/public" && options.includePublicBundle !== true);
3383
4070
  if (ignored)
@@ -3399,8 +4086,8 @@ var developmentTeamArgument = (value) => {
3399
4086
  });
3400
4087
  return hasher.digest("hex");
3401
4088
  }, safeOutputDirectory = (projectRoot, requested) => {
3402
- const root = resolve7(projectRoot);
3403
- const output = resolve7(root, requested ?? ".absolutejs/mobile/releases/ios");
4089
+ const root = resolve8(projectRoot);
4090
+ const output = resolve8(root, requested ?? ".absolutejs/mobile/releases/ios");
3404
4091
  const projectRelative = relative4(root, output);
3405
4092
  if (projectRelative === ".." || projectRelative.startsWith(`..${sep2}`) || isAbsolute2(projectRelative)) {
3406
4093
  throw new TypeError("mobile build --outdir must remain inside the project.");
@@ -3411,7 +4098,7 @@ var developmentTeamArgument = (value) => {
3411
4098
  return;
3412
4099
  const entries = await readdir3(root, { withFileTypes: true });
3413
4100
  const matches = await Promise.all(entries.map(async (entry) => {
3414
- const path = join11(root, entry.name);
4101
+ const path = join12(root, entry.name);
3415
4102
  if (entry.isDirectory() && entry.name.endsWith(extension))
3416
4103
  return path;
3417
4104
  if (entry.isFile() && entry.name.endsWith(extension))
@@ -3434,10 +4121,10 @@ var developmentTeamArgument = (value) => {
3434
4121
  throw new TypeError("iOS build number must be a positive integer.");
3435
4122
  return value;
3436
4123
  }, installRelease = async (artifactPath, metadata, outputRoot) => {
3437
- const releaseRoot = join11(outputRoot, metadata.releaseId);
3438
- const destination = join11(releaseRoot, "App.ipa");
4124
+ const releaseRoot = join12(outputRoot, metadata.releaseId);
4125
+ const destination = join12(releaseRoot, "App.ipa");
3439
4126
  if (await pathExists3(releaseRoot)) {
3440
- const value = JSON.parse(await readFile6(join11(releaseRoot, "release.json"), "utf8"));
4127
+ const value = JSON.parse(await readFile6(join12(releaseRoot, "release.json"), "utf8"));
3441
4128
  if (!isRecord2(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
3442
4129
  throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
3443
4130
  }
@@ -3456,15 +4143,15 @@ var developmentTeamArgument = (value) => {
3456
4143
  releaseRoot
3457
4144
  };
3458
4145
  }
3459
- await mkdir4(dirname5(releaseRoot), { recursive: true });
3460
- 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-"));
3461
4148
  try {
3462
- await copyFile2(artifactPath, join11(staging, "App.ipa"));
4149
+ await copyFile2(artifactPath, join12(staging, "App.ipa"));
3463
4150
  const complete = {
3464
4151
  ...metadata,
3465
4152
  artifact: "App.ipa"
3466
4153
  };
3467
- await writeFile4(join11(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
4154
+ await writeFile4(join12(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
3468
4155
  `, { flag: "wx" });
3469
4156
  await rename4(staging, releaseRoot);
3470
4157
  return { artifactPath: destination, metadata: complete, releaseRoot };
@@ -3481,22 +4168,22 @@ var developmentTeamArgument = (value) => {
3481
4168
  const marketingVersion = options.config.iosVersion;
3482
4169
  if (!marketingVersion)
3483
4170
  throw new TypeError("iOS release builds require mobile.ios.version in absolutejs.config.ts.");
3484
- 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")));
3485
4172
  if (manifest.appId !== options.config.appId)
3486
4173
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
3487
- const nativeDirectory = join11(options.config.nativeProjectDirectory, "ios");
4174
+ const nativeDirectory = join12(options.config.nativeProjectDirectory, "ios");
3488
4175
  let buildNumber = requireBuildNumber(options.buildNumber);
3489
4176
  if (options.prepareBuildNumber) {
3490
4177
  const nativeFingerprint = await fingerprintAbsoluteIosNativeProject(nativeDirectory);
3491
4178
  const buildIdentity = createHash4("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}\x00${marketingVersion}`).digest("hex");
3492
4179
  buildNumber = requireBuildNumber(await options.prepareBuildNumber(buildIdentity));
3493
4180
  }
3494
- const stagingParent = resolve7(options.projectRoot, ".absolutejs/mobile");
4181
+ const stagingParent = resolve8(options.projectRoot, ".absolutejs/mobile");
3495
4182
  await mkdir4(stagingParent, { recursive: true });
3496
- const staging = await mkdtemp3(join11(stagingParent, ".ios-build-"));
3497
- const archivePath = join11(staging, "App.xcarchive");
3498
- const exportPath = join11(staging, "export");
3499
- 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");
3500
4187
  await mkdir4(exportPath, { recursive: true });
3501
4188
  await writeFile4(exportPlist, exportOptions());
3502
4189
  const run = options.run ?? defaultRun2;
@@ -3510,7 +4197,7 @@ var developmentTeamArgument = (value) => {
3510
4197
  const archiveExit = await run([
3511
4198
  "xcodebuild",
3512
4199
  "-workspace",
3513
- join11(nativeDirectory, "App", "App.xcworkspace"),
4200
+ join12(nativeDirectory, "App", "App.xcworkspace"),
3514
4201
  "-scheme",
3515
4202
  "App",
3516
4203
  "-configuration",
@@ -3524,7 +4211,7 @@ var developmentTeamArgument = (value) => {
3524
4211
  ], { cwd: nativeDirectory });
3525
4212
  if (archiveExit !== 0)
3526
4213
  throw new TypeError("Xcode failed to archive the iOS app.");
3527
- const archivedApp = await findByExtension(join11(archivePath, "Products", "Applications"), ".app");
4214
+ const archivedApp = await findByExtension(join12(archivePath, "Products", "Applications"), ".app");
3528
4215
  const capture = options.capture ?? defaultCapture2;
3529
4216
  const signed = archivedApp ? capture([
3530
4217
  "codesign",
@@ -3598,7 +4285,7 @@ import {
3598
4285
  writeFile as writeFile5
3599
4286
  } from "fs/promises";
3600
4287
  import { isIP as isIP2 } from "net";
3601
- 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";
3602
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) => {
3603
4290
  try {
3604
4291
  await access7(path);
@@ -3816,15 +4503,15 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
3816
4503
  const leftPro = left.name.includes("Pro") ? 1 : 0;
3817
4504
  return rightPro - leftPro;
3818
4505
  })[0], journalPaths2 = (projectRoot) => {
3819
- const root = join12(projectRoot, ".absolutejs", "mobile", "ios-dev-session");
4506
+ const root = join13(projectRoot, ".absolutejs", "mobile", "ios-dev-session");
3820
4507
  return {
3821
- configBackup: join12(root, "capacitor-config.backup"),
3822
- infoBackup: join12(root, "Info.plist.backup"),
3823
- 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"),
3824
4511
  root
3825
4512
  };
3826
- }, nativeCachePath2 = (projectRoot) => join12(projectRoot, ".absolutejs", "mobile", "ios-native-cache.json"), isInside2 = (root, path) => {
3827
- 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));
3828
4515
  return value === "" || !value.startsWith(`..${sep3}`) && value !== ".." && !isAbsolute3(value);
3829
4516
  }, parseJournal2 = (value) => {
3830
4517
  if (!isRecord3(value) || value.format !== DEV_JOURNAL_FORMAT2)
@@ -3880,8 +4567,8 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
3880
4567
  }, writeDevProjection = async (project, port, https, serverHost = "localhost") => {
3881
4568
  const paths = journalPaths2(project.projectRoot);
3882
4569
  await repairAbsoluteIosDevSession(project.projectRoot);
3883
- const nativeConfigPath = join12(project.nativeDirectory, "App", "App", "capacitor.config.json");
3884
- 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");
3885
4572
  const [configSource, infoSource] = await Promise.all([
3886
4573
  readFile7(nativeConfigPath, "utf8"),
3887
4574
  readFile7(infoPath, "utf8")
@@ -3937,7 +4624,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
3937
4624
  }, readNativeCache2 = (projectRoot) => readFile7(nativeCachePath2(projectRoot), "utf8").then((source) => parseNativeCache2(JSON.parse(source))).catch(() => null), writeNativeCache2 = async (projectRoot, cache) => {
3938
4625
  const destination = nativeCachePath2(projectRoot);
3939
4626
  const temporary = `${destination}.${process.pid}.${randomUUID3()}.tmp`;
3940
- await mkdir5(dirname6(destination), { recursive: true });
4627
+ await mkdir5(dirname7(destination), { recursive: true });
3941
4628
  try {
3942
4629
  await writeFile5(temporary, `${JSON.stringify(cache, null, "\t")}
3943
4630
  `, {
@@ -4042,12 +4729,12 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4042
4729
  ]);
4043
4730
  return result.exitCode === 0 && result.stdout.includes(project.config.appId);
4044
4731
  }, buildIosDebugApp = async (project, udid, fingerprint, run, signal) => {
4045
- 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));
4046
4733
  await mkdir5(derivedDataPath, { recursive: true });
4047
4734
  await requireSuccess2([
4048
4735
  project.xcodebuild,
4049
4736
  "-workspace",
4050
- join12(project.nativeDirectory, "App", "App.xcworkspace"),
4737
+ join13(project.nativeDirectory, "App", "App.xcworkspace"),
4051
4738
  "-scheme",
4052
4739
  "App",
4053
4740
  "-configuration",
@@ -4058,17 +4745,17 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4058
4745
  derivedDataPath,
4059
4746
  "build"
4060
4747
  ], "iOS simulator build", run, { cwd: project.nativeDirectory, signal });
4061
- const appPath = join12(derivedDataPath, "Build", "Products", "Debug-iphonesimulator", "App.app");
4748
+ const appPath = join13(derivedDataPath, "Build", "Products", "Debug-iphonesimulator", "App.app");
4062
4749
  if (!await pathExists4(appPath))
4063
4750
  throw new Error(`Xcode did not produce the simulator app at ${appPath}.`);
4064
4751
  return appPath;
4065
4752
  }, buildPhysicalIosDebugApp = async (project, identifier, run, signal) => {
4066
- 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));
4067
4754
  await mkdir5(derivedDataPath, { recursive: true });
4068
4755
  await requireSuccess2([
4069
4756
  project.xcodebuild,
4070
4757
  "-workspace",
4071
- join12(project.nativeDirectory, "App", "App.xcworkspace"),
4758
+ join13(project.nativeDirectory, "App", "App.xcworkspace"),
4072
4759
  "-scheme",
4073
4760
  "App",
4074
4761
  "-configuration",
@@ -4080,7 +4767,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4080
4767
  "-allowProvisioningUpdates",
4081
4768
  "build"
4082
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 });
4083
- const appPath = join12(derivedDataPath, "Build", "Products", "Debug-iphoneos", "App.app");
4770
+ const appPath = join13(derivedDataPath, "Build", "Products", "Debug-iphoneos", "App.app");
4084
4771
  if (!await pathExists4(appPath))
4085
4772
  throw new Error(`Xcode did not produce the physical-device app at ${appPath}.`);
4086
4773
  return appPath;
@@ -4191,7 +4878,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4191
4878
  if (detectAbsoluteMobileHost() !== "macos")
4192
4879
  throw new Error("iOS development requires macOS and Xcode.");
4193
4880
  const target = options.target ?? "simulator";
4194
- const projectRoot = resolve8(options.projectRoot);
4881
+ const projectRoot = resolve9(options.projectRoot);
4195
4882
  const checks = await inspectAbsoluteMobileToolchain({ host: "macos" });
4196
4883
  const failed = checks.filter((check) => check.platform === "ios" && !(target === "device" && check.id === "ios.runtime") && (check.status === "fail" || check.status === "warn"));
4197
4884
  if (failed.length > 0)
@@ -4200,16 +4887,16 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4200
4887
  const xcodebuild = checks.find((check) => check.id === "ios.xcodebuild")?.path;
4201
4888
  if (!xcrun || !xcodebuild)
4202
4889
  throw new Error("Xcode tools disappeared after readiness checks.");
4203
- const cap = join12(projectRoot, "node_modules", ".bin", "cap");
4890
+ const cap = join13(projectRoot, "node_modules", ".bin", "cap");
4204
4891
  if (!await pathExists4(cap))
4205
4892
  throw new Error("Capacitor CLI is not installed. Run absolute mobile init first.");
4206
4893
  await writeAbsoluteCapacitorConfig(config, { projectRoot });
4207
4894
  await mkdir5(config.bundleDirectory, { recursive: true });
4208
- const placeholder = join12(config.bundleDirectory, "index.html");
4895
+ const placeholder = join13(config.bundleDirectory, "index.html");
4209
4896
  if (!await pathExists4(placeholder))
4210
4897
  await writeFile5(placeholder, `<!doctype html><title>AbsoluteJS mobile development</title>
4211
4898
  `);
4212
- const nativeDirectory = join12(config.nativeProjectDirectory, "ios");
4899
+ const nativeDirectory = join13(config.nativeProjectDirectory, "ios");
4213
4900
  if (!await pathExists4(nativeDirectory)) {
4214
4901
  if (!options.createNativeProject)
4215
4902
  throw new Error("iOS native project has not been created.");
@@ -4434,10 +5121,10 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
4434
5121
  screenshot: async (destination) => {
4435
5122
  if (deviceIdentifier)
4436
5123
  throw new Error("Physical iOS screenshots are captured in Xcode Device Hub; the CLI never records a device screen automatically.");
4437
- const resolved = resolve8(project.projectRoot, destination);
5124
+ const resolved = resolve9(project.projectRoot, destination);
4438
5125
  if (!isInside2(project.projectRoot, resolved))
4439
5126
  throw new Error("iOS screenshot destination must remain inside the project.");
4440
- await mkdir5(dirname6(resolved), { recursive: true });
5127
+ await mkdir5(dirname7(resolved), { recursive: true });
4441
5128
  await requireSuccess2([
4442
5129
  project.xcrun,
4443
5130
  "simctl",
@@ -4496,15 +5183,15 @@ import { chmod, mkdir as mkdir6, readFile as readFile8, rename as rename6, write
4496
5183
  import { homedir as homedir4 } from "os";
4497
5184
  import { isIP as isIP3 } from "net";
4498
5185
  import {
4499
- dirname as dirname7,
5186
+ dirname as dirname8,
4500
5187
  isAbsolute as isAbsolute4,
4501
- join as join13,
5188
+ join as join14,
4502
5189
  posix,
4503
5190
  relative as relative6,
4504
5191
  resolve as resolvePath,
4505
5192
  sep as sep4
4506
5193
  } from "path";
4507
- 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 = () => ({
4508
5195
  format: PROFILE_FORMAT,
4509
5196
  profiles: {}
4510
5197
  }), loadStore = async (path = defaultProfilePath()) => {
@@ -4525,7 +5212,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4525
5212
  throw error;
4526
5213
  }
4527
5214
  }, saveStore = async (store, path = defaultProfilePath()) => {
4528
- await mkdir6(dirname7(path), { recursive: true });
5215
+ await mkdir6(dirname8(path), { recursive: true });
4529
5216
  const temporary = `${path}.${randomUUID4()}.tmp`;
4530
5217
  await writeFile6(temporary, `${JSON.stringify(store, null, 2)}
4531
5218
  `, {
@@ -4627,10 +5314,10 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4627
5314
  "/bin/sh -lc",
4628
5315
  shellQuote(script)
4629
5316
  ]);
4630
- const host = requireRemoteSuccess(result, "Remote Mac LAN address discovery").trim();
4631
- if (isIP3(host) === 0)
5317
+ const host2 = requireRemoteSuccess(result, "Remote Mac LAN address discovery").trim();
5318
+ if (isIP3(host2) === 0)
4632
5319
  throw new Error("The Remote Mac did not report a device-reachable LAN address.");
4633
- return host;
5320
+ return host2;
4634
5321
  }, listAbsoluteRemoteMacProfiles = async (profilePath) => {
4635
5322
  const store = await loadStore(profilePath);
4636
5323
  return {
@@ -4680,9 +5367,9 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4680
5367
  throw new TypeError("Remote Expo execution requires mobile.engine: expo.");
4681
5368
  return createAbsoluteRemoteIosDevProject(config, projectRoot, profile);
4682
5369
  }, createAbsoluteRemoteIosDevProject = (config, projectRoot, profile) => ({
4683
- cap: join13(resolvePath(projectRoot), "node_modules", ".bin", "cap"),
5370
+ cap: join14(resolvePath(projectRoot), "node_modules", ".bin", "cap"),
4684
5371
  config,
4685
- nativeDirectory: join13(config.nativeProjectDirectory, "ios"),
5372
+ nativeDirectory: join14(config.nativeProjectDirectory, "ios"),
4686
5373
  profile,
4687
5374
  projectRoot: resolvePath(projectRoot),
4688
5375
  remote: true,
@@ -4729,8 +5416,8 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4729
5416
  return { ...artifact, remotePath, uploaded: true };
4730
5417
  }, materializeAbsoluteRemoteMacAgent = async (projectRoot) => {
4731
5418
  const shippedCandidates = [
4732
- join13(import.meta.dir, "remoteMacAgentEntry.js"),
4733
- join13(import.meta.dir, "..", "mobile", "remoteMacAgentEntry.js")
5419
+ join14(import.meta.dir, "remoteMacAgentEntry.js"),
5420
+ join14(import.meta.dir, "..", "mobile", "remoteMacAgentEntry.js")
4734
5421
  ];
4735
5422
  let path;
4736
5423
  for (const candidate of shippedCandidates) {
@@ -4741,13 +5428,13 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4741
5428
  }
4742
5429
  if (!path) {
4743
5430
  const sourceCandidates = [
4744
- join13(import.meta.dir, "remoteMacAgentEntry.ts"),
4745
- join13(import.meta.dir, "..", "..", "src", "mobile", "remoteMacAgentEntry.ts")
5431
+ join14(import.meta.dir, "remoteMacAgentEntry.ts"),
5432
+ join14(import.meta.dir, "..", "..", "src", "mobile", "remoteMacAgentEntry.ts")
4746
5433
  ];
4747
5434
  const source = await sourceCandidates.reduce(async (found, candidate) => await found ?? (await Bun.file(candidate).exists() ? candidate : undefined), Promise.resolve(undefined));
4748
5435
  if (!source)
4749
5436
  throw new Error("The AbsoluteJS installation does not contain its remote Mac agent artifact.");
4750
- const outdir = join13(resolvePath(projectRoot), ".absolutejs", "mobile", "remote-agent");
5437
+ const outdir = join14(resolvePath(projectRoot), ".absolutejs", "mobile", "remote-agent");
4751
5438
  await mkdir6(outdir, { recursive: true });
4752
5439
  const result = await Bun.build({
4753
5440
  entrypoints: [source],
@@ -4757,7 +5444,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4757
5444
  });
4758
5445
  if (!result.success)
4759
5446
  throw new AggregateError(result.logs, "Failed to build the AbsoluteJS remote Mac agent.");
4760
- path = join13(outdir, "remoteMacAgentEntry.js");
5447
+ path = join14(outdir, "remoteMacAgentEntry.js");
4761
5448
  }
4762
5449
  const bytes = await Bun.file(path).arrayBuffer();
4763
5450
  const sha256 = createHash6("sha256").update(new Uint8Array(bytes)).digest("hex");
@@ -4978,8 +5665,8 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
4978
5665
  const pending = new Map;
4979
5666
  let resolveReady;
4980
5667
  let rejectReady;
4981
- const readyPromise = new Promise((resolve9, reject) => {
4982
- resolveReady = resolve9;
5668
+ const readyPromise = new Promise((resolve10, reject) => {
5669
+ resolveReady = resolve10;
4983
5670
  rejectReady = reject;
4984
5671
  });
4985
5672
  const handleEvent = (event) => {
@@ -5080,7 +5767,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
5080
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.`);
5081
5768
  const request = (commandName) => {
5082
5769
  const id = randomUUID4();
5083
- 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 }));
5084
5771
  process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION })}
5085
5772
  `);
5086
5773
  const flush = async () => {
@@ -5100,7 +5787,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
5100
5787
  if (closed)
5101
5788
  return;
5102
5789
  closed = true;
5103
- 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));
5104
5791
  await Promise.race([
5105
5792
  request("close").catch(() => {
5106
5793
  return;
@@ -5151,7 +5838,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
5151
5838
  const targetRelative = relative6(options.project.projectRoot, target);
5152
5839
  if (targetRelative.startsWith("..") || isAbsolute4(targetRelative))
5153
5840
  throw new Error("iOS screenshot must remain inside the project.");
5154
- await mkdir6(dirname7(target), { recursive: true });
5841
+ await mkdir6(dirname8(target), { recursive: true });
5155
5842
  await writeFile6(target, Buffer.from(result.data, "base64"));
5156
5843
  return target;
5157
5844
  },
@@ -5178,44 +5865,44 @@ var init_remoteMacProtocol = __esm(() => {
5178
5865
  // src/dev/devCert.ts
5179
5866
  var exports_devCert = {};
5180
5867
  __export(exports_devCert, {
5181
- setupMkcert: () => setupMkcert,
5182
- normalizeDevCertificateHosts: () => normalizeDevCertificateHosts,
5183
- loadDevCert: () => loadDevCert,
5184
- hasMkcert: () => hasMkcert,
5185
- hasCert: () => hasCert,
5868
+ ensureDevCert: () => ensureDevCert,
5186
5869
  getDevCertificateAuthorityPath: () => getDevCertificateAuthorityPath,
5187
- ensureDevCert: () => ensureDevCert
5870
+ hasCert: () => hasCert,
5871
+ hasMkcert: () => hasMkcert,
5872
+ loadDevCert: () => loadDevCert,
5873
+ normalizeDevCertificateHosts: () => normalizeDevCertificateHosts,
5874
+ setupMkcert: () => setupMkcert
5188
5875
  });
5189
5876
  import {
5190
5877
  copyFileSync,
5191
5878
  existsSync as existsSync4,
5192
5879
  mkdirSync as mkdirSync4,
5193
- readFileSync as readFileSync7,
5880
+ readFileSync as readFileSync8,
5194
5881
  rmSync
5195
5882
  } from "fs";
5196
5883
  import { X509Certificate as X509Certificate2 } from "crypto";
5197
5884
  import { isIP as isIP4 } from "net";
5198
5885
  import { platform as platform2 } from "os";
5199
- import { join as join14 } from "path";
5886
+ import { join as join15 } from "path";
5200
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 = []) => {
5201
5888
  const normalized = new Set(DEFAULT_CERTIFICATE_HOSTS);
5202
- for (const host of hosts) {
5203
- const value = host.trim().toLowerCase();
5889
+ for (const host2 of hosts) {
5890
+ const value = host2.trim().toLowerCase();
5204
5891
  if (!value || value === "0.0.0.0" || value === "::")
5205
5892
  continue;
5206
5893
  if (isIP4(value) === 0 && !CERTIFICATE_HOSTNAME_PATTERN.test(value)) {
5207
- throw new TypeError(`Invalid development certificate host: ${host}`);
5894
+ throw new TypeError(`Invalid development certificate host: ${host2}`);
5208
5895
  }
5209
5896
  normalized.add(value);
5210
5897
  }
5211
5898
  return [...normalized];
5212
5899
  }, certificateIsUsable = (hosts) => {
5213
5900
  try {
5214
- const certPem = readFileSync7(CERT_PATH, "utf-8");
5901
+ const certPem = readFileSync8(CERT_PATH, "utf-8");
5215
5902
  const certificate = new X509Certificate2(certPem);
5216
5903
  if (new Date(certificate.validTo).getTime() <= Date.now())
5217
5904
  return false;
5218
- 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);
5219
5906
  } catch {
5220
5907
  return false;
5221
5908
  }
@@ -5243,7 +5930,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5243
5930
  throw new Error(`mkcert failed: ${err}`);
5244
5931
  }
5245
5932
  }, generateSelfSigned = (hosts = []) => {
5246
- 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(",");
5247
5934
  const proc = Bun.spawnSync([
5248
5935
  "openssl",
5249
5936
  "req",
@@ -5296,8 +5983,8 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5296
5983
  return null;
5297
5984
  try {
5298
5985
  return {
5299
- cert: readFileSync7(paths.cert, "utf-8"),
5300
- key: readFileSync7(paths.key, "utf-8")
5986
+ cert: readFileSync8(paths.cert, "utf-8"),
5987
+ key: readFileSync8(paths.key, "utf-8")
5301
5988
  };
5302
5989
  } catch {
5303
5990
  return null;
@@ -5393,7 +6080,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5393
6080
  if (platform2() !== "linux")
5394
6081
  return false;
5395
6082
  try {
5396
- return /microsoft|wsl/i.test(readFileSync7("/proc/version", "utf-8"));
6083
+ return /microsoft|wsl/i.test(readFileSync8("/proc/version", "utf-8"));
5397
6084
  } catch {
5398
6085
  return false;
5399
6086
  }
@@ -5409,7 +6096,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5409
6096
  }
5410
6097
  }, mkcertCaRoot = () => runCapture(["mkcert", "-CAROOT"]), getDevCertificateAuthorityPath = () => {
5411
6098
  const caRoot = hasMkcert() ? mkcertCaRoot() : null;
5412
- const rootCertificate = caRoot ? join14(caRoot, "rootCA.pem") : null;
6099
+ const rootCertificate = caRoot ? join15(caRoot, "rootCA.pem") : null;
5413
6100
  if (rootCertificate && existsSync4(rootCertificate))
5414
6101
  return rootCertificate;
5415
6102
  if (certFilesExist())
@@ -5424,13 +6111,13 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5424
6111
  const caRoot = mkcertCaRoot();
5425
6112
  if (!caRoot)
5426
6113
  return false;
5427
- const rootCa = join14(caRoot, "rootCA.pem");
6114
+ const rootCa = join15(caRoot, "rootCA.pem");
5428
6115
  if (!existsSync4(rootCa))
5429
6116
  return false;
5430
6117
  const winTemp = windowsTempDir();
5431
6118
  if (!winTemp)
5432
6119
  return false;
5433
- const staged = join14(winTemp, "absolutejs-mkcert-rootCA.crt");
6120
+ const staged = join15(winTemp, "absolutejs-mkcert-rootCA.crt");
5434
6121
  try {
5435
6122
  copyFileSync(rootCa, staged);
5436
6123
  } catch {
@@ -5466,7 +6153,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5466
6153
  devLog("Trusted the local CA in the Windows store \u2014 Chrome/Edge on Windows now accept dev HTTPS");
5467
6154
  } else {
5468
6155
  const caRoot = mkcertCaRoot();
5469
- const hint = caRoot ? toWindowsPath(join14(caRoot, "rootCA.pem")) : null;
6156
+ const hint = caRoot ? toWindowsPath(join15(caRoot, "rootCA.pem")) : null;
5470
6157
  devWarn("Could not auto-trust the local CA on Windows; Windows browsers may warn.");
5471
6158
  if (hint) {
5472
6159
  console.log(` Run in PowerShell: Import-Certificate -FilePath "${hint}" -CertStoreLocation Cert:\\CurrentUser\\Root`);
@@ -5482,9 +6169,9 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
5482
6169
  return true;
5483
6170
  };
5484
6171
  var init_devCert = __esm(() => {
5485
- CERT_DIR = join14(process.cwd(), ".absolutejs");
5486
- CERT_PATH = join14(CERT_DIR, "cert.pem");
5487
- 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");
5488
6175
  DEFAULT_CERTIFICATE_HOSTS = ["localhost", "127.0.0.1", "::1"];
5489
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;
5490
6177
  });
@@ -5492,16 +6179,16 @@ var init_devCert = __esm(() => {
5492
6179
  // src/cli/scripts/eslintChunked.ts
5493
6180
  var exports_eslintChunked = {};
5494
6181
  __export(exports_eslintChunked, {
5495
- upstreamRef: () => upstreamRef,
5496
- ruleSummary: () => ruleSummary,
5497
- resolveLintTargets: () => resolveLintTargets,
5498
- parseChunkedArgs: () => parseChunkedArgs,
5499
- gitVisibleFiles: () => gitVisibleFiles,
6182
+ eslintChunked: () => eslintChunked,
5500
6183
  gitChangedFiles: () => gitChangedFiles,
5501
- eslintChunked: () => eslintChunked
6184
+ gitVisibleFiles: () => gitVisibleFiles,
6185
+ parseChunkedArgs: () => parseChunkedArgs,
6186
+ resolveLintTargets: () => resolveLintTargets,
6187
+ ruleSummary: () => ruleSummary,
6188
+ upstreamRef: () => upstreamRef
5502
6189
  });
5503
6190
  import { existsSync as existsSync6 } from "fs";
5504
- import { relative as relative7, resolve as resolve9 } from "path";
6191
+ import { relative as relative7, resolve as resolve10 } from "path";
5505
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(`
5506
6193
  `).map((line) => line.trimEnd()).filter(Boolean), applyChangedBase = (parsed, base) => {
5507
6194
  parsed.changedOnly = true;
@@ -5513,7 +6200,7 @@ var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAUL
5513
6200
  }, matchesAnyGlob = (file, globs) => globs.some((pattern) => new Bun.Glob(pattern).match(file)), resolveLintSet = (parsed, cwd) => {
5514
6201
  const visible = gitVisibleFiles(cwd);
5515
6202
  const matched = parsed.globs.length === 0 ? visible.filter((file) => LINTABLE_EXTENSIONS.test(file)) : visible.filter((file) => matchesAnyGlob(file, parsed.globs));
5516
- return matched.filter((file) => existsSync6(resolve9(cwd, file))).sort();
6203
+ return matched.filter((file) => existsSync6(resolve10(cwd, file))).sort();
5517
6204
  }, resolveLintTargets = (args, cwd = process.cwd()) => resolveLintSet(parseChunkedArgs(args), cwd), buildShardChunks = (files, shards, chunkSize) => {
5518
6205
  const shardFiles = Array.from({ length: shards }, () => []);
5519
6206
  for (const file of files)
@@ -5535,7 +6222,7 @@ var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAUL
5535
6222
  "content"
5536
6223
  ];
5537
6224
  const proc = Bun.spawn([
5538
- resolve9(cwd, "node_modules/.bin/eslint"),
6225
+ resolve10(cwd, "node_modules/.bin/eslint"),
5539
6226
  "--color",
5540
6227
  ...hasMaxWarnings ? [] : ["--max-warnings", "0"],
5541
6228
  ...cacheArgs,
@@ -5582,7 +6269,7 @@ var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAUL
5582
6269
  const fingerprint = createEslintCacheFingerprint(cwd);
5583
6270
  for (let shard = 0;shard < parsed.shards; shard++)
5584
6271
  prepareEslintCache({
5585
- cacheLocation: relative7(cwd, resolve9(cwd, `${cachePrefix}${shard}`)),
6272
+ cacheLocation: relative7(cwd, resolve10(cwd, `${cachePrefix}${shard}`)),
5586
6273
  cwd,
5587
6274
  fingerprint
5588
6275
  });
@@ -5623,7 +6310,7 @@ var DEFAULT_CHUNK_SIZE = 20, DEFAULT_SHARDS = 4, DEFAULT_CONCURRENCY = 2, DEFAUL
5623
6310
  const header = `eslint report \u2014 ${files.length} files, ${totalChunks} chunks, ${elapsed}s
5624
6311
  ${"=".repeat(SUMMARY_RULE_WIDTH)}
5625
6312
  `;
5626
- await Bun.write(resolve9(cwd, parsed.outFile), header + report + summary);
6313
+ await Bun.write(resolve10(cwd, parsed.outFile), header + report + summary);
5627
6314
  console.log(summary);
5628
6315
  console.log(`Full report written to ${parsed.outFile}`);
5629
6316
  if (failedChunks > 0) {
@@ -5712,12 +6399,12 @@ import { createHash as createHash7 } from "crypto";
5712
6399
  import {
5713
6400
  existsSync as existsSync7,
5714
6401
  mkdirSync as mkdirSync5,
5715
- readFileSync as readFileSync9,
6402
+ readFileSync as readFileSync10,
5716
6403
  renameSync,
5717
6404
  rmSync as rmSync3,
5718
6405
  writeFileSync as writeFileSync5
5719
6406
  } from "fs";
5720
- 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";
5721
6408
  var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION = "1", CACHE_FINGERPRINT_SUFFIX = ".fingerprint", flagValue = (args, flag) => {
5722
6409
  const assignment = args.find((arg) => arg.startsWith(`${flag}=`));
5723
6410
  if (assignment)
@@ -5741,20 +6428,20 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5741
6428
  return false;
5742
6429
  }, findEslintConfigPath = (cwd = process.cwd()) => {
5743
6430
  for (const name of CONFIG_CANDIDATES) {
5744
- const candidate = resolve10(cwd, name);
6431
+ const candidate = resolve11(cwd, name);
5745
6432
  if (existsSync7(candidate))
5746
6433
  return candidate;
5747
6434
  }
5748
6435
  return null;
5749
6436
  }, fingerprintLocation = (cacheLocation, cwd) => {
5750
- const absolute = resolve10(cwd, cacheLocation);
5751
- 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}`;
5752
6439
  }, addFileToFingerprint = (hash, path, label) => {
5753
6440
  if (!existsSync7(path))
5754
6441
  return;
5755
6442
  hash.update(label);
5756
6443
  hash.update("\x00");
5757
- hash.update(readFileSync9(path));
6444
+ hash.update(readFileSync10(path));
5758
6445
  hash.update("\x00");
5759
6446
  }, packageNameFor = (specifier) => {
5760
6447
  if (specifier.startsWith("@"))
@@ -5764,7 +6451,7 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5764
6451
  }, configPackageNames = (configPath2) => {
5765
6452
  if (!configPath2)
5766
6453
  return [];
5767
- const source = readFileSync9(configPath2, "utf-8");
6454
+ const source = readFileSync10(configPath2, "utf-8");
5768
6455
  const names = new Set;
5769
6456
  for (const match of source.matchAll(/(?:from\s+|import\s*(?:\(\s*)?|require\s*\(\s*)(['"])([^'".][^'"]*)\1/g)) {
5770
6457
  const [, , specifier] = match;
@@ -5783,11 +6470,11 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5783
6470
  return Object.keys(value);
5784
6471
  });
5785
6472
  }, lintDependencyNames = (cwd, configPath2) => {
5786
- const manifestPath = resolve10(cwd, "package.json");
6473
+ const manifestPath = resolve11(cwd, "package.json");
5787
6474
  if (!existsSync7(manifestPath))
5788
6475
  return configPackageNames(configPath2);
5789
6476
  try {
5790
- const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
6477
+ const manifest = JSON.parse(readFileSync10(manifestPath, "utf-8"));
5791
6478
  const lintPackages = manifestDependencyNames(manifest).filter((name) => /eslint|typescript/.test(name));
5792
6479
  return [
5793
6480
  ...new Set([...lintPackages, ...configPackageNames(configPath2)])
@@ -5798,10 +6485,10 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5798
6485
  }, findInstalledManifest = (cwd, dependency) => {
5799
6486
  let directory = cwd;
5800
6487
  while (true) {
5801
- const candidate = resolve10(directory, "node_modules", dependency, "package.json");
6488
+ const candidate = resolve11(directory, "node_modules", dependency, "package.json");
5802
6489
  if (existsSync7(candidate))
5803
6490
  return candidate;
5804
- const parent = dirname8(directory);
6491
+ const parent = dirname9(directory);
5805
6492
  if (parent === directory)
5806
6493
  return null;
5807
6494
  directory = parent;
@@ -5824,17 +6511,17 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5824
6511
  addFileToFingerprint(hash, configPath2, relative8(cwd, configPath2));
5825
6512
  return hash.digest("hex");
5826
6513
  }, writeFingerprint = (path, fingerprint) => {
5827
- mkdirSync5(dirname8(path), { recursive: true });
6514
+ mkdirSync5(dirname9(path), { recursive: true });
5828
6515
  const temporary = `${path}.${process.pid}.tmp`;
5829
6516
  writeFileSync5(temporary, `${fingerprint}
5830
6517
  `);
5831
6518
  renameSync(temporary, path);
5832
6519
  }, prepareEslintCache = (options) => {
5833
6520
  const cwd = options.cwd ?? process.cwd();
5834
- const cachePath = resolve10(cwd, options.cacheLocation);
6521
+ const cachePath = resolve11(cwd, options.cacheLocation);
5835
6522
  const metadataPath = fingerprintLocation(options.cacheLocation, cwd);
5836
6523
  const fingerprint = options.fingerprint ?? createEslintCacheFingerprint(cwd);
5837
- const prior = existsSync7(metadataPath) ? readFileSync9(metadataPath, "utf-8").trim() : null;
6524
+ const prior = existsSync7(metadataPath) ? readFileSync10(metadataPath, "utf-8").trim() : null;
5838
6525
  if (prior === fingerprint)
5839
6526
  return false;
5840
6527
  rmSync3(cachePath, { force: true, recursive: true });
@@ -5922,7 +6609,7 @@ var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache", CACHE_CONTRACT_VERSION
5922
6609
  return;
5923
6610
  let source;
5924
6611
  try {
5925
- source = readFileSync9(configPath2, "utf-8");
6612
+ source = readFileSync10(configPath2, "utf-8");
5926
6613
  } catch {
5927
6614
  return;
5928
6615
  }
@@ -5961,7 +6648,7 @@ Detected at: ${configPath2}${reset}`);
5961
6648
  return `${minutes}m ${seconds}s`;
5962
6649
  }, handleClearCache = (cacheLocation, cwd = process.cwd()) => {
5963
6650
  try {
5964
- const cachePath = resolve10(cwd, cacheLocation);
6651
+ const cachePath = resolve11(cwd, cacheLocation);
5965
6652
  const metadataPath = fingerprintLocation(cacheLocation, cwd);
5966
6653
  rmSync3(cachePath, { force: true, recursive: true });
5967
6654
  rmSync3(metadataPath, { force: true, recursive: true });
@@ -5990,7 +6677,7 @@ Detected at: ${configPath2}${reset}`);
5990
6677
  return;
5991
6678
  }
5992
6679
  if (args.includes("--chunked")) {
5993
- if (!existsSync7(resolve10("node_modules", ".bin", "eslint"))) {
6680
+ if (!existsSync7(resolve11("node_modules", ".bin", "eslint"))) {
5994
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");
5995
6682
  process.exit(1);
5996
6683
  }
@@ -5998,7 +6685,7 @@ Detected at: ${configPath2}${reset}`);
5998
6685
  await eslintChunked2(args);
5999
6686
  return;
6000
6687
  }
6001
- if (!existsSync7(resolve10("node_modules", ".bin", "eslint"))) {
6688
+ if (!existsSync7(resolve11("node_modules", ".bin", "eslint"))) {
6002
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");
6003
6690
  process.exit(1);
6004
6691
  }
@@ -6175,21 +6862,21 @@ var stripStringsAndComments = (source) => {
6175
6862
  };
6176
6863
 
6177
6864
  // src/core/islandManifest.ts
6178
- 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}`;
6179
6866
 
6180
6867
  // src/core/islands.ts
6181
- var isRecord4 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component) => {
6182
- if (!isIslandComponentDefinition(component))
6868
+ var isRecord4 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component2) => {
6869
+ if (!isIslandComponentDefinition(component2))
6183
6870
  return null;
6184
6871
  return {
6185
- export: component.export,
6186
- source: component.source
6872
+ export: component2.export,
6873
+ source: component2.source
6187
6874
  };
6188
6875
  }, isIslandComponentDefinition = (value) => isRecord4(value) && ("component" in value) && ("source" in value) && typeof value.source === "string";
6189
6876
  var init_islands = () => {};
6190
6877
 
6191
6878
  // src/build/islandEntries.ts
6192
- 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";
6193
6880
  import ts from "typescript";
6194
6881
  var frameworks, isRecord5 = (value) => typeof value === "object" && value !== null, resolveRegistryExport = (mod) => {
6195
6882
  if (isRecord5(mod.islandRegistry))
@@ -6201,7 +6888,7 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6201
6888
  if (sourcePath.startsWith("file://")) {
6202
6889
  return new URL(sourcePath).pathname;
6203
6890
  }
6204
- return resolve12(dirname9(registryPath), sourcePath);
6891
+ return resolve13(dirname10(registryPath), sourcePath);
6205
6892
  }, getObjectPropertyName = (name) => {
6206
6893
  if (ts.isIdentifier(name) || ts.isStringLiteral(name)) {
6207
6894
  return name.text;
@@ -6244,9 +6931,9 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6244
6931
  component: reference.source,
6245
6932
  export: reference.export,
6246
6933
  source: reference.source
6247
- }), addRegistryEntries = (frameworkNode, framework, imports, definitions, registry) => {
6248
- const frameworkRegistry = registry[framework] ?? {};
6249
- registry[framework] = frameworkRegistry;
6934
+ }), addRegistryEntries = (frameworkNode, framework, imports, definitions, registry2) => {
6935
+ const frameworkRegistry = registry2[framework] ?? {};
6936
+ registry2[framework] = frameworkRegistry;
6250
6937
  for (const property of frameworkNode.properties) {
6251
6938
  if (!ts.isPropertyAssignment(property) && !ts.isShorthandPropertyAssignment(property))
6252
6939
  continue;
@@ -6266,7 +6953,7 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6266
6953
  framework
6267
6954
  });
6268
6955
  }
6269
- }, processDefineIslandRegistry = (node, imports, definitions, registry) => {
6956
+ }, processDefineIslandRegistry = (node, imports, definitions, registry2) => {
6270
6957
  const [firstArg] = node.arguments;
6271
6958
  if (!firstArg || !ts.isObjectLiteralExpression(firstArg))
6272
6959
  return;
@@ -6287,13 +6974,13 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6287
6974
  continue;
6288
6975
  if (!ts.isObjectLiteralExpression(property.initializer))
6289
6976
  continue;
6290
- addRegistryEntries(property.initializer, framework, imports, definitions, registry);
6977
+ addRegistryEntries(property.initializer, framework, imports, definitions, registry2);
6291
6978
  }
6292
- }, walkRegistryNode = (node, imports, registryFactoryNames, registryNamespaceNames, definitions, registry) => {
6979
+ }, walkRegistryNode = (node, imports, registryFactoryNames, registryNamespaceNames, definitions, registry2) => {
6293
6980
  if (ts.isCallExpression(node) && isDefineIslandRegistryCall(node.expression, registryFactoryNames, registryNamespaceNames)) {
6294
- processDefineIslandRegistry(node, imports, definitions, registry);
6981
+ processDefineIslandRegistry(node, imports, definitions, registry2);
6295
6982
  }
6296
- ts.forEachChild(node, (child) => walkRegistryNode(child, imports, registryFactoryNames, registryNamespaceNames, definitions, registry));
6983
+ ts.forEachChild(node, (child) => walkRegistryNode(child, imports, registryFactoryNames, registryNamespaceNames, definitions, registry2));
6297
6984
  }, isDefineIslandRegistryCall = (expression, registryFactoryNames, registryNamespaceNames) => {
6298
6985
  if (ts.isIdentifier(expression)) {
6299
6986
  return registryFactoryNames.has(expression.text);
@@ -6331,34 +7018,34 @@ var frameworks, isRecord5 = (value) => typeof value === "object" && value !== nu
6331
7018
  const registryFactoryNames = new Set(["defineIslandRegistry"]);
6332
7019
  const registryNamespaceNames = new Set;
6333
7020
  const definitions = [];
6334
- const registry = {};
7021
+ const registry2 = {};
6335
7022
  collectImportDeclarations(sourceFile, registryPath, imports, registryFactoryNames, registryNamespaceNames);
6336
- walkRegistryNode(sourceFile, imports, registryFactoryNames, registryNamespaceNames, definitions, registry);
7023
+ walkRegistryNode(sourceFile, imports, registryFactoryNames, registryNamespaceNames, definitions, registry2);
6337
7024
  return {
6338
7025
  definitions,
6339
7026
  hasNamedExport: hasIslandRegistryNamedExport(sourceFile),
6340
- registry
7027
+ registry: registry2
6341
7028
  };
6342
7029
  }, loadDynamicIslandRegistryBuildInfo = async (resolvedRegistryPath) => {
6343
7030
  const registryModule = await import(resolvedRegistryPath);
6344
- const registry = resolveRegistryExport(registryModule);
7031
+ const registry2 = resolveRegistryExport(registryModule);
6345
7032
  const definitions = frameworks.flatMap((framework) => {
6346
- const frameworkRegistry = registry[framework];
7033
+ const frameworkRegistry = registry2[framework];
6347
7034
  if (!isRecord5(frameworkRegistry))
6348
7035
  return [];
6349
- return Object.entries(frameworkRegistry).map(([component, value]) => ({
7036
+ return Object.entries(frameworkRegistry).map(([component2, value]) => ({
6350
7037
  buildReference: getIslandBuildReference(value),
6351
- component,
7038
+ component: component2,
6352
7039
  framework
6353
7040
  }));
6354
7041
  });
6355
7042
  return {
6356
7043
  definitions,
6357
7044
  hasNamedExport: isRecord5(registryModule.islandRegistry),
6358
- registry
7045
+ registry: registry2
6359
7046
  };
6360
7047
  }, loadIslandRegistryBuildInfo = async (registryPath) => {
6361
- const resolvedRegistryPath = resolve12(registryPath);
7048
+ const resolvedRegistryPath = resolve13(registryPath);
6362
7049
  const registrySource = Bun.file(resolvedRegistryPath);
6363
7050
  const registrySourceText = await registrySource.text();
6364
7051
  const parsedInfo = parseIslandRegistryBuildInfo(registrySourceText, resolvedRegistryPath);
@@ -6892,7 +7579,7 @@ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-
6892
7579
  // src/mobile/buildRelease.ts
6893
7580
  import { createHash as createHash9 } from "crypto";
6894
7581
  import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
6895
- 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";
6896
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) => {
6897
7584
  if (path.endsWith("/htmx.min.js"))
6898
7585
  return match;
@@ -6900,12 +7587,12 @@ var sha256 = (bytes) => createHash9("sha256").update(bytes).digest("hex"), STATI
6900
7587
  const builtPath = manifest[key];
6901
7588
  return builtPath ? `${prefix}${builtPath}${suffix}` : match;
6902
7589
  }), readPageMetadata = (route) => parseAbsoluteMobileBuildPageMetadata(route.hooks?.detail?.[ABSOLUTE_MOBILE_ROUTE_DETAIL]), resolveAssetPath = (buildDirectory, assetPath) => {
6903
- const resolvedBuildDirectory = resolve13(buildDirectory);
6904
- const resolvedAsset = resolve13(assetPath);
7590
+ const resolvedBuildDirectory = resolve14(buildDirectory);
7591
+ const resolvedAsset = resolve14(assetPath);
6905
7592
  if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
6906
7593
  return resolvedAsset;
6907
7594
  }
6908
- return join18(buildDirectory, assetPath.replace(/^\/+/, ""));
7595
+ return join19(buildDirectory, assetPath.replace(/^\/+/, ""));
6909
7596
  }, pageFor = async (metadata, manifest, buildDirectory) => {
6910
7597
  const assetPath = manifest[metadata.bundleKey];
6911
7598
  if (!assetPath) {
@@ -6916,8 +7603,8 @@ var sha256 = (bytes) => createHash9("sha256").update(bytes).digest("hex"), STATI
6916
7603
  const source = await readFile9(resolvedAssetPath, "utf8");
6917
7604
  const rewritten = rewriteStaticScriptPaths(source, manifest);
6918
7605
  const documentHash = sha256(new TextEncoder().encode(rewritten));
6919
- resolvedAssetPath = join18(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
6920
- await mkdir8(dirname10(resolvedAssetPath), { recursive: true });
7606
+ resolvedAssetPath = join19(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
7607
+ await mkdir8(dirname11(resolvedAssetPath), { recursive: true });
6921
7608
  await writeFile7(resolvedAssetPath, rewritten);
6922
7609
  }
6923
7610
  const pageAssetKey = metadata.bundleKey.replace(/Index$/u, "");
@@ -6930,8 +7617,8 @@ var sha256 = (bytes) => createHash9("sha256").update(bytes).digest("hex"), STATI
6930
7617
  readFile9(resolvedAssetPath),
6931
7618
  resolvedStylePath ? readFile9(resolvedStylePath) : undefined
6932
7619
  ]);
6933
- const bundlePath = `/${relative10(resolve13(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
6934
- 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;
6935
7622
  return {
6936
7623
  bundleHash: sha256(bytes),
6937
7624
  bundlePath,
@@ -7094,7 +7781,7 @@ var init_pageProtocol = __esm(() => {
7094
7781
 
7095
7782
  // src/mobile/client.ts
7096
7783
  var frameworks5, upgradeReasons;
7097
- var init_client = __esm(() => {
7784
+ var init_client2 = __esm(() => {
7098
7785
  init_pageProtocol();
7099
7786
  frameworks5 = new Set([
7100
7787
  "angular",
@@ -7116,7 +7803,7 @@ var init_client = __esm(() => {
7116
7803
  // src/mobile/transport.ts
7117
7804
  var ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT = 1;
7118
7805
  var init_transport = __esm(() => {
7119
- init_client();
7806
+ init_client2();
7120
7807
  init_pageProtocol();
7121
7808
  init_routeMatcher();
7122
7809
  });
@@ -7133,29 +7820,39 @@ import {
7133
7820
  writeFile as writeFile8
7134
7821
  } from "fs/promises";
7135
7822
  import { existsSync as existsSync9 } from "fs";
7136
- 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";
7137
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 = () => {
7138
- 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);
7139
7826
  if (candidate)
7140
7827
  return candidate;
7141
7828
  throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
7142
7829
  }, shellAuthModule = () => {
7143
- 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);
7144
7831
  if (candidate)
7145
7832
  return candidate;
7146
7833
  throw new TypeError("AbsoluteJS mobile auth shell module is missing.");
7834
+ }, shellExpoAuthModule = () => {
7835
+ const candidate = ["js", "ts"].map((extension) => join20(import.meta.dir, `shellExpoAuth.${extension}`)).find(existsSync9);
7836
+ if (candidate)
7837
+ return candidate;
7838
+ throw new TypeError("AbsoluteJS Expo auth bridge module is missing.");
7147
7839
  }, shellSyncModule = () => {
7148
- 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);
7149
7841
  if (candidate)
7150
7842
  return candidate;
7151
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.");
7152
7849
  }, shellPushModule = () => {
7153
- 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);
7154
7851
  if (candidate)
7155
7852
  return candidate;
7156
7853
  throw new TypeError("AbsoluteJS mobile push shell module is missing.");
7157
7854
  }, shellExpoDevicesModule = () => {
7158
- 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);
7159
7856
  if (candidate)
7160
7857
  return candidate;
7161
7858
  throw new TypeError("AbsoluteJS Expo device bridge module is missing.");
@@ -7186,8 +7883,8 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
7186
7883
  </body>
7187
7884
  </html>
7188
7885
  `, sourceAssetPath = (buildDirectory, bundlePath) => {
7189
- const root = resolve14(buildDirectory);
7190
- const asset = resolve14(root, bundlePath.replace(/^\/+/, ""));
7886
+ const root = resolve15(buildDirectory);
7887
+ const asset = resolve15(root, bundlePath.replace(/^\/+/, ""));
7191
7888
  if (!asset.startsWith(`${root}/`)) {
7192
7889
  throw new TypeError("Mobile page bundle escaped the build directory.");
7193
7890
  }
@@ -7202,25 +7899,27 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
7202
7899
  const segments = specifier.split("/");
7203
7900
  const packageName = specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? "";
7204
7901
  const subpath = specifier.slice(packageName.length);
7205
- const packageDirectory = join19(resolve14(projectRoot), "node_modules", packageName);
7206
- 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"));
7207
7904
  const exports = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "exports") : undefined;
7208
7905
  const entry = typeof exports === "object" && exports !== null ? Reflect.get(exports, subpath ? `.${subpath}` : ".") : undefined;
7209
7906
  const target = importEntryTarget(entry);
7210
7907
  if (typeof target !== "string" || !target.startsWith("./"))
7211
7908
  throw new TypeError(`${specifier} does not publish an import entry.`);
7212
- const resolved = resolve14(packageDirectory, target);
7213
- if (!resolved.startsWith(`${resolve14(packageDirectory)}/`))
7909
+ const resolved = resolve15(packageDirectory, target);
7910
+ if (!resolved.startsWith(`${resolve15(packageDirectory)}/`))
7214
7911
  throw new TypeError(`${specifier} has an unsafe import entry.`);
7215
7912
  return resolved;
7216
7913
  }, buildShellBootstrap = async (staging, auth, sync, storagePrefix, engine, deviceCapabilities, projectRoot) => {
7217
7914
  const capacitor = engine !== "expo";
7218
7915
  const shellCapabilities = capacitor ? deviceCapabilities.capabilities : [];
7219
7916
  const modulePath = shellBootstrapModule();
7220
- const authImport = auth ? `import { createAbsoluteMobileShellAuth } from ${JSON.stringify(shellAuthModule())};
7917
+ const authFactory = capacitor ? "createAbsoluteMobileShellAuth" : "createAbsoluteExpoShellAuth";
7918
+ const syncFactory = capacitor ? "installAbsoluteMobileShellSync" : "installAbsoluteExpoShellSync";
7919
+ const authImport = auth ? `import { ${authFactory} } from ${JSON.stringify(capacitor ? shellAuthModule() : shellExpoAuthModule())};
7221
7920
  ` : "";
7222
- const options = auth ? `{ createAuth: createAbsoluteMobileShellAuth${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }` : "";
7223
- 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())};
7224
7923
  ` : "";
7225
7924
  const pushIndex = shellCapabilities.indexOf("pushNotifications");
7226
7925
  const push = pushIndex !== -1;
@@ -7237,15 +7936,15 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
7237
7936
  const absoluteMobilePushCapability = absoluteDeviceCapability${pushIndex}(absoluteMobilePush.capabilityOptions);
7238
7937
  ` : "";
7239
7938
  const capabilityOptions = shellCapabilities.map((name, index) => `${JSON.stringify(name)}: ${name === "pushNotifications" ? "absoluteMobilePushCapability" : `absoluteDeviceCapability${index}()`}`).join(", ");
7240
- const entryPath = join19(staging, ".absolute-mobile-entry.ts");
7939
+ const entryPath = join20(staging, ".absolute-mobile-entry.ts");
7241
7940
  const baseAdapterModule = capacitor ? await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor") : shellExpoDevicesModule();
7242
7941
  const adapterImport = capacitor ? `import { installCapacitorDeviceAdapterIfNative } from ${JSON.stringify(baseAdapterModule)};` : `import { createAbsoluteExpoBridgeFetch, installAbsoluteExpoWebDeviceAdapter } from ${JSON.stringify(baseAdapterModule)};`;
7243
7942
  const adapterInstall = capacitor ? `installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });` : "installAbsoluteExpoWebDeviceAdapter();";
7244
- let shellOptions = "{ createFetch: createAbsoluteExpoBridgeFetch }";
7943
+ let shellOptions = auth ? options : "{ createFetch: createAbsoluteExpoBridgeFetch }";
7245
7944
  if (capacitor)
7246
7945
  shellOptions = options;
7247
7946
  if (push) {
7248
- shellOptions = `{ createAuth: (config, options) => createAbsoluteMobileShellAuth(config, options), beforeSignOut: absoluteMobilePush.beforeSignOut, connectPush: (auth) => absoluteMobilePush.connect(auth, absoluteMobilePushCapability)${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }`;
7947
+ shellOptions = `{ createAuth: (config, options) => createAbsoluteMobileShellAuth(config, options), beforeSignOut: absoluteMobilePush.beforeSignOut, connectPush: (auth) => absoluteMobilePush.connect(auth, absoluteMobilePushCapability)${sync ? `, installSync: ${syncFactory}` : ""} }`;
7249
7948
  }
7250
7949
  await writeFile8(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
7251
7950
  ${adapterImport}
@@ -7256,751 +7955,329 @@ void startAbsoluteMobileShell(${shellOptions});
7256
7955
  const build = await Bun.build({
7257
7956
  entrypoints: [entryPath],
7258
7957
  minify: true,
7259
- outdir: staging,
7260
- target: "browser"
7261
- });
7262
- if (!build.success || build.outputs.length !== 1) {
7263
- throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
7264
- }
7265
- await rename7(build.outputs[0]?.path ?? "", join19(staging, BOOTSTRAP_FILE));
7266
- await rm6(entryPath, { force: true });
7267
- }, removePreviousBundle = async (backup, moved) => {
7268
- if (!moved)
7269
- return;
7270
- await rm6(backup, { force: true, recursive: true });
7271
- }, restorePreviousBundle = async (backup, destination, moved) => {
7272
- if (!moved)
7273
- return;
7274
- await rename7(backup, destination);
7275
- }, installBundle = async (staging, destination) => {
7276
- const backup = `${destination}.previous-${crypto.randomUUID()}`;
7277
- let movedPrevious = false;
7278
- try {
7279
- await rename7(destination, backup);
7280
- movedPrevious = true;
7281
- } catch (error) {
7282
- if (!errorHasCode(error, "ENOENT"))
7283
- throw error;
7284
- }
7285
- try {
7286
- await rename7(staging, destination);
7287
- await removePreviousBundle(backup, movedPrevious);
7288
- } catch (error) {
7289
- await restorePreviousBundle(backup, destination, movedPrevious);
7290
- throw error;
7291
- }
7292
- }, copyClientPage = async (page, buildDirectory, staging, copiedDependencies) => {
7293
- if (!CAPACITOR_CLIENT_FRAMEWORKS.has(page.framework)) {
7294
- throw new TypeError(`Capacitor client rendering does not yet support ${page.framework} page ${page.pageId}.`);
7295
- }
7296
- const extension = extname4(page.bundlePath) || ".js";
7297
- const localBundlePath = `./pages/${page.bundleHash}${extension}`;
7298
- const source = sourceAssetPath(buildDirectory, page.bundlePath);
7299
- await copyFile4(source, join19(staging, localBundlePath));
7300
- await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
7301
- let localStylePath;
7302
- if (page.styleBundlePath && page.styleBundleHash) {
7303
- const styleExtension = extname4(page.styleBundlePath) || ".css";
7304
- localStylePath = `./styles/${page.styleBundleHash}${styleExtension}`;
7305
- const styleSource = sourceAssetPath(buildDirectory, page.styleBundlePath);
7306
- await mkdir9(dirname11(join19(staging, localStylePath)), {
7307
- recursive: true
7308
- });
7309
- await copyFile4(styleSource, join19(staging, localStylePath));
7310
- await copyAbsoluteClientDependencies(styleSource, buildDirectory, staging, copiedDependencies);
7311
- }
7312
- return {
7313
- ...page,
7314
- localBundlePath,
7315
- ...localStylePath ? { localStylePath } : {}
7316
- };
7317
- }, absoluteClientImports = async (sourcePath, buildDirectory) => {
7318
- const source = await readFile10(sourcePath, "utf8");
7319
- const extension = extname4(sourcePath).toLowerCase();
7320
- let scriptLoader;
7321
- if (extension === ".tsx")
7322
- scriptLoader = "tsx";
7323
- else if (extension === ".ts")
7324
- scriptLoader = "ts";
7325
- else if (extension === ".jsx")
7326
- scriptLoader = "jsx";
7327
- else if ([".js", ".mjs", ".cjs"].includes(extension))
7328
- scriptLoader = "js";
7329
- const scriptImports = scriptLoader ? new Bun.Transpiler({ loader: scriptLoader }).scanImports(source).map(({ path }) => path) : [];
7330
- const cssImports = extension === ".css" ? [...source.matchAll(CLIENT_CSS_DEPENDENCY_PATTERN)].flatMap((match) => match[1] ?? []) : [];
7331
- const markupImports = extension === ".html" ? [...source.matchAll(CLIENT_MARKUP_DEPENDENCY_PATTERN)].flatMap((match) => match[1] ?? []) : [];
7332
- return [...scriptImports, ...cssImports, ...markupImports].flatMap((specifier) => {
7333
- if (!specifier)
7334
- return [];
7335
- if (!specifier.startsWith("/") && !specifier.startsWith("./") && !specifier.startsWith("../")) {
7336
- return [];
7337
- }
7338
- const clean = specifier.split(/[?#]/u, 1)[0] ?? specifier;
7339
- if (clean.startsWith("/"))
7340
- return [clean];
7341
- const resolved = resolve14(dirname11(sourcePath), clean);
7342
- const root = resolve14(buildDirectory);
7343
- const relativePath = relative11(root, resolved).replaceAll("\\", "/");
7344
- if (relativePath === ".." || relativePath.startsWith("../")) {
7345
- throw new TypeError(`Mobile client dependency escaped the build directory: ${specifier}`);
7346
- }
7347
- return [`/${relativePath}`];
7348
- });
7349
- }, copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, copied) => {
7350
- if (copied.has(specifier))
7351
- return;
7352
- copied.add(specifier);
7353
- const source = sourceAssetPath(buildDirectory, specifier);
7354
- const destination = join19(staging, specifier.replace(/^\/+/, ""));
7355
- await mkdir9(dirname11(destination), { recursive: true });
7356
- await copyFile4(source, destination);
7357
- await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
7358
- }, copyAbsoluteClientDependencies = async (sourcePath, buildDirectory, staging, copied) => {
7359
- const dependencies = await absoluteClientImports(sourcePath, buildDirectory);
7360
- await Promise.all(dependencies.map((specifier) => copyAbsoluteClientDependency(specifier, buildDirectory, staging, copied)));
7361
- }, materializeAbsoluteCapacitorWebBundle = async (options) => {
7362
- if (!resolveAbsoluteMobileRoute(options.artifact.routes, new URL(options.config.entry, "https://absolute.invalid").pathname)) {
7363
- throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
7364
- }
7365
- const destination = options.config.bundleDirectory;
7366
- await mkdir9(dirname11(destination), { recursive: true });
7367
- const staging = await mkdtemp4(join19(dirname11(destination), `.${basename8(destination)}.stage-`));
7368
- try {
7369
- const pageDirectory = join19(staging, "pages");
7370
- await mkdir9(pageDirectory, { recursive: true });
7371
- await Promise.all(CLIENT_ASSET_DIRECTORIES.map((directory) => ({
7372
- destination: join19(staging, directory),
7373
- source: join19(options.buildDirectory, directory)
7374
- })).filter(({ source }) => existsSync9(source)).map(({ destination: assetDestination, source }) => cp3(source, assetDestination, { recursive: true })));
7375
- const copiedDependencies = new Set;
7376
- const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
7377
- const manifest = {
7378
- appBuild: options.artifact.appBuild,
7379
- ...options.auth ? { auth: options.auth } : {},
7380
- appId: options.config.appId,
7381
- appName: options.config.appName,
7382
- deepLinkHosts: options.config.deepLinkHosts,
7383
- deepLinkScheme: options.config.deepLinkScheme,
7384
- deviceCapabilities: options.deviceCapabilities.capabilities,
7385
- entry: options.config.entry,
7386
- format: ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
7387
- pages,
7388
- productionOrigin: options.config.productionOrigin,
7389
- routes: options.artifact.routes,
7390
- runtime: options.artifact.runtime,
7391
- ...options.sync ? {
7392
- sync: {
7393
- background: {
7394
- endpoint: new URL("/__absolute/sync/background", options.config.productionOrigin).href,
7395
- intervalMinutes: 15
7396
- },
7397
- socketTickets: true,
7398
- storageSchema: options.syncSchema ?? {
7399
- components: [
7400
- { id: "@absolutejs/app", version: 1 }
7401
- ]
7402
- }
7403
- }
7404
- } : {}
7405
- };
7406
- await Promise.all([
7407
- writeFile8(join19(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
7408
- `),
7409
- writeFile8(join19(staging, INDEX_FILE), indexHtml(options.config.appName, options.config.productionOrigin)),
7410
- 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)
7411
- ]);
7412
- await installBundle(staging, destination);
7413
- return manifest;
7414
- } catch (error) {
7415
- await rm6(staging, { force: true, recursive: true });
7416
- throw error;
7417
- }
7418
- };
7419
- var init_capacitorBundle = __esm(() => {
7420
- init_routeMatcher();
7421
- init_transport();
7422
- CLIENT_CSS_DEPENDENCY_PATTERN = /(?:@import\s+(?:url\(\s*)?|url\(\s*)["']?((?:\/|\.\.\/|\.\/)[^"')\s]+)["']?\s*\)?/gu;
7423
- CLIENT_MARKUP_DEPENDENCY_PATTERN = /<(?:script\b[^>]*\bsrc|link\b[^>]*\bhref|img\b[^>]*\bsrc|source\b[^>]*\bsrcset)\s*=\s*["']((?:\/|\.\.\/|\.\/)[^"',\s]+)/giu;
7424
- CAPACITOR_CLIENT_FRAMEWORKS = new Set([
7425
- "angular",
7426
- "html",
7427
- "htmx",
7428
- "react",
7429
- "svelte",
7430
- "vue"
7431
- ]);
7432
- CLIENT_ASSET_DIRECTORIES = ["assets", "html", "htmx", "indexes"];
7433
- });
7434
-
7435
- // src/mobile/artifactStore.ts
7436
- import { createHash as createHash10 } from "crypto";
7437
- var DEFAULT_MAX_PRODUCER_BYTES = 134217728, SHA_2562 = "sha256", hashBlob = async (blob) => {
7438
- const bytes = new Uint8Array(await blob.arrayBuffer());
7439
- return createHash10(SHA_2562).update(bytes).digest("hex");
7440
- }, verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes = DEFAULT_MAX_PRODUCER_BYTES) => {
7441
- const { artifact, producer } = release;
7442
- if (producer.size !== artifact.producer.bytes || producer.size > maxProducerBytes) {
7443
- throw new TypeError("Mobile compatibility producer size does not match its artifact.");
7444
- }
7445
- if (await hashBlob(producer) !== artifact.producer.bundleHash) {
7446
- throw new TypeError("Mobile compatibility producer hash does not match its artifact.");
7447
- }
7448
- return release;
7449
- };
7450
- var init_artifactStore = __esm(() => {
7451
- init_releaseArtifact();
7452
- });
7453
-
7454
- // src/mobile/materializedBundle.ts
7455
- import { createHash as createHash11 } from "crypto";
7456
- import {
7457
- access as access8,
7458
- mkdir as mkdir10,
7459
- mkdtemp as mkdtemp5,
7460
- readFile as readFile11,
7461
- rename as rename8,
7462
- rm as rm7,
7463
- writeFile as writeFile9
7464
- } from "fs/promises";
7465
- import { dirname as dirname12, join as join20, resolve as resolvePath3 } from "path";
7466
- 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) => {
7467
- const identity = JSON.stringify({
7468
- currentReleaseId,
7469
- releases: releases.map(({ releaseId }) => releaseId)
7470
- });
7471
- return `amb_${createHash11("sha256").update(identity).digest("hex")}`;
7472
- }, parseBundleIndex = (value) => {
7473
- 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)) {
7474
- throw new TypeError("Invalid materialized mobile compatibility bundle.");
7475
- }
7476
- const releases = value.releases.map(parseAbsoluteMobileCompatibilityArtifact);
7477
- const retained = retainAbsoluteMobileCompatibilityArtifacts(releases);
7478
- if (retained.length !== releases.length || retained.some(({ releaseId }, index) => releaseId !== releases[index]?.releaseId)) {
7479
- throw new TypeError("Materialized mobile bundle releases are not a retained generation window.");
7480
- }
7481
- const expectedBundleId = bundleIdFor(value.currentReleaseId, releases);
7482
- if (value.bundleId !== expectedBundleId) {
7483
- throw new TypeError("Materialized mobile bundle integrity failed.");
7484
- }
7485
- if (releases[0]?.releaseId !== value.currentReleaseId) {
7486
- throw new TypeError("Materialized mobile bundle current release is not its newest generation.");
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.");
7487
7963
  }
7488
- return {
7489
- bundleId: value.bundleId,
7490
- currentReleaseId: value.currentReleaseId,
7491
- format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
7492
- releases
7493
- };
7494
- }, writeRelease = async (root, release) => {
7495
- const directory = join20(root, release.artifact.releaseId);
7496
- const producerPath = join20(directory, release.artifact.producer.module);
7497
- await mkdir10(dirname12(producerPath), { recursive: true });
7498
- await Promise.all([
7499
- writeFile9(join20(directory, ARTIFACT_FILE), `${JSON.stringify(release.artifact, null, "\t")}
7500
- `),
7501
- writeFile9(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
7502
- ]);
7503
- }, installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
7504
- 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;
7505
7977
  try {
7506
- await access8(destination);
7507
- return destination;
7978
+ await rename7(destination, backup);
7979
+ movedPrevious = true;
7508
7980
  } catch (error) {
7509
- if (!errorHasCode2(error, "ENOENT"))
7981
+ if (!errorHasCode(error, "ENOENT"))
7510
7982
  throw error;
7511
7983
  }
7512
- const staging = await mkdtemp5(join20(bundlesRoot, ".stage-"));
7513
7984
  try {
7514
- await Promise.all(releases.map((release) => writeRelease(staging, release)));
7515
- await rename8(staging, destination);
7985
+ await rename7(staging, destination);
7986
+ await removePreviousBundle(backup, movedPrevious);
7516
7987
  } catch (error) {
7517
- await rm7(staging, { force: true, recursive: true });
7518
- if (errorHasCode2(error, "EEXIST") || errorHasCode2(error, "ENOTEMPTY")) {
7519
- return destination;
7520
- }
7988
+ await restorePreviousBundle(backup, destination, movedPrevious);
7521
7989
  throw error;
7522
7990
  }
7523
- return destination;
7524
- }, materializeAbsoluteMobileCompatibilityBundle = async (input) => {
7525
- const releases = await Promise.all(input.releases.map((release) => verifyAbsoluteMobileCompatibilityProducer(release)));
7526
- const artifacts = retainAbsoluteMobileCompatibilityArtifacts(releases.map(({ artifact }) => artifact));
7527
- if (artifacts.length !== releases.length) {
7528
- 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}.`);
7529
7994
  }
7530
- if (artifacts[0]?.releaseId !== input.currentReleaseId) {
7531
- 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);
7532
8010
  }
7533
- const orderedReleases = artifacts.map((artifact) => {
7534
- const release = releases.find((candidate) => candidate.artifact.releaseId === artifact.releaseId);
7535
- if (!release) {
7536
- throw new TypeError("Materialization input is missing a producer.");
7537
- }
7538
- return release;
7539
- });
7540
- const root = resolvePath3(input.root);
7541
- const bundlesRoot = join20(root, BUNDLES_DIRECTORY);
7542
- await mkdir10(bundlesRoot, { recursive: true });
7543
- const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
7544
- await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
7545
- const index = {
7546
- bundleId,
7547
- currentReleaseId: input.currentReleaseId,
7548
- format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
7549
- releases: artifacts
8011
+ return {
8012
+ ...page,
8013
+ localBundlePath,
8014
+ ...localStylePath ? { localStylePath } : {}
7550
8015
  };
7551
- const pointerPath = join20(root, CURRENT_BUNDLE_FILE);
7552
- const temporaryPointerPath = join20(root, `.current-${crypto.randomUUID()}.json`);
7553
- await writeFile9(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
7554
- `, { flag: "wx" });
7555
- await rename8(temporaryPointerPath, pointerPath);
7556
- return index;
7557
- }, readAbsoluteMobileMaterializedReleases = async (root) => {
7558
- const resolvedRoot = resolvePath3(root);
7559
- try {
7560
- const serialized = await readFile11(join20(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
7561
- const parsed = JSON.parse(serialized);
7562
- const index = parseBundleIndex(parsed);
7563
- const bundleRoot = join20(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
7564
- return Promise.all(index.releases.map(async (artifact) => {
7565
- const producer = Bun.file(join20(bundleRoot, artifact.releaseId, artifact.producer.module));
7566
- await verifyAbsoluteMobileCompatibilityProducer({
7567
- artifact,
7568
- producer
7569
- });
7570
- return { artifact, producer };
7571
- }));
7572
- } catch (error) {
7573
- if (errorHasCode2(error, "ENOENT"))
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("../")) {
7574
8035
  return [];
7575
- throw error;
7576
- }
7577
- };
7578
- var init_materializedBundle = __esm(() => {
7579
- init_artifactStore();
7580
- init_releaseArtifact();
7581
- BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
7582
- });
7583
-
7584
- // node_modules/@absolutejs/sync/dist/client/index.js
7585
- 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) => {
7586
- if (!Number.isSafeInteger(value) || value < 1)
7587
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", `${label} must be a positive safe integer`);
7588
- return value;
7589
- }, isSchemaBundle = (schema) => ("components" in schema), validatePolicyMatch = (match, label) => {
7590
- if (match.length === 0 || match.trim() !== match || /^\*+$/.test(match) || match.includes("**"))
7591
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.match must be an exact name or a non-empty glob without adjacent wildcards.`);
7592
- }, validateSyncLocalDataPolicy = (policy, label = "localData") => {
7593
- if (policy.maxBytesPerNamespace !== undefined && (!Number.isSafeInteger(policy.maxBytesPerNamespace) || policy.maxBytesPerNamespace < 1))
7594
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.maxBytesPerNamespace must be a positive safe integer.`);
7595
- for (const [index, rule] of (policy.collections ?? []).entries()) {
7596
- validatePolicyMatch(rule.match, `${label}.collections[${index}]`);
7597
- if (rule.maxAgeMs !== undefined && (!Number.isSafeInteger(rule.maxAgeMs) || rule.maxAgeMs < 1))
7598
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}].maxAgeMs must be a positive safe integer.`);
7599
- if (rule.persistence === "memory-only" && rule.protection === "required")
7600
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] cannot require at-rest protection when it is memory-only.`);
7601
- if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
7602
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.collections[${index}] declares ${rule.sensitivity} data without required protection or memory-only persistence.`);
7603
- }
7604
- for (const [index, rule] of (policy.mutations ?? []).entries()) {
7605
- validatePolicyMatch(rule.match, `${label}.mutations[${index}]`);
7606
- if (rule.conflict !== undefined && rule.conflict.strategy !== "client-wins" && rule.conflict.strategy !== "manual" && rule.conflict.strategy !== "server-wins")
7607
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.strategy is invalid.`);
7608
- if (rule.conflict?.maxAttempts !== undefined && (!Number.isSafeInteger(rule.conflict.maxAttempts) || rule.conflict.maxAttempts < 1))
7609
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts must be a positive safe integer.`);
7610
- if (rule.conflict?.maxAttempts !== undefined && rule.conflict.strategy !== "client-wins")
7611
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts is only valid for client-wins.`);
7612
- if (rule.persistence === "memory-only" && rule.protection === "required")
7613
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] cannot require at-rest protection when it is memory-only.`);
7614
- if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
7615
- throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] declares ${rule.sensitivity} arguments without required protection.`);
7616
- }
7617
- return policy;
7618
- }, normalizeSyncLocalSchemaComponents = (schema = { version: 1 }) => {
7619
- const components = isSchemaBundle(schema) ? [...schema.components] : [{ ...schema, id: "@absolutejs/app" }];
7620
- const ids = new Set;
7621
- for (const component of components) {
7622
- if (typeof component.id !== "string" || component.id.trim() !== component.id || component.id.length === 0)
7623
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Sync schema component id must be non-empty and trimmed");
7624
- if (ids.has(component.id))
7625
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync schema component "${component.id}" is declared more than once`);
7626
- ids.add(component.id);
7627
- if (component.localData)
7628
- validateSyncLocalDataPolicy(component.localData, `${component.id}.localData`);
7629
- }
7630
- return components.sort((a, b) => a.id.localeCompare(b.id));
7631
- }, resolveSyncLocalSchemaComponents = (storedVersions, schema = { version: 1 }) => {
7632
- const components = normalizeSyncLocalSchemaComponents(schema).map((component) => {
7633
- const current = resolveSyncLocalMigrations(component.version, component);
7634
- return {
7635
- id: component.id,
7636
- ...resolveSyncLocalMigrations(storedVersions[component.id] ?? current.minimumCompatibleVersion, component)
7637
- };
7638
- });
7639
- const active = new Set(components.map((component) => component.id));
7640
- const orphanedComponents = Object.keys(storedVersions).filter((id) => !active.has(id)).sort();
7641
- return { components, orphanedComponents };
7642
- }, resolveSyncLocalMigrations = (storedVersion, schema = { version: 1 }) => {
7643
- positiveVersion(storedVersion, "Stored Sync schema version");
7644
- const targetVersion = positiveVersion(schema.version, "Target Sync schema version");
7645
- const migrations = [...schema.migrations ?? []].sort((a, b) => a.toVersion - b.toVersion);
7646
- const versions = new Set;
7647
- for (const migration of migrations) {
7648
- positiveVersion(migration.toVersion, "Sync migration toVersion");
7649
- if (versions.has(migration.toVersion))
7650
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", `Sync migration ${migration.toVersion} is declared more than once`);
7651
- versions.add(migration.toVersion);
7652
- }
7653
- const inferredMinimum = migrations[0] ? migrations[0].toVersion - 1 : targetVersion;
7654
- const minimumCompatibleVersion = positiveVersion(schema.minimumCompatibleVersion ?? inferredMinimum, "Minimum compatible Sync schema version");
7655
- if (minimumCompatibleVersion > targetVersion)
7656
- throw new SyncLocalStoreSchemaError("INVALID_PLAN", "Minimum compatible Sync schema version cannot exceed its target");
7657
- if (storedVersion > targetVersion)
7658
- throw new SyncLocalStoreSchemaError("SCHEMA_TOO_NEW", `Stored Sync schema ${storedVersion} is newer than this runtime's schema ${targetVersion}`, { storedVersion, targetVersion });
7659
- if (storedVersion < minimumCompatibleVersion)
7660
- throw new SyncLocalStoreSchemaError("SCHEMA_TOO_OLD", `Stored Sync schema ${storedVersion} is older than the minimum compatible schema ${minimumCompatibleVersion}`, { storedVersion, targetVersion });
7661
- const steps = [];
7662
- for (let version2 = storedVersion + 1;version2 <= targetVersion; version2++) {
7663
- const migration = migrations.find((candidate) => candidate.toVersion === version2);
7664
- if (migration === undefined)
7665
- throw new SyncLocalStoreSchemaError("MIGRATION_MISSING", `Sync migration ${version2 - 1} -> ${version2} is missing`, { storedVersion, targetVersion });
7666
- steps.push(migration);
7667
- }
7668
- return { minimumCompatibleVersion, steps, targetVersion };
7669
- };
7670
- var init_client2 = __esm(() => {
7671
- RUNTIME_TRANSPORT = Symbol.for("@absolutejs/sync/client-runtime-transport");
7672
- host = globalThis;
7673
- registry = (() => {
7674
- const existing = host[RUNTIME_TRANSPORT];
7675
- if (isRegistry(existing))
7676
- return existing;
7677
- if (typeof existing === "object" && existing !== null && Array.isArray(Reflect.get(existing, "installations"))) {
7678
- Reflect.set(existing, "clients", []);
7679
- return existing;
7680
- }
7681
- const created = { clients: [], installations: [] };
7682
- Object.defineProperty(host, RUNTIME_TRANSPORT, {
7683
- configurable: false,
7684
- enumerable: false,
7685
- value: created,
7686
- writable: false
7687
- });
7688
- return created;
7689
- })();
7690
- SyncLocalDataPolicyError = class SyncLocalDataPolicyError extends Error {
7691
- code;
7692
- constructor(code, message) {
7693
- super(message);
7694
- this.name = "SyncLocalDataPolicyError";
7695
- this.code = code;
7696
8036
  }
7697
- };
7698
- SyncLocalStoreSchemaError = class SyncLocalStoreSchemaError extends Error {
7699
- code;
7700
- storedVersion;
7701
- targetVersion;
7702
- constructor(code, message, versions = {}) {
7703
- super(message);
7704
- this.name = "SyncLocalStoreSchemaError";
7705
- this.code = code;
7706
- this.storedVersion = versions.storedVersion;
7707
- 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}`);
7708
8045
  }
7709
- };
7710
- });
7711
-
7712
- // src/mobile/syncSchema.ts
7713
- import { readFileSync as readFileSync11 } from "fs";
7714
- import { dirname as dirname13, join as join21, resolve as resolve15 } from "path";
7715
- var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value), manifestAt = (path) => {
7716
- try {
7717
- const value = JSON.parse(readFileSync11(path, "utf8"));
7718
- return object(value) ? value : undefined;
7719
- } catch {
7720
- return;
7721
- }
7722
- }, localSchemaMetadata = (manifest) => {
7723
- const absolutejs = Reflect.get(manifest, "absolutejs");
7724
- if (!object(absolutejs))
7725
- return;
7726
- const sync = Reflect.get(absolutejs, "sync");
7727
- if (!object(sync))
8046
+ return [`/${relativePath}`];
8047
+ });
8048
+ }, copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, copied) => {
8049
+ if (copied.has(specifier))
7728
8050
  return;
7729
- return Reflect.get(sync, "localSchema");
7730
- }, packageManifestPath = (projectRoot, packageName) => {
7731
- let directory = resolve15(projectRoot);
7732
- while (true) {
7733
- const candidate = join21(directory, "node_modules", packageName, "package.json");
7734
- const manifest = manifestAt(candidate);
7735
- if (manifest && Reflect.get(manifest, "name") === packageName)
7736
- return candidate;
7737
- const parent = dirname13(directory);
7738
- if (parent === directory)
7739
- return;
7740
- 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.`);
7741
8063
  }
7742
- }, metadataError = (id, detail) => new TypeError(`Invalid AbsoluteJS Sync schema metadata for ${id}: ${detail}`), positiveVersion2 = (value, id, field) => {
7743
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
7744
- throw metadataError(id, `${field} must be a positive safe integer.`);
7745
- return value;
7746
- }, nonEmpty = (value, id, field) => {
7747
- if (typeof value !== "string" || value.trim() !== value || value.length === 0)
7748
- throw metadataError(id, `${field} must be a non-empty trimmed string.`);
7749
- return value;
7750
- }, requireObject = (value, id, detail) => {
7751
- if (!object(value))
7752
- throw metadataError(id, detail);
7753
- return value;
7754
- }, unknownField = (record, key) => record[key], normalizeJsonValue = (value, id, field) => {
7755
- if (value === null || typeof value === "string" || typeof value === "boolean")
7756
- return value;
7757
- if (typeof value === "number" && Number.isFinite(value))
7758
- return value;
7759
- if (Array.isArray(value))
7760
- return value.map((entry) => normalizeJsonValue(entry, id, field));
7761
- if (object(value))
7762
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
7763
- key,
7764
- normalizeJsonValue(entry, id, field)
7765
- ]));
7766
- throw metadataError(id, `${field} must be JSON-safe.`);
7767
- }, operation = (value, id, index) => {
7768
- const record = requireObject(value, id, `migration operation ${index} must be an object.`);
7769
- const type = Reflect.get(record, "type");
7770
- const collection = nonEmpty(Reflect.get(record, "collection"), id, `operation ${index}.collection`);
7771
- if (type === "delete-collection")
7772
- return { collection, type };
7773
- if (type === "rename-field")
7774
- return {
7775
- collection,
7776
- from: nonEmpty(Reflect.get(record, "from"), id, `operation ${index}.from`),
7777
- to: nonEmpty(Reflect.get(record, "to"), id, `operation ${index}.to`),
7778
- type
7779
- };
7780
- const field = nonEmpty(Reflect.get(record, "field"), id, `operation ${index}.field`);
7781
- if (type === "remove-field")
7782
- return { collection, field, type };
7783
- if (type === "set-default")
7784
- return {
7785
- collection,
7786
- field,
7787
- type,
7788
- value: normalizeJsonValue(Reflect.get(record, "value"), id, `operation ${index}.value`)
7789
- };
7790
- throw metadataError(id, `operation ${index}.type is not supported.`);
7791
- }, migration = (value, id, index) => {
7792
- const record = requireObject(value, id, `migration ${index} must be an object.`);
7793
- const allowed = new Set(["operations", "toVersion"]);
7794
- const unsupported = Object.keys(record).find((key) => !allowed.has(key));
7795
- if (unsupported)
7796
- throw metadataError(id, `migration ${index}.${unsupported} is not declarative metadata.`);
7797
- const declaredOperations = Reflect.get(record, "operations");
7798
- if (declaredOperations !== undefined && !Array.isArray(declaredOperations))
7799
- throw metadataError(id, `migration ${index}.operations must be an array.`);
7800
- const operations = Array.isArray(declaredOperations) ? declaredOperations : [];
7801
- return {
7802
- operations: operations.map((entry, operationIndex) => operation(entry, id, operationIndex)),
7803
- toVersion: positiveVersion2(Reflect.get(record, "toVersion"), id, `migration ${index}.toVersion`)
7804
- };
7805
- }, localDataPolicy = (value, id) => {
7806
- const record = requireObject(value, id, "localData must be an object.");
7807
- const allowed = new Set([
7808
- "collections",
7809
- "maxBytesPerNamespace",
7810
- "mutations"
7811
- ]);
7812
- const unsupported = Object.keys(record).find((key) => !allowed.has(key));
7813
- if (unsupported)
7814
- throw metadataError(id, `localData.${unsupported} is not supported.`);
7815
- const collectionRules = Reflect.get(record, "collections");
7816
- const mutationRules = Reflect.get(record, "mutations");
7817
- if (collectionRules !== undefined && !Array.isArray(collectionRules))
7818
- throw metadataError(id, "localData.collections must be an array.");
7819
- if (mutationRules !== undefined && !Array.isArray(mutationRules))
7820
- throw metadataError(id, "localData.mutations must be an array.");
7821
- const collections = Array.isArray(collectionRules) ? collectionRules.map((entry, index) => {
7822
- const rule = requireObject(entry, id, `localData.collections[${index}] must be an object.`);
7823
- const allowedRuleKeys = new Set([
7824
- "evictionPriority",
7825
- "match",
7826
- "maxAgeMs",
7827
- "onProtectionUnavailable",
7828
- "persistence",
7829
- "protection",
7830
- "sensitivity"
7831
- ]);
7832
- const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
7833
- if (unsupportedRuleKey)
7834
- throw metadataError(id, `localData.collections[${index}].${unsupportedRuleKey} is not supported.`);
7835
- const match = nonEmpty(Reflect.get(rule, "match"), id, `localData.collections[${index}].match`);
7836
- const persistence = unknownField(rule, "persistence");
7837
- const sensitivity = unknownField(rule, "sensitivity");
7838
- const protection = unknownField(rule, "protection");
7839
- const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
7840
- const evictionPriority = unknownField(rule, "evictionPriority");
7841
- const maxAge = unknownField(rule, "maxAgeMs");
7842
- if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
7843
- throw metadataError(id, `localData.collections[${index}].persistence is invalid.`);
7844
- if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
7845
- throw metadataError(id, `localData.collections[${index}].sensitivity is invalid.`);
7846
- if (protection !== undefined && protection !== "none" && protection !== "required")
7847
- throw metadataError(id, `localData.collections[${index}].protection is invalid.`);
7848
- if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
7849
- throw metadataError(id, `localData.collections[${index}].onProtectionUnavailable is invalid.`);
7850
- if (evictionPriority !== undefined && evictionPriority !== "critical" && evictionPriority !== "normal" && evictionPriority !== "disposable")
7851
- throw metadataError(id, `localData.collections[${index}].evictionPriority is invalid.`);
7852
- return {
7853
- match,
7854
- ...sensitivity ? { sensitivity } : {},
7855
- ...persistence ? { persistence } : {},
7856
- ...protection ? { protection } : {},
7857
- ...onProtectionUnavailable ? {
7858
- onProtectionUnavailable
7859
- } : {},
7860
- ...evictionPriority ? { evictionPriority } : {},
7861
- ...maxAge === undefined ? {} : {
7862
- maxAgeMs: positiveVersion2(maxAge, id, `localData.collections[${index}].maxAgeMs`)
7863
- }
7864
- };
7865
- }) : undefined;
7866
- const mutations = Array.isArray(mutationRules) ? mutationRules.map((entry, index) => {
7867
- const rule = requireObject(entry, id, `localData.mutations[${index}] must be an object.`);
7868
- const allowedRuleKeys = new Set([
7869
- "conflict",
7870
- "match",
7871
- "onProtectionUnavailable",
7872
- "persistence",
7873
- "protection",
7874
- "sensitivity"
7875
- ]);
7876
- const unsupportedRuleKey = Object.keys(rule).find((key) => !allowedRuleKeys.has(key));
7877
- if (unsupportedRuleKey)
7878
- throw metadataError(id, `localData.mutations[${index}].${unsupportedRuleKey} is not supported.`);
7879
- const protection = unknownField(rule, "protection");
7880
- const sensitivity = unknownField(rule, "sensitivity");
7881
- const persistence = unknownField(rule, "persistence");
7882
- const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
7883
- const declaredConflict = unknownField(rule, "conflict");
7884
- let conflict;
7885
- if (declaredConflict !== undefined) {
7886
- const conflictRecord = requireObject(declaredConflict, id, `localData.mutations[${index}].conflict must be an object.`);
7887
- const unsupportedConflictKey = Object.keys(conflictRecord).find((key) => key !== "maxAttempts" && key !== "strategy");
7888
- if (unsupportedConflictKey)
7889
- throw metadataError(id, `localData.mutations[${index}].conflict.${unsupportedConflictKey} is not supported.`);
7890
- const strategy = unknownField(conflictRecord, "strategy");
7891
- if (strategy !== "client-wins" && strategy !== "manual" && strategy !== "server-wins")
7892
- throw metadataError(id, `localData.mutations[${index}].conflict.strategy is invalid.`);
7893
- const maxAttempts = unknownField(conflictRecord, "maxAttempts");
7894
- if (maxAttempts !== undefined && strategy !== "client-wins")
7895
- throw metadataError(id, `localData.mutations[${index}].conflict.maxAttempts requires client-wins.`);
7896
- conflict = {
7897
- strategy,
7898
- ...maxAttempts === undefined ? {} : {
7899
- 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
+ }
7900
8102
  }
7901
- };
7902
- }
7903
- if (protection !== undefined && protection !== "none" && protection !== "required")
7904
- throw metadataError(id, `localData.mutations[${index}].protection is invalid.`);
7905
- if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
7906
- throw metadataError(id, `localData.mutations[${index}].sensitivity is invalid.`);
7907
- if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
7908
- throw metadataError(id, `localData.mutations[${index}].onProtectionUnavailable is invalid.`);
7909
- if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
7910
- throw metadataError(id, `localData.mutations[${index}].persistence is invalid.`);
7911
- return {
7912
- match: nonEmpty(Reflect.get(rule, "match"), id, `localData.mutations[${index}].match`),
7913
- ...conflict ? { conflict } : {},
7914
- ...sensitivity ? { sensitivity } : {},
7915
- ...onProtectionUnavailable ? { onProtectionUnavailable } : {},
7916
- ...persistence ? {
7917
- persistence
7918
- } : {},
7919
- ...protection ? { protection } : {}
8103
+ } : {}
7920
8104
  };
7921
- }) : undefined;
7922
- 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
+ }
7923
8187
  return {
7924
- ...collections ? { collections } : {},
7925
- ...mutations ? { mutations } : {},
7926
- ...quota === undefined ? {} : {
7927
- maxBytesPerNamespace: positiveVersion2(quota, id, "localData.maxBytesPerNamespace")
7928
- }
8188
+ bundleId: value.bundleId,
8189
+ currentReleaseId: value.currentReleaseId,
8190
+ format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
8191
+ releases
7929
8192
  };
7930
- }, component = (id, value) => {
7931
- const record = requireObject(value, id, "localSchema must be an object.");
7932
- const allowed = new Set([
7933
- "localData",
7934
- "migrations",
7935
- "minimumCompatibleVersion",
7936
- "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()))
7937
8201
  ]);
7938
- const unsupported = Object.keys(record).find((key) => !allowed.has(key));
7939
- if (unsupported)
7940
- throw metadataError(id, `${unsupported} is not supported.`);
7941
- const version2 = positiveVersion2(Reflect.get(record, "version"), id, "version");
7942
- const declaredMinimum = Reflect.get(record, "minimumCompatibleVersion");
7943
- const minimumCompatibleVersion = declaredMinimum === undefined ? Math.max(1, version2 - 2) : positiveVersion2(declaredMinimum, id, "minimumCompatibleVersion");
7944
- const declaredMigrations = Reflect.get(record, "migrations");
7945
- const declaredLocalData = Reflect.get(record, "localData");
7946
- if (declaredMigrations !== undefined && !Array.isArray(declaredMigrations))
7947
- throw metadataError(id, "migrations must be an array.");
7948
- const migrations = Array.isArray(declaredMigrations) ? declaredMigrations : undefined;
7949
- return {
7950
- id,
7951
- ...declaredLocalData === undefined ? {} : { localData: localDataPolicy(declaredLocalData, id) },
7952
- minimumCompatibleVersion,
7953
- ...Array.isArray(migrations) ? {
7954
- migrations: migrations.map((entry, index) => migration(entry, id, index))
7955
- } : {},
7956
- 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
7957
8249
  };
7958
- }, dependencyNames = (manifest) => [
7959
- Reflect.get(manifest, "dependencies"),
7960
- Reflect.get(manifest, "optionalDependencies"),
7961
- Reflect.get(manifest, "devDependencies"),
7962
- Reflect.get(manifest, "peerDependencies")
7963
- ].flatMap((dependencies) => object(dependencies) ? Object.keys(dependencies) : []).filter((name, index, names) => names.indexOf(name) === index).sort(), discoverAbsoluteSyncSchema = (projectRoot) => {
7964
- const appManifestPath = join21(resolve15(projectRoot), "package.json");
7965
- const appManifest = manifestAt(appManifestPath);
7966
- if (!appManifest)
7967
- return {
7968
- components: [
7969
- {
7970
- id: "@absolutejs/app",
7971
- minimumCompatibleVersion: 1,
7972
- version: 1
7973
- }
7974
- ],
7975
- sources: []
7976
- };
7977
- const appMetadata = localSchemaMetadata(appManifest);
7978
- const components = [
7979
- appMetadata === undefined ? { id: "@absolutejs/app", minimumCompatibleVersion: 1, version: 1 } : component("@absolutejs/app", appMetadata)
7980
- ];
7981
- const sources = [
7982
- { id: "@absolutejs/app", manifestPath: appManifestPath }
7983
- ];
7984
- for (const name of dependencyNames(appManifest)) {
7985
- const manifestPath = packageManifestPath(projectRoot, name);
7986
- if (!manifestPath)
7987
- continue;
7988
- const manifest = manifestAt(manifestPath);
7989
- if (!manifest)
7990
- continue;
7991
- const metadata = localSchemaMetadata(manifest);
7992
- if (metadata === undefined)
7993
- continue;
7994
- components.push(component(name, metadata));
7995
- 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;
7996
8275
  }
7997
- components.sort((left, right) => left.id.localeCompare(right.id));
7998
- sources.sort((left, right) => left.id.localeCompare(right.id));
7999
- resolveSyncLocalSchemaComponents({}, { components });
8000
- return { components, sources };
8001
8276
  };
8002
- var init_syncSchema = __esm(() => {
8003
- init_client2();
8277
+ var init_materializedBundle = __esm(() => {
8278
+ init_artifactStore();
8279
+ init_releaseArtifact();
8280
+ BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
8004
8281
  });
8005
8282
 
8006
8283
  // src/mobile/deviceCapabilities.ts
@@ -8327,9 +8604,6 @@ var isElysiaApp = (value) => typeof value === "object" && value !== null && type
8327
8604
  runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
8328
8605
  });
8329
8606
  const auth = resolveAbsoluteMobileAuthManifest(options.projectRoot, mobile);
8330
- if (mobile.engine === "expo" && auth) {
8331
- throw new TypeError("Expo mobile Auth is not released yet. The experimental Expo shell cannot safely substitute Capacitor credentials; remove engine: 'expo' or wait for @absolutejs/auth-expo.");
8332
- }
8333
8607
  const sync = auth !== undefined && projectUsesAbsoluteSync(options.projectRoot);
8334
8608
  const syncSchema = sync ? discoverAbsoluteSyncSchema(options.projectRoot) : undefined;
8335
8609
  const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot);
@@ -8956,12 +9230,12 @@ var init_serverBundleExternals = __esm(() => {
8956
9230
  // src/core/prerender.ts
8957
9231
  var exports_prerender = {};
8958
9232
  __export(exports_prerender, {
8959
- routeToFilename: () => routeToFilename,
8960
- rerenderRoute: () => rerenderRoute,
8961
- readTimestamp: () => readTimestamp,
8962
- prerenderWithServer: () => prerenderWithServer,
9233
+ PRERENDER_BYPASS_HEADER: () => PRERENDER_BYPASS_HEADER,
8963
9234
  prerender: () => prerender,
8964
- PRERENDER_BYPASS_HEADER: () => PRERENDER_BYPASS_HEADER
9235
+ prerenderWithServer: () => prerenderWithServer,
9236
+ readTimestamp: () => readTimestamp,
9237
+ rerenderRoute: () => rerenderRoute,
9238
+ routeToFilename: () => routeToFilename
8965
9239
  });
8966
9240
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync14 } from "fs";
8967
9241
  import { join as join24 } from "path";
@@ -9621,8 +9895,8 @@ var init_rewriteImportsPlugin = __esm(() => {
9621
9895
  // src/build/rewriteImports.ts
9622
9896
  var exports_rewriteImports = {};
9623
9897
  __export(exports_rewriteImports, {
9624
- rewriteVendorDirectories: () => rewriteVendorDirectories2,
9625
- rewriteImports: () => rewriteImports
9898
+ rewriteImports: () => rewriteImports,
9899
+ rewriteVendorDirectories: () => rewriteVendorDirectories2
9626
9900
  });
9627
9901
  var rewriteImports = async (outputPaths, vendorPaths) => {
9628
9902
  const jsFiles = outputPaths.filter((path) => path.endsWith(".js"));
@@ -10285,11 +10559,11 @@ var init_build = __esm(() => {
10285
10559
  // src/cli/scripts/lintProof.ts
10286
10560
  var exports_lintProof = {};
10287
10561
  __export(exports_lintProof, {
10288
- writeLintProof: () => writeLintProof,
10289
- verifyLintProof: () => verifyLintProof,
10290
- runLintProof: () => runLintProof,
10562
+ createLintProof: () => createLintProof,
10291
10563
  createLintSourceTree: () => createLintSourceTree,
10292
- createLintProof: () => createLintProof
10564
+ runLintProof: () => runLintProof,
10565
+ verifyLintProof: () => verifyLintProof,
10566
+ writeLintProof: () => writeLintProof
10293
10567
  });
10294
10568
  import {
10295
10569
  createHash as createHash12,
@@ -15063,9 +15337,9 @@ var init_htmx = __esm(() => {
15063
15337
  // src/cli/scripts/env.ts
15064
15338
  var exports_env = {};
15065
15339
  __export(exports_env, {
15066
- scanEnvUsage: () => scanEnvUsage,
15340
+ collectEnvVars: () => collectEnvVars,
15067
15341
  runEnv: () => runEnv,
15068
- collectEnvVars: () => collectEnvVars
15342
+ scanEnvUsage: () => scanEnvUsage
15069
15343
  });
15070
15344
  import { existsSync as existsSync33, readFileSync as readFileSync33 } from "fs";
15071
15345
  import { join as join41 } from "path";
@@ -15129,12 +15403,12 @@ var init_env = __esm(() => {
15129
15403
  // src/cli/scripts/db.ts
15130
15404
  var exports_db = {};
15131
15405
  __export(exports_db, {
15132
- runDb: () => runDb,
15133
- quoteIdent: () => quoteIdent,
15134
- encodeValue: () => encodeValue,
15135
- dependencyOrder: () => dependencyOrder,
15406
+ chunkRows: () => chunkRows,
15136
15407
  conflictClause: () => conflictClause,
15137
- chunkRows: () => chunkRows
15408
+ dependencyOrder: () => dependencyOrder,
15409
+ encodeValue: () => encodeValue,
15410
+ quoteIdent: () => quoteIdent,
15411
+ runDb: () => runDb
15138
15412
  });
15139
15413
  import { existsSync as existsSync34, mkdirSync as mkdirSync15, readFileSync as readFileSync34, writeFileSync as writeFileSync18 } from "fs";
15140
15414
  import { join as join42 } from "path";
@@ -16474,8 +16748,8 @@ var init_externalAssetPlugin = () => {};
16474
16748
  // src/cli/scripts/compile.ts
16475
16749
  var exports_compile = {};
16476
16750
  __export(exports_compile, {
16477
- shouldEmbedCompiledAsset: () => shouldEmbedCompiledAsset,
16478
- compile: () => compile
16751
+ compile: () => compile,
16752
+ shouldEmbedCompiledAsset: () => shouldEmbedCompiledAsset
16479
16753
  });
16480
16754
  var {env: env5 } = globalThis.Bun;
16481
16755
  import {
@@ -18434,12 +18708,38 @@ import { mkdir as mkdir12, writeFile as writeFile14 } from "fs/promises";
18434
18708
  import { dirname as dirname29, resolve as resolve38 } from "path";
18435
18709
 
18436
18710
  class CdpConnection {
18437
- diagnostics = [];
18438
- socket;
18439
- #closed = false;
18440
- #nextId = 1;
18441
- #pending = new Map;
18711
+ #closed;
18712
+ #nextId;
18713
+ #pending;
18442
18714
  constructor(socket) {
18715
+ this.diagnostics = [];
18716
+ this.#closed = false;
18717
+ this.#nextId = 1;
18718
+ this.#pending = new Map;
18719
+ this.command = (method, params = {}, timeoutMs = CDP_COMMAND_TIMEOUT_MS) => {
18720
+ if (this.#closed) {
18721
+ return Promise.reject(new Error("Android WebView debugger is closed."));
18722
+ }
18723
+ const id = this.#nextId++;
18724
+ return new Promise((_resolve, reject) => {
18725
+ const timer = setTimeout(() => {
18726
+ this.#pending.delete(id);
18727
+ reject(new Error(`CDP ${method} timed out after ${timeoutMs}ms.`));
18728
+ }, timeoutMs);
18729
+ this.#pending.set(id, {
18730
+ reject,
18731
+ resolve: _resolve,
18732
+ timer
18733
+ });
18734
+ this.socket.send(JSON.stringify({ id, method, params }));
18735
+ });
18736
+ };
18737
+ this.close = async () => {
18738
+ if (this.#closed)
18739
+ return;
18740
+ this.#closed = true;
18741
+ this.socket.close();
18742
+ };
18443
18743
  this.socket = socket;
18444
18744
  socket.addEventListener("message", (event) => {
18445
18745
  this.onMessage(event.data);
@@ -18518,30 +18818,6 @@ class CdpConnection {
18518
18818
  }
18519
18819
  pending.resolve(message.result);
18520
18820
  }
18521
- command = (method, params = {}, timeoutMs = CDP_COMMAND_TIMEOUT_MS) => {
18522
- if (this.#closed) {
18523
- return Promise.reject(new Error("Android WebView debugger is closed."));
18524
- }
18525
- const id = this.#nextId++;
18526
- return new Promise((_resolve, reject) => {
18527
- const timer = setTimeout(() => {
18528
- this.#pending.delete(id);
18529
- reject(new Error(`CDP ${method} timed out after ${timeoutMs}ms.`));
18530
- }, timeoutMs);
18531
- this.#pending.set(id, {
18532
- reject,
18533
- resolve: _resolve,
18534
- timer
18535
- });
18536
- this.socket.send(JSON.stringify({ id, method, params }));
18537
- });
18538
- };
18539
- close = async () => {
18540
- if (this.#closed)
18541
- return;
18542
- this.#closed = true;
18543
- this.socket.close();
18544
- };
18545
18821
  }
18546
18822
  var CDP_COMMAND_TIMEOUT_MS = 1e4, WEBVIEW_ATTACH_TIMEOUT_MS = 30000, WEBVIEW_POLL_MS = 200, captureCommand3 = (command) => {
18547
18823
  const result = Bun.spawnSync(command, {
@@ -21019,7 +21295,7 @@ var NOT_FOUND4 = -1, isRecord15 = (value) => typeof value === "object" && value
21019
21295
  }, initialize = async (args) => {
21020
21296
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
21021
21297
  if (mobile.engine === "expo") {
21022
- console.warn("Experimental: Expo Auth, Sync, release publishing, and physical-device acceptance are not complete.");
21298
+ console.warn("Experimental: Expo Auth and authenticated HTTP are available; Sync, release publishing, and physical-device acceptance are not complete.");
21023
21299
  const generated2 = await writeAbsoluteExpoProject(mobile, {
21024
21300
  force: args.includes("--force"),
21025
21301
  projectRoot
@@ -22940,12 +23216,12 @@ import { spawn as nodeSpawn } from "child_process";
22940
23216
  import {
22941
23217
  createWriteStream,
22942
23218
  existsSync as existsSync5,
22943
- readFileSync as readFileSync8,
23219
+ readFileSync as readFileSync9,
22944
23220
  rmSync as rmSync2,
22945
23221
  writeFileSync as writeFileSync4
22946
23222
  } from "fs";
22947
23223
  import { tmpdir as tmpdir2 } from "os";
22948
- import { join as join15, resolve as resolvePath2 } from "path";
23224
+ import { join as join16, resolve as resolvePath2 } from "path";
22949
23225
 
22950
23226
  // src/dev/tunnel/client.ts
22951
23227
  var RECONNECT_DELAY_MS = 2000;
@@ -23455,7 +23731,7 @@ init_expoProject();
23455
23731
  init_iosPhysicalDeviceTransport();
23456
23732
  import { spawn } from "child_process";
23457
23733
  import { access as access2 } from "fs/promises";
23458
- import { join as join6 } from "path";
23734
+ import { join as join8 } from "path";
23459
23735
  var METRO_READY_TIMEOUT_MS = 60000;
23460
23736
  var PROCESS_CLOSE_TIMEOUT_MS = 2000;
23461
23737
  var commandEnvironment = (options) => ({
@@ -23468,7 +23744,7 @@ var commandEnvironment = (options) => ({
23468
23744
  ...options.metroHost ? { REACT_NATIVE_PACKAGER_HOSTNAME: options.metroHost } : {}
23469
23745
  });
23470
23746
  var absoluteExpoExecutable = async (project) => {
23471
- const executable = join6(project, "node_modules", ".bin", "expo");
23747
+ const executable = join8(project, "node_modules", ".bin", "expo");
23472
23748
  try {
23473
23749
  await access2(executable);
23474
23750
  return executable;
@@ -23564,18 +23840,18 @@ var stopProcess = async (process2) => {
23564
23840
  return;
23565
23841
  process2.kill("SIGTERM");
23566
23842
  await Promise.race([
23567
- new Promise((resolve4) => process2.once("exit", () => resolve4())),
23568
- 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))
23569
23845
  ]);
23570
23846
  if (process2.exitCode === null)
23571
23847
  process2.kill("SIGKILL");
23572
23848
  };
23573
- var waitForExit = (process2) => new Promise((resolve4) => {
23849
+ var waitForExit = (process2) => new Promise((resolve5) => {
23574
23850
  if (process2.exitCode !== null) {
23575
- resolve4(process2.exitCode);
23851
+ resolve5(process2.exitCode);
23576
23852
  return;
23577
23853
  }
23578
- process2.once("exit", (code) => resolve4(code ?? 1));
23854
+ process2.once("exit", (code) => resolve5(code ?? 1));
23579
23855
  });
23580
23856
  var runUtilityCommand = async (run, command, args, options) => {
23581
23857
  const child = run(command, args, {
@@ -23655,9 +23931,9 @@ var startAbsoluteExpoDevSession = async (options) => {
23655
23931
  }) : undefined;
23656
23932
  let metroReady = false;
23657
23933
  let resolveMetro;
23658
- const metroPromise = new Promise((resolve4, reject) => {
23934
+ const metroPromise = new Promise((resolve5, reject) => {
23659
23935
  if (!metro) {
23660
- resolve4();
23936
+ resolve5();
23661
23937
  return;
23662
23938
  }
23663
23939
  const timeout = setTimeout(() => {
@@ -23665,7 +23941,7 @@ var startAbsoluteExpoDevSession = async (options) => {
23665
23941
  }, METRO_READY_TIMEOUT_MS);
23666
23942
  resolveMetro = () => {
23667
23943
  clearTimeout(timeout);
23668
- resolve4();
23944
+ resolve5();
23669
23945
  };
23670
23946
  metro.once("exit", (code) => {
23671
23947
  if (!metroReady) {
@@ -24123,9 +24399,9 @@ var setupHttpsCert = async (hosts = []) => {
24123
24399
  await setupCertWithPrompt(ensureDevCert2, setupMkcert2, hosts);
24124
24400
  return getDevCertificateAuthorityPath2();
24125
24401
  };
24126
- var mobileReachableHost = (host) => {
24127
- if (host !== "0.0.0.0" && host !== "::")
24128
- return host;
24402
+ var mobileReachableHost = (host2) => {
24403
+ if (host2 !== "0.0.0.0" && host2 !== "::")
24404
+ return host2;
24129
24405
  const address = getLocalIPAddress();
24130
24406
  if (address === "localhost") {
24131
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.");
@@ -24220,7 +24496,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24220
24496
  const generated = await writeAbsoluteExpoProject(normalized, {
24221
24497
  projectRoot: process.cwd()
24222
24498
  });
24223
- 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"));
24224
24500
  if (!dependenciesReady) {
24225
24501
  const install = await confirmPrompt("Expo development dependencies are missing. Install the pinned SDK 57 development client now?");
24226
24502
  if (!install) {
@@ -24230,7 +24506,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24230
24506
  cwd: generated.path,
24231
24507
  stdio: "inherit"
24232
24508
  });
24233
- 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)));
24234
24510
  if (installExit !== 0) {
24235
24511
  throw new TypeError(`Expo dependency installation exited with status ${installExit}.`);
24236
24512
  }
@@ -24299,7 +24575,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24299
24575
  }
24300
24576
  }
24301
24577
  if (ready) {
24302
- const nativeDirectory = join15(normalized.nativeProjectDirectory, "android");
24578
+ const nativeDirectory = join16(normalized.nativeProjectDirectory, "android");
24303
24579
  let createNativeProject = false;
24304
24580
  if (!existsSync5(nativeDirectory)) {
24305
24581
  createNativeProject = await confirmPrompt("Create the managed Capacitor Android project now?");
@@ -24319,7 +24595,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24319
24595
  if (!remote) {
24320
24596
  console.log(cliTag("\x1B[33m", "iOS target skipped. Pair a Mac with `absolute mobile pair mac <name> <user@host>`."));
24321
24597
  } else {
24322
- const nativeDirectory = join15(normalized.nativeProjectDirectory, "ios");
24598
+ const nativeDirectory = join16(normalized.nativeProjectDirectory, "ios");
24323
24599
  if (!existsSync5(nativeDirectory)) {
24324
24600
  console.log(cliTag("\x1B[33m", "The iOS project is missing. Run `absolute mobile init` before remote development."));
24325
24601
  } else {
@@ -24340,7 +24616,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24340
24616
  }
24341
24617
  }
24342
24618
  if (ready) {
24343
- const nativeDirectory = join15(normalized.nativeProjectDirectory, "ios");
24619
+ const nativeDirectory = join16(normalized.nativeProjectDirectory, "ios");
24344
24620
  let createNativeProject = false;
24345
24621
  if (!existsSync5(nativeDirectory)) {
24346
24622
  createNativeProject = await confirmPrompt("Create the managed Capacitor iOS project now?");
@@ -24692,9 +24968,9 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24692
24968
  iosDevStart = null;
24693
24969
  });
24694
24970
  };
24695
- const absoluteDevOrigin = (host) => {
24971
+ const absoluteDevOrigin = (host2) => {
24696
24972
  const url = new URL(`${httpsEnabled ? "https" : "http"}://localhost`);
24697
- url.hostname = host;
24973
+ url.hostname = host2;
24698
24974
  url.port = String(port);
24699
24975
  return url.origin;
24700
24976
  };
@@ -24960,8 +25236,8 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24960
25236
  });
24961
25237
  if (probe && probe.port !== port) {
24962
25238
  const { port: nextPort } = probe;
24963
- const { host } = refreshedDevConfig;
24964
- const displayHost = host === "0.0.0.0" ? "localhost" : host;
25239
+ const { host: host2 } = refreshedDevConfig;
25240
+ const displayHost = host2 === "0.0.0.0" ? "localhost" : host2;
24965
25241
  console.log(cliTag("\x1B[36m", `Port changed in config \u2014 switching to http://${displayHost}:${nextPort}/`));
24966
25242
  port = nextPort;
24967
25243
  updateLockMetadata(buildDirectory, { port });
@@ -24976,7 +25252,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24976
25252
  for (const name of candidates) {
24977
25253
  let text;
24978
25254
  try {
24979
- text = readFileSync8(resolvePath2(process.cwd(), name), "utf8");
25255
+ text = readFileSync9(resolvePath2(process.cwd(), name), "utf8");
24980
25256
  } catch {
24981
25257
  continue;
24982
25258
  }
@@ -24998,7 +25274,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
24998
25274
  }
24999
25275
  return merged;
25000
25276
  };
25001
- const heapPreloadPath = join15(tmpdir2(), `absolute-heap-${process.pid}.ts`);
25277
+ const heapPreloadPath = join16(tmpdir2(), `absolute-heap-${process.pid}.ts`);
25002
25278
  let heapSnapshotEnabled = false;
25003
25279
  try {
25004
25280
  writeFileSync4(heapPreloadPath, DEV_CHILD_PRELOAD);
@@ -25097,9 +25373,9 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25097
25373
  };
25098
25374
  try {
25099
25375
  const { watch: watch3 } = await import("fs");
25100
- const { dirname: dirname8 } = await import("path");
25376
+ const { dirname: dirname9 } = await import("path");
25101
25377
  const absServerEntry = resolvePath2(serverEntry);
25102
- const serverEntryDir = dirname8(absServerEntry);
25378
+ const serverEntryDir = dirname9(absServerEntry);
25103
25379
  const ROOT_RESTART_DENY = new Set([
25104
25380
  "build",
25105
25381
  "dist",
@@ -25131,7 +25407,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25131
25407
  if (now - last < 100)
25132
25408
  return;
25133
25409
  recentlyHandled.set(filename, now);
25134
- scheduleServerRestart(join15(serverEntryDir, filename));
25410
+ scheduleServerRestart(join16(serverEntryDir, filename));
25135
25411
  };
25136
25412
  const recoveryScan = async () => {
25137
25413
  let entries;
@@ -25150,7 +25426,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
25150
25426
  continue;
25151
25427
  let fileStat;
25152
25428
  try {
25153
- fileStat = statSync(join15(serverEntryDir, entry.name));
25429
+ fileStat = statSync(join16(serverEntryDir, entry.name));
25154
25430
  } catch {
25155
25431
  continue;
25156
25432
  }
@@ -25538,9 +25814,9 @@ init_eslint();
25538
25814
  init_constants();
25539
25815
  init_utils();
25540
25816
  import { execSync as execSync2 } from "child_process";
25541
- import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
25817
+ import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
25542
25818
  import { arch as arch2, cpus, platform as platform3, totalmem, version } from "os";
25543
- import { resolve as resolve11 } from "path";
25819
+ import { resolve as resolve12 } from "path";
25544
25820
  var bold = (str) => `\x1B[1m${str}\x1B[0m`;
25545
25821
  var getBinaryVersion = (binary, flag = "--version") => {
25546
25822
  try {
@@ -25560,7 +25836,7 @@ var getPackageVersion = (packageName) => {
25560
25836
  const pkgPath = __require.resolve(`${packageName}/package.json`, {
25561
25837
  paths: [process.cwd()]
25562
25838
  });
25563
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
25839
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
25564
25840
  const ver = pkg.version;
25565
25841
  return ver;
25566
25842
  } catch {
@@ -25570,8 +25846,8 @@ var getPackageVersion = (packageName) => {
25570
25846
  var getAbsoluteVersion = () => {
25571
25847
  try {
25572
25848
  const candidates = [
25573
- resolve11(import.meta.dir, "..", "..", "package.json"),
25574
- resolve11(import.meta.dir, "..", "..", "..", "package.json")
25849
+ resolve12(import.meta.dir, "..", "..", "package.json"),
25850
+ resolve12(import.meta.dir, "..", "..", "..", "package.json")
25575
25851
  ];
25576
25852
  const pkgPath = candidates.find((candidate) => existsSync8(candidate));
25577
25853
  if (pkgPath)
@@ -25582,7 +25858,7 @@ var getAbsoluteVersion = () => {
25582
25858
  return getPackageVersion("@absolutejs/absolute");
25583
25859
  };
25584
25860
  var readPackageVersion = (pkgPath) => {
25585
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
25861
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
25586
25862
  const ver = pkg.version;
25587
25863
  return ver;
25588
25864
  };
@@ -25684,7 +25960,7 @@ var info = () => {
25684
25960
  // src/cli/cache.ts
25685
25961
  init_constants();
25686
25962
  import { mkdir as mkdir7 } from "fs/promises";
25687
- import { join as join16 } from "path";
25963
+ import { join as join17 } from "path";
25688
25964
  var {Glob } = globalThis.Bun;
25689
25965
  var CACHE_DIR = ".absolutejs";
25690
25966
  var MAX_FILES_PER_BATCH = 200;
@@ -25736,7 +26012,7 @@ var hashFiles = async (paths) => {
25736
26012
  };
25737
26013
  var loadCache = async (tool) => {
25738
26014
  try {
25739
- const path = join16(CACHE_DIR, `${tool}.cache.json`);
26015
+ const path = join17(CACHE_DIR, `${tool}.cache.json`);
25740
26016
  const data = await Bun.file(path).json();
25741
26017
  const result = data;
25742
26018
  return result;
@@ -25783,7 +26059,7 @@ var runTool = async (adapter, args) => {
25783
26059
  };
25784
26060
  var saveCache = async (tool, data) => {
25785
26061
  await mkdir7(CACHE_DIR, { recursive: true });
25786
- const path = join16(CACHE_DIR, `${tool}.cache.json`);
26062
+ const path = join17(CACHE_DIR, `${tool}.cache.json`);
25787
26063
  await Bun.write(path, JSON.stringify(data, null, "\t"));
25788
26064
  };
25789
26065