@absolutejs/absolute 0.20.0-beta.103 → 0.20.0-beta.105

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 (32) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/changelog.json +33 -0
  3. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  4. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  5. package/dist/cli/{compile-4xbpm6hf.js → compile-hsbs1xf6.js} +2 -2
  6. package/dist/cli/{dev-m4r4zs2k.js → dev-9cp5t2n1.js} +4 -4
  7. package/dist/cli/{expoProject-780thyfd.js → expoProject-zkprmjjz.js} +1 -1
  8. package/dist/cli/{index-d5fr1xp3.js → index-0zcebhm4.js} +59 -1
  9. package/dist/cli/{index-wpfvpsk7.js → index-5skbydh0.js} +1 -1
  10. package/dist/cli/{index-3sdvzrv2.js → index-7fxrgx5q.js} +1 -1
  11. package/dist/cli/{index-6ff78meg.js → index-hrzd7e5x.js} +10 -2
  12. package/dist/cli/{index-rvpd4w9t.js → index-mazxtfh4.js} +1 -1
  13. package/dist/cli/{index-qxnptvwn.js → index-q1d4ns4n.js} +100 -9
  14. package/dist/cli/index.js +5 -5
  15. package/dist/cli/{iosNativeWatcher-3k715hq0.js → iosNativeWatcher-t6gv0p1w.js} +2 -2
  16. package/dist/cli/{iosSimulatorController-dr98a0p9.js → iosSimulatorController-5n2a9fxv.js} +2 -2
  17. package/dist/cli/{mobile-6xe6zysv.js → mobile-fbtq3s5x.js} +1240 -370
  18. package/dist/cli/{remoteMacProtocol-kqvkp0qd.js → remoteMacProtocol-bbramzh6.js} +2 -2
  19. package/dist/cli/{start-nmyy64tr.js → start-5grq7nfr.js} +3 -3
  20. package/dist/mobile/index.js +1340 -571
  21. package/dist/mobile/index.js.map +10 -8
  22. package/dist/mobile/remoteMacAgentEntry.js +302 -255
  23. package/dist/src/mobile/index.d.ts +2 -0
  24. package/dist/src/mobile/iosRelease.d.ts +31 -7
  25. package/dist/src/mobile/iosReleaseAcceptance.d.ts +63 -0
  26. package/dist/src/mobile/iosTestReport.d.ts +1 -0
  27. package/dist/src/mobile/nativeReleaseReadiness.d.ts +5 -0
  28. package/dist/src/mobile/nativeTestReport.d.ts +15 -0
  29. package/dist/src/mobile/releaseDoctor.d.ts +2 -0
  30. package/dist/src/mobile/remoteMacProtocol.d.ts +7 -1
  31. package/dist/src/mobile/shellReleaseReadiness.d.ts +2 -0
  32. package/package.json +2 -1
@@ -7,12 +7,12 @@ import {
7
7
  } from "./index-zh6hhrwy.js";
8
8
  import {
9
9
  start
10
- } from "./index-rvpd4w9t.js";
10
+ } from "./index-mazxtfh4.js";
11
11
  import {
12
12
  ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
13
13
  ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
14
14
  resolveAbsoluteMobileRoute
15
- } from "./index-6ff78meg.js";
15
+ } from "./index-hrzd7e5x.js";
16
16
  import"./index-bzs19w3r.js";
17
17
  import"./index-s2tazax7.js";
18
18
  import {
@@ -30,15 +30,17 @@ import {
30
30
  listAbsoluteRemoteMacProfiles,
31
31
  pairAbsoluteRemoteMac,
32
32
  removeAbsoluteRemoteMacProfile
33
- } from "./index-3sdvzrv2.js";
33
+ } from "./index-7fxrgx5q.js";
34
34
  import {
35
35
  ABSOLUTE_IOS_SIMULATOR_NAME,
36
36
  parseIosSimulators,
37
37
  repairAbsoluteIosDevSession
38
- } from "./index-wpfvpsk7.js";
38
+ } from "./index-5skbydh0.js";
39
39
  import {
40
- buildAbsoluteIosRelease
41
- } from "./index-qxnptvwn.js";
40
+ buildAbsoluteIosRelease,
41
+ requireAbsoluteIosReleaseMetadata,
42
+ resolveAbsoluteIosXcodeProject
43
+ } from "./index-q1d4ns4n.js";
42
44
  import"./index-fc9vxw56.js";
43
45
  import {
44
46
  listLiveInstances
@@ -67,7 +69,7 @@ import {
67
69
  syncAbsoluteExpoWebAssets,
68
70
  unsignedAbsoluteMobileUpdate,
69
71
  writeAbsoluteExpoProject
70
- } from "./index-d5fr1xp3.js";
72
+ } from "./index-0zcebhm4.js";
71
73
  import {
72
74
  buildAbsoluteExpoAndroidWslBundle
73
75
  } from "./index-28nxv57v.js";
@@ -4412,9 +4414,9 @@ var require_sha256 = __commonJS(function(exports, module) {
4412
4414
  var forge = require_forge();
4413
4415
  require_md();
4414
4416
  require_util();
4415
- var sha2563 = module.exports = forge.sha256 = forge.sha256 || {};
4416
- forge.md.sha256 = forge.md.algorithms.sha256 = sha2563;
4417
- sha2563.create = function() {
4417
+ var sha2564 = module.exports = forge.sha256 = forge.sha256 || {};
4418
+ forge.md.sha256 = forge.md.algorithms.sha256 = sha2564;
4419
+ sha2564.create = function() {
4418
4420
  if (!_initialized) {
4419
4421
  _init();
4420
4422
  }
@@ -16776,15 +16778,15 @@ var require_main = __commonJS(function(exports) {
16776
16778
 
16777
16779
  // src/cli/scripts/mobile.ts
16778
16780
  import {
16779
- access as access13,
16780
- mkdir as mkdir13,
16781
- mkdtemp as mkdtemp4,
16782
- readFile as readFile17,
16783
- rm as rm7,
16784
- writeFile as writeFile15
16781
+ access as access14,
16782
+ mkdir as mkdir15,
16783
+ mkdtemp as mkdtemp5,
16784
+ readFile as readFile19,
16785
+ rm as rm8,
16786
+ writeFile as writeFile16
16785
16787
  } from "fs/promises";
16786
16788
  import { createPublicKey as createPublicKey2 } from "crypto";
16787
- import { join as join14, relative as relative12, resolve as resolve13 } from "path";
16789
+ import { join as join16, relative as relative13, resolve as resolve14 } from "path";
16788
16790
  import { createInterface } from "readline/promises";
16789
16791
 
16790
16792
  // src/mobile/nativeDeepLinks.ts
@@ -18454,14 +18456,228 @@ var applyAbsoluteNativeObservability = async (config, platforms = config.platfor
18454
18456
  return { changed: changed.some(Boolean) };
18455
18457
  };
18456
18458
 
18459
+ // src/mobile/nativeReleaseReadiness.ts
18460
+ import { mkdir as mkdir3, readFile as readFile6, readdir as readdir3, rename as rename6, writeFile as writeFile6 } from "fs/promises";
18461
+ import { dirname as dirname3, join as join6 } from "path";
18462
+ var START3 = "// absolutejs:release-readiness:start";
18463
+ var END3 = "// absolutejs:release-readiness:end";
18464
+ var IOS_PLUGIN_START = "// absolutejs:release-readiness-plugin:start";
18465
+ var IOS_PLUGIN_END = "// absolutejs:release-readiness-plugin:end";
18466
+ var ANDROID_PLUGIN3 = "AbsoluteReleaseReadinessPlugin.java";
18467
+ var optionalSource4 = async (path) => {
18468
+ try {
18469
+ return await readFile6(path, "utf8");
18470
+ } catch (error) {
18471
+ if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
18472
+ return null;
18473
+ throw error;
18474
+ }
18475
+ };
18476
+ var writeChanged4 = async (path, source) => {
18477
+ const current = await optionalSource4(path);
18478
+ if (current === source)
18479
+ return false;
18480
+ await mkdir3(dirname3(path), { recursive: true });
18481
+ if (current === null) {
18482
+ await writeFile6(path, source, { flag: "wx" });
18483
+ return true;
18484
+ }
18485
+ const temporary = `${path}.${crypto.randomUUID()}.tmp`;
18486
+ await writeFile6(temporary, source, { flag: "wx" });
18487
+ await rename6(temporary, path);
18488
+ return true;
18489
+ };
18490
+ var activityInRoot = async (root) => {
18491
+ let entries;
18492
+ try {
18493
+ entries = await readdir3(root, { recursive: true });
18494
+ } catch (error) {
18495
+ if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
18496
+ return;
18497
+ throw error;
18498
+ }
18499
+ const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
18500
+ return candidate ? join6(root, candidate) : undefined;
18501
+ };
18502
+ var activityPath = async (config) => {
18503
+ const roots = [
18504
+ join6(config.nativeProjectDirectory, "android/app/src/main/java"),
18505
+ join6(config.nativeProjectDirectory, "android/app/src/main/kotlin")
18506
+ ];
18507
+ const candidates = await Promise.all(roots.map(activityInRoot));
18508
+ const candidate = candidates.find((value) => value !== undefined);
18509
+ if (candidate)
18510
+ return candidate;
18511
+ throw new TypeError("Android MainActivity.java or MainActivity.kt was not found.");
18512
+ };
18513
+ var registrationRegion = (kotlin, generatedMethod) => {
18514
+ if (generatedMethod)
18515
+ return kotlin ? ` ${START3}
18516
+ override fun onCreate(savedInstanceState: android.os.Bundle?) {
18517
+ registerPlugin(AbsoluteReleaseReadinessPlugin::class.java)
18518
+ super.onCreate(savedInstanceState)
18519
+ }
18520
+ ${END3}
18521
+ ` : ` ${START3}
18522
+ @Override
18523
+ public void onCreate(android.os.Bundle savedInstanceState) {
18524
+ registerPlugin(AbsoluteReleaseReadinessPlugin.class);
18525
+ super.onCreate(savedInstanceState);
18526
+ }
18527
+ ${END3}
18528
+ `;
18529
+ return kotlin ? ` ${START3}
18530
+ registerPlugin(AbsoluteReleaseReadinessPlugin::class.java)
18531
+ ${END3}
18532
+ ` : ` ${START3}
18533
+ registerPlugin(AbsoluteReleaseReadinessPlugin.class);
18534
+ ${END3}
18535
+ `;
18536
+ };
18537
+ var injectRegistration = (source) => {
18538
+ const kotlin = /\bfun\s+onCreate\s*\(/u.test(source) || source.includes("BridgeActivity()");
18539
+ const existingStart = source.indexOf(START3);
18540
+ const existingEnd = source.indexOf(END3);
18541
+ if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
18542
+ throw new TypeError("AbsoluteJS release-readiness markers are malformed.");
18543
+ if (existingStart >= 0) {
18544
+ const from = source.lastIndexOf(`
18545
+ `, existingStart) + 1;
18546
+ const newline = source.indexOf(`
18547
+ `, existingEnd + END3.length);
18548
+ const through = newline < 0 ? source.length : newline + 1;
18549
+ const generatedMethod = source.slice(from, through).includes("onCreate(");
18550
+ return `${source.slice(0, from)}${registrationRegion(kotlin, generatedMethod)}${source.slice(through)}`;
18551
+ }
18552
+ const onCreate = kotlin ? source.search(/\boverride\s+fun\s+onCreate\s*\([^)]*\)\s*\{/u) : source.search(/\b(?:public|protected)\s+void\s+onCreate\s*\([^)]*\)\s*\{/u);
18553
+ if (onCreate >= 0) {
18554
+ const brace = source.indexOf("{", onCreate);
18555
+ const insert = source.indexOf(`
18556
+ `, brace) + 1;
18557
+ if (insert <= 0)
18558
+ throw new TypeError("Could not inject Android release readiness into MainActivity.");
18559
+ return `${source.slice(0, insert)}${registrationRegion(kotlin, false)}${source.slice(insert)}`;
18560
+ }
18561
+ const close = source.lastIndexOf("}");
18562
+ if (close < 0)
18563
+ throw new TypeError("Could not find the Android MainActivity class body.");
18564
+ const separator = source[close - 1] === `
18565
+ ` ? "" : `
18566
+ `;
18567
+ return `${source.slice(0, close)}${separator}${registrationRegion(kotlin, true)}${source.slice(close)}`;
18568
+ };
18569
+ var pluginSource = (packageName) => `package ${packageName};
18570
+
18571
+ import android.util.Log;
18572
+ import com.getcapacitor.JSObject;
18573
+ import com.getcapacitor.Plugin;
18574
+ import com.getcapacitor.PluginCall;
18575
+ import com.getcapacitor.PluginMethod;
18576
+ import com.getcapacitor.annotation.CapacitorPlugin;
18577
+
18578
+ @CapacitorPlugin(name = "AbsoluteReleaseReadiness")
18579
+ public final class AbsoluteReleaseReadinessPlugin extends Plugin {
18580
+ @PluginMethod
18581
+ public void ready(PluginCall call) {
18582
+ Log.i("AbsoluteJS", "Capacitor embedded web content ready");
18583
+ call.resolve(new JSObject().put("ready", true));
18584
+ }
18585
+ }
18586
+ `;
18587
+ var iosPluginRegion3 = `${IOS_PLUGIN_START}
18588
+ @objc(AbsoluteReleaseReadinessPlugin)
18589
+ public final class AbsoluteReleaseReadinessPlugin: CAPPlugin, CAPBridgedPlugin {
18590
+ public let identifier = "AbsoluteReleaseReadinessPlugin"
18591
+ public let jsName = "AbsoluteReleaseReadiness"
18592
+ public let pluginMethods: [CAPPluginMethod] = [
18593
+ CAPPluginMethod(name: "ready", returnType: CAPPluginReturnPromise)
18594
+ ]
18595
+
18596
+ @objc public func ready(_ call: CAPPluginCall) {
18597
+ let info = Bundle.main.infoDictionary ?? [:]
18598
+ let version = info["CFBundleShortVersionString"] as? String ?? "unknown"
18599
+ let build = info["CFBundleVersion"] as? String ?? "unknown"
18600
+ NSLog("AbsoluteJS: Capacitor embedded web content ready; version=%@; build=%@", version, build)
18601
+ call.resolve(["ready": true])
18602
+ }
18603
+ }
18604
+ ${IOS_PLUGIN_END}
18605
+ `;
18606
+ var replaceIosPluginRegion = (appDelegate, existingStart, existingEnd) => {
18607
+ const from = appDelegate.lastIndexOf(`
18608
+ `, existingStart) + 1;
18609
+ const newline = appDelegate.indexOf(`
18610
+ `, existingEnd + IOS_PLUGIN_END.length);
18611
+ const through = newline < 0 ? appDelegate.length : newline + 1;
18612
+ return `${appDelegate.slice(0, from)}${iosPluginRegion3}${appDelegate.slice(through)}`;
18613
+ };
18614
+ var configureIos5 = async (config) => {
18615
+ const appDelegatePath = join6(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
18616
+ const capacitorConfigPath = join6(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
18617
+ const appDelegate = await readFile6(appDelegatePath, "utf8");
18618
+ const existingStart = appDelegate.indexOf(IOS_PLUGIN_START);
18619
+ const existingEnd = appDelegate.indexOf(IOS_PLUGIN_END);
18620
+ if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
18621
+ throw new TypeError("AbsoluteJS iOS release-readiness markers are malformed.");
18622
+ const source = existingStart < 0 ? `${appDelegate.trimEnd()}
18623
+
18624
+ ${iosPluginRegion3}` : replaceIosPluginRegion(appDelegate, existingStart, existingEnd);
18625
+ let capacitorConfig;
18626
+ try {
18627
+ capacitorConfig = JSON.parse(await readFile6(capacitorConfigPath, "utf8"));
18628
+ } catch {
18629
+ throw new TypeError("iOS capacitor.config.json is invalid; run Capacitor sync before projecting release readiness.");
18630
+ }
18631
+ if (typeof capacitorConfig !== "object" || capacitorConfig === null)
18632
+ throw new TypeError("iOS capacitor.config.json must contain an object.");
18633
+ const packageClassList = Reflect.get(capacitorConfig, "packageClassList");
18634
+ if (!Array.isArray(packageClassList))
18635
+ throw new TypeError("iOS capacitor.config.json has no packageClassList; run Capacitor sync before projecting release readiness.");
18636
+ const classes = packageClassList.filter((value) => typeof value === "string");
18637
+ const nextConfig = `${JSON.stringify({
18638
+ ...capacitorConfig,
18639
+ packageClassList: [
18640
+ ...new Set([...classes, "AbsoluteReleaseReadinessPlugin"])
18641
+ ]
18642
+ }, null, "\t")}
18643
+ `;
18644
+ const changed = await Promise.all([
18645
+ writeChanged4(appDelegatePath, source),
18646
+ writeChanged4(capacitorConfigPath, nextConfig)
18647
+ ]);
18648
+ return changed.some(Boolean);
18649
+ };
18650
+ var configureAndroid5 = async (config) => {
18651
+ const mainActivityPath = await activityPath(config);
18652
+ const activity = await readFile6(mainActivityPath, "utf8");
18653
+ const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
18654
+ if (!packageName)
18655
+ throw new TypeError("Android MainActivity package declaration was not found.");
18656
+ const pluginPath = join6(config.nativeProjectDirectory, "android/app/src/main/java", packageName.replaceAll(".", "/"), ANDROID_PLUGIN3);
18657
+ const changed = await Promise.all([
18658
+ writeChanged4(mainActivityPath, injectRegistration(activity)),
18659
+ writeChanged4(pluginPath, pluginSource(packageName))
18660
+ ]);
18661
+ return changed.some(Boolean);
18662
+ };
18663
+ var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.platforms) => {
18664
+ if (config.engine !== "capacitor")
18665
+ return { changed: false };
18666
+ const changed = await Promise.all([
18667
+ platforms.includes("android") ? configureAndroid5(config) : false,
18668
+ platforms.includes("ios") ? configureIos5(config) : false
18669
+ ]);
18670
+ return { changed: changed.some(Boolean) };
18671
+ };
18672
+
18457
18673
  // src/mobile/associationFiles.ts
18458
18674
  import {
18459
18675
  access,
18460
- mkdir as mkdir3,
18461
- readFile as readFile6,
18462
- rename as rename6,
18676
+ mkdir as mkdir4,
18677
+ readFile as readFile7,
18678
+ rename as rename7,
18463
18679
  rm as rm3,
18464
- writeFile as writeFile6
18680
+ writeFile as writeFile7
18465
18681
  } from "fs/promises";
18466
18682
  import { resolve } from "path";
18467
18683
  import { Elysia } from "elysia";
@@ -18525,7 +18741,7 @@ var createAbsoluteMobileAssociationDocuments = (config, options = {}) => {
18525
18741
  var writeAtomic = async (path, source) => {
18526
18742
  let current;
18527
18743
  try {
18528
- current = await readFile6(path, "utf8");
18744
+ current = await readFile7(path, "utf8");
18529
18745
  } catch (error) {
18530
18746
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
18531
18747
  throw error;
@@ -18534,8 +18750,8 @@ var writeAtomic = async (path, source) => {
18534
18750
  if (current === source)
18535
18751
  return false;
18536
18752
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
18537
- await writeFile6(temporary, source, { flag: "wx" });
18538
- await rename6(temporary, path);
18753
+ await writeFile7(temporary, source, { flag: "wx" });
18754
+ await rename7(temporary, path);
18539
18755
  return true;
18540
18756
  };
18541
18757
  var exists = async (path) => {
@@ -18550,7 +18766,7 @@ var assertOwnedOutput = async (root) => {
18550
18766
  const path = resolve(root, OWNERSHIP_FILE);
18551
18767
  let ownership;
18552
18768
  try {
18553
- ownership = JSON.parse(await readFile6(path, "utf8"));
18769
+ ownership = JSON.parse(await readFile7(path, "utf8"));
18554
18770
  } catch {
18555
18771
  throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
18556
18772
  }
@@ -18564,12 +18780,12 @@ var publishGeneratedDirectory = async (temporary, root) => {
18564
18780
  await assertOwnedOutput(root);
18565
18781
  const backup = `${root}.${crypto.randomUUID()}.previous`;
18566
18782
  if (hasCurrent)
18567
- await rename6(root, backup);
18783
+ await rename7(root, backup);
18568
18784
  try {
18569
- await rename6(temporary, root);
18785
+ await rename7(temporary, root);
18570
18786
  } catch (error) {
18571
18787
  if (hasCurrent)
18572
- await rename6(backup, root);
18788
+ await rename7(backup, root);
18573
18789
  throw error;
18574
18790
  }
18575
18791
  if (hasCurrent)
@@ -18577,7 +18793,7 @@ var publishGeneratedDirectory = async (temporary, root) => {
18577
18793
  };
18578
18794
  var materializeHost = async (root, host, files) => {
18579
18795
  const directory = resolve(root, host, ".well-known");
18580
- await mkdir3(directory, { recursive: true });
18796
+ await mkdir4(directory, { recursive: true });
18581
18797
  return Promise.all(files.map(async ([name, document2]) => {
18582
18798
  const path = resolve(directory, name);
18583
18799
  await writeAtomic(path, `${JSON.stringify(document2, null, 2)}
@@ -18615,7 +18831,7 @@ var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory)
18615
18831
  if (documents.apple) {
18616
18832
  files.push(["apple-app-site-association", documents.apple]);
18617
18833
  }
18618
- await mkdir3(temporary, { recursive: true });
18834
+ await mkdir4(temporary, { recursive: true });
18619
18835
  try {
18620
18836
  const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host) => materializeHost(temporary, host, files)))).flat();
18621
18837
  await writeAtomic(resolve(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
@@ -18660,8 +18876,8 @@ var verifyAbsoluteMobileAssociationFiles = async (config, request = globalThis.f
18660
18876
  };
18661
18877
 
18662
18878
  // src/mobile/androidWebView.ts
18663
- import { mkdir as mkdir4, writeFile as writeFile7 } from "fs/promises";
18664
- import { dirname as dirname3, resolve as resolve2 } from "path";
18879
+ import { mkdir as mkdir5, writeFile as writeFile8 } from "fs/promises";
18880
+ import { dirname as dirname4, resolve as resolve2 } from "path";
18665
18881
  var CDP_COMMAND_TIMEOUT_MS = 1e4;
18666
18882
  var WEBVIEW_ATTACH_TIMEOUT_MS = 30000;
18667
18883
  var WEBVIEW_POLL_MS = 200;
@@ -18955,8 +19171,8 @@ var createSession = (options, capture, socket, hostPort, target, connection) =>
18955
19171
  throw new Error("Android WebView screenshot returned no image data.");
18956
19172
  }
18957
19173
  const absolutePath = resolve2(path);
18958
- await mkdir4(dirname3(absolutePath), { recursive: true });
18959
- await writeFile7(absolutePath, Buffer.from(data, "base64"));
19174
+ await mkdir5(dirname4(absolutePath), { recursive: true });
19175
+ await writeFile8(absolutePath, Buffer.from(data, "base64"));
18960
19176
  return absolutePath;
18961
19177
  },
18962
19178
  tap: async (coordinateX, coordinateY) => {
@@ -19103,13 +19319,13 @@ var waitForAbsoluteAndroidHmrApply = async (session, options) => {
19103
19319
  };
19104
19320
 
19105
19321
  // src/mobile/releaseDoctor.ts
19106
- import { access as access4, readFile as readFile8, readdir as readdir3 } from "fs/promises";
19107
- import { dirname as dirname5, extname, join as join7, relative as relative3 } from "path";
19322
+ import { access as access4, readFile as readFile9, readdir as readdir4 } from "fs/promises";
19323
+ import { dirname as dirname6, extname, join as join8, relative as relative3 } from "path";
19108
19324
 
19109
19325
  // src/mobile/mobileBundleInspection.ts
19110
19326
  import { createHash } from "crypto";
19111
- import { access as access2, readFile as readFile7, stat } from "fs/promises";
19112
- import { join as join6, relative, resolve as resolve3 } from "path";
19327
+ import { access as access2, readFile as readFile8, stat } from "fs/promises";
19328
+ import { join as join7, relative, resolve as resolve3 } from "path";
19113
19329
  var MOBILE_FRAMEWORKS = new Set([
19114
19330
  "angular",
19115
19331
  "ember",
@@ -19134,7 +19350,7 @@ var pathExists = async (path) => {
19134
19350
  }
19135
19351
  };
19136
19352
  var readObject = async (path) => {
19137
- const value = JSON.parse(await readFile7(path, "utf8"));
19353
+ const value = JSON.parse(await readFile8(path, "utf8"));
19138
19354
  if (!isObject(value))
19139
19355
  throw new TypeError("JSON root must be an object.");
19140
19356
  return value;
@@ -19162,14 +19378,14 @@ var requireBundleFile = async (root, value, field, expectedHash) => {
19162
19378
  if (expectedHash !== undefined) {
19163
19379
  if (!SHA256_PATTERN.test(expectedHash))
19164
19380
  throw new TypeError(`${field} has an invalid SHA-256 digest.`);
19165
- const actual = createHash("sha256").update(await readFile7(path)).digest("hex");
19381
+ const actual = createHash("sha256").update(await readFile8(path)).digest("hex");
19166
19382
  if (actual !== expectedHash)
19167
19383
  throw new TypeError(`${field} failed its SHA-256 integrity check.`);
19168
19384
  }
19169
19385
  return portable;
19170
19386
  };
19171
19387
  var inspectAbsoluteMobileBundle = async (config, projectRoot) => {
19172
- const manifestPath = join6(config.bundleDirectory, "absolute-mobile-manifest.json");
19388
+ const manifestPath = join7(config.bundleDirectory, "absolute-mobile-manifest.json");
19173
19389
  const manifest = portablePath(projectRoot, manifestPath);
19174
19390
  if (!await pathExists(manifestPath))
19175
19391
  return { manifest, status: "missing" };
@@ -19266,8 +19482,8 @@ import {
19266
19482
  createPublicKey,
19267
19483
  X509Certificate
19268
19484
  } from "crypto";
19269
- import { access as access3, mkdir as mkdir5 } from "fs/promises";
19270
- import { dirname as dirname4, isAbsolute, relative as relative2, resolve as resolve4, sep } from "path";
19485
+ import { access as access3, mkdir as mkdir6 } from "fs/promises";
19486
+ import { dirname as dirname5, isAbsolute, relative as relative2, resolve as resolve4, sep } from "path";
19271
19487
  import { pathToFileURL } from "url";
19272
19488
  import { Elysia as Elysia2 } from "elysia";
19273
19489
  var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1;
@@ -19484,7 +19700,7 @@ var writeAbsoluteMobileUpdateRegistry = async (options) => {
19484
19700
  return;
19485
19701
  });
19486
19702
  }
19487
- await mkdir5(dirname4(path), { recursive: true });
19703
+ await mkdir6(dirname5(path), { recursive: true });
19488
19704
  await Bun.write(path, renderAbsoluteMobileUpdateRegistry({
19489
19705
  ...options.health ? { health: options.health } : {},
19490
19706
  ...options.rollout ? { rollout: options.rollout } : {},
@@ -19527,14 +19743,14 @@ var inspectReleaseAsset = async (path, isDirectory, isFile) => {
19527
19743
  return findHmrAsset(path);
19528
19744
  if (!isFile || !RELEASE_ASSET_EXTENSIONS.has(extname(path)))
19529
19745
  return;
19530
- const source = await readFile8(path, "utf8");
19746
+ const source = await readFile9(path, "utf8");
19531
19747
  return HMR_ASSET_PATTERN.test(source) ? path : undefined;
19532
19748
  };
19533
19749
  var findHmrAsset = async (root) => {
19534
19750
  if (!await pathExists2(root))
19535
19751
  return;
19536
- const entries = await readdir3(root, { withFileTypes: true });
19537
- const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(join7(root, entry.name), entry.isDirectory(), entry.isFile())));
19752
+ const entries = await readdir4(root, { withFileTypes: true });
19753
+ const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(join8(root, entry.name), entry.isDirectory(), entry.isFile())));
19538
19754
  return matches.find((match) => match !== undefined);
19539
19755
  };
19540
19756
  var pass = (id, detail, path) => ({ detail, id, path, status: "pass" });
@@ -19564,7 +19780,7 @@ var mobileUpdateServerCheck = async (config, projectRoot) => {
19564
19780
  };
19565
19781
  var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19566
19782
  var readJsonObject = async (path) => {
19567
- const value = JSON.parse(await readFile8(path, "utf8"));
19783
+ const value = JSON.parse(await readFile9(path, "utf8"));
19568
19784
  if (typeof value !== "object" || value === null || Array.isArray(value))
19569
19785
  throw new TypeError("JSON root must be an object.");
19570
19786
  return Object.fromEntries(Object.entries(value));
@@ -19583,7 +19799,7 @@ var packageDeclarations = (manifest) => {
19583
19799
  };
19584
19800
  var versionCore = (version) => version.split("-")[0]?.split(".").slice(0, 2).join(".");
19585
19801
  var capacitorVersionCheck = async (config, projectRoot) => {
19586
- const manifestPath = join7(projectRoot, "package.json");
19802
+ const manifestPath = join8(projectRoot, "package.json");
19587
19803
  try {
19588
19804
  const manifest = await readJsonObject(manifestPath);
19589
19805
  const declarations = packageDeclarations(manifest);
@@ -19596,7 +19812,7 @@ var capacitorVersionCheck = async (config, projectRoot) => {
19596
19812
  const declared = declarations.get(name);
19597
19813
  if (!declared || !EXACT_VERSION_PATTERN.test(declared))
19598
19814
  throw new TypeError(`${name} must be a direct exact dependency.`);
19599
- const installed = await readJsonObject(join7(projectRoot, "node_modules", name, "package.json"));
19815
+ const installed = await readJsonObject(await findAbsoluteInstalledPackageManifest(projectRoot, name));
19600
19816
  if (installed.version !== declared)
19601
19817
  throw new TypeError(`${name} does not match its installed version.`);
19602
19818
  return declared;
@@ -19619,7 +19835,7 @@ var satisfiesGeneratedVersion = (declared, installed) => {
19619
19835
  return actual[0] === expected[0] && actual[1] === expected[1] && (actual[2] ?? NOT_FOUND3) >= (expected[2] ?? 0);
19620
19836
  };
19621
19837
  var expoVersionCheck = async (config) => {
19622
- const manifestPath = join7(config.nativeProjectDirectory, "package.json");
19838
+ const manifestPath = join8(config.nativeProjectDirectory, "package.json");
19623
19839
  try {
19624
19840
  const manifest = await readJsonObject(manifestPath);
19625
19841
  const declarations = packageDeclarations(manifest);
@@ -19629,13 +19845,13 @@ var expoVersionCheck = async (config) => {
19629
19845
  throw new TypeError(`Generated Expo project is missing ${missingRequired}.`);
19630
19846
  const installedVersions = await Promise.all([...declarations].map(async ([name, declared]) => ({
19631
19847
  declared,
19632
- installed: await readJsonObject(join7(config.nativeProjectDirectory, "node_modules", name, "package.json")),
19848
+ installed: await readJsonObject(join8(config.nativeProjectDirectory, "node_modules", name, "package.json")),
19633
19849
  name
19634
19850
  })));
19635
19851
  const mismatch = installedVersions.find(({ declared, installed }) => typeof installed.version !== "string" || !satisfiesGeneratedVersion(declared, installed.version));
19636
19852
  if (mismatch)
19637
19853
  throw new TypeError(`Generated Expo dependency ${mismatch.name}@${mismatch.declared} does not match its installed version.`);
19638
- if (!await pathExists2(join7(config.nativeProjectDirectory, "bun.lock")))
19854
+ if (!await pathExists2(join8(config.nativeProjectDirectory, "bun.lock")))
19639
19855
  throw new TypeError("Generated Expo dependency lockfile is missing.");
19640
19856
  return pass("mobile.expo-versions", `Generated Expo SDK dependencies are pinned, installed, and locked (${declarations.get("expo")}).`, manifestPath);
19641
19857
  } catch (error) {
@@ -19643,9 +19859,9 @@ var expoVersionCheck = async (config) => {
19643
19859
  }
19644
19860
  };
19645
19861
  var dependencyLockAt = async (directory) => (await Promise.all(LOCK_FILES.map(async (name) => ({
19646
- exists: await pathExists2(join7(directory, name)),
19862
+ exists: await pathExists2(join8(directory, name)),
19647
19863
  name,
19648
- path: join7(directory, name)
19864
+ path: join8(directory, name)
19649
19865
  })))).find(({ exists: exists2 }) => exists2);
19650
19866
  var declaredWorkspacePatterns = (declared) => {
19651
19867
  if (Array.isArray(declared))
@@ -19655,7 +19871,7 @@ var declaredWorkspacePatterns = (declared) => {
19655
19871
  return [];
19656
19872
  };
19657
19873
  var ownsWorkspace = async (directory, projectRoot) => {
19658
- const manifest = await readJsonObject(join7(directory, "package.json")).catch(() => {
19874
+ const manifest = await readJsonObject(join8(directory, "package.json")).catch(() => {
19659
19875
  return;
19660
19876
  });
19661
19877
  const workspacePath = relative3(directory, projectRoot).replace(/\\/gu, "/");
@@ -19666,7 +19882,7 @@ var ancestorDirectories = (projectRoot) => {
19666
19882
  let directory = projectRoot;
19667
19883
  while (true) {
19668
19884
  directories.push(directory);
19669
- const parent = dirname5(directory);
19885
+ const parent = dirname6(directory);
19670
19886
  if (parent === directory)
19671
19887
  return directories;
19672
19888
  directory = parent;
@@ -19681,6 +19897,18 @@ var findAbsoluteDependencyLock = async (projectRoot) => {
19681
19897
  }));
19682
19898
  return candidates.find((candidate) => candidate !== undefined);
19683
19899
  };
19900
+ var findAbsoluteInstalledPackageManifest = async (projectRoot, name) => {
19901
+ const candidates = await Promise.all(ancestorDirectories(projectRoot).map(async (directory, index) => {
19902
+ if (index > 0 && !await ownsWorkspace(directory, projectRoot))
19903
+ return;
19904
+ const candidate = join8(directory, "node_modules", name, "package.json");
19905
+ return await pathExists2(candidate) ? candidate : undefined;
19906
+ }));
19907
+ const installed = candidates.find((candidate) => candidate !== undefined);
19908
+ if (installed)
19909
+ return installed;
19910
+ throw new TypeError(`${name} is not installed for this application.`);
19911
+ };
19684
19912
  var dependencyLockCheck = async (projectRoot) => {
19685
19913
  const present = await findAbsoluteDependencyLock(projectRoot);
19686
19914
  return present ? pass("mobile.dependency-lock", `Dependency graph is locked by ${present.name}.`, present.path) : fail("mobile.dependency-lock", "No supported dependency lockfile is present.", projectRoot, "Install dependencies with the project package manager and commit its lockfile before release.");
@@ -19725,7 +19953,7 @@ var capacitorConfigReleaseCheck = async (nativeConfigPath) => {
19725
19953
  if (!await pathExists2(nativeConfigPath)) {
19726
19954
  return fail("android.capacitor-config", "The generated Android Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync android` before release validation.");
19727
19955
  }
19728
- const unsafe = isUnsafeCapacitorConfig(await readFile8(nativeConfigPath, "utf8"));
19956
+ const unsafe = isUnsafeCapacitorConfig(await readFile9(nativeConfigPath, "utf8"));
19729
19957
  return unsafe ? fail("android.capacitor-config", "Android Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync android`; do not ship development transport overrides.") : pass("android.capacitor-config", "Android Capacitor config contains no development transport overrides.", nativeConfigPath);
19730
19958
  };
19731
19959
  var capacitorIdentityCheck = async (config, platform, nativeConfigPath) => {
@@ -19742,12 +19970,12 @@ var manifestReleaseCheck = async (manifestPath) => {
19742
19970
  if (!await pathExists2(manifestPath)) {
19743
19971
  return fail("android.cleartext", "The Android manifest is missing.", manifestPath, "Run `absolute mobile sync android` before release validation.");
19744
19972
  }
19745
- const source = await readFile8(manifestPath, "utf8");
19973
+ const source = await readFile9(manifestPath, "utf8");
19746
19974
  const cleartext = /android:usesCleartextTraffic=["']true["']/u.test(source);
19747
19975
  const networkConfigName = source.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
19748
- const networkConfigPath = networkConfigName ? join7(dirname5(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
19976
+ const networkConfigPath = networkConfigName ? join8(dirname6(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
19749
19977
  const developmentTrustReference = /android:networkSecurityConfig=["']@xml\/absolutejs_dev_network_security["']/u.test(source);
19750
- const developmentTrustContents = networkConfigPath ? await readFile8(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
19978
+ const developmentTrustContents = networkConfigPath ? await readFile9(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
19751
19979
  const developmentTrust = developmentTrustReference || developmentTrustContents;
19752
19980
  return cleartext || developmentTrust ? fail("android.cleartext", developmentTrust ? "Android still references the AbsoluteJS development certificate authority." : "Android explicitly permits cleartext traffic.", manifestPath, "Run `absolute mobile sync android`; do not ship development transport or trust overrides.") : pass("android.cleartext", "Android does not explicitly permit cleartext traffic.", manifestPath);
19753
19981
  };
@@ -19758,13 +19986,13 @@ var hmrAssetsReleaseCheck = async (publicRoot) => {
19758
19986
  var embeddedBundleReleaseCheck = async (config, projectRoot, platform, publicRoot) => {
19759
19987
  const inspection = await inspectAbsoluteMobileBundle({ ...config, bundleDirectory: publicRoot }, projectRoot);
19760
19988
  if (inspection.status === "valid")
19761
- return pass(`${platform}.bundle-integrity`, `Packaged mobile manifest, runtime, routes, and ${inspection.pageCount ?? 0} page asset(s) passed structural and SHA-256 validation.`, join7(publicRoot, "absolute-mobile-manifest.json"));
19762
- return fail(`${platform}.bundle-integrity`, inspection.status === "missing" ? "The packaged mobile manifest is missing." : `The packaged mobile bundle is invalid: ${inspection.issue ?? "unknown validation error"}`, join7(publicRoot, "absolute-mobile-manifest.json"), "Rebuild the production mobile bundle and run Capacitor sync for this platform.");
19989
+ return pass(`${platform}.bundle-integrity`, `Packaged mobile manifest, runtime, routes, and ${inspection.pageCount ?? 0} page asset(s) passed structural and SHA-256 validation.`, join8(publicRoot, "absolute-mobile-manifest.json"));
19990
+ return fail(`${platform}.bundle-integrity`, inspection.status === "missing" ? "The packaged mobile manifest is missing." : `The packaged mobile bundle is invalid: ${inspection.issue ?? "unknown validation error"}`, join8(publicRoot, "absolute-mobile-manifest.json"), "Rebuild the production mobile bundle and run Capacitor sync for this platform.");
19763
19991
  };
19764
19992
  var contentSecurityPolicyCheck = async (config, platform, publicRoot) => {
19765
- const path = join7(publicRoot, "index.html");
19993
+ const path = join8(publicRoot, "index.html");
19766
19994
  try {
19767
- const source = await readFile8(path, "utf8");
19995
+ const source = await readFile9(path, "utf8");
19768
19996
  const requirements = [
19769
19997
  "Content-Security-Policy",
19770
19998
  "default-src 'self'",
@@ -19793,12 +20021,12 @@ var validateExpoCodeSigningProjection = async (config, expo) => {
19793
20021
  return;
19794
20022
  if (!generatedUpdates || generatedUpdates.codeSigningCertificate !== "./certs/absolute-mobile-update.pem" || !isRecord3(generatedUpdates.codeSigningMetadata) || generatedUpdates.codeSigningMetadata.keyid !== configuredSigning.keyId || generatedUpdates.codeSigningMetadata.alg !== configuredSigning.algorithm)
19795
20023
  throw new TypeError("Generated Expo code-signing metadata does not match mobile config.");
19796
- const generatedCertificate = await readFile8(join7(config.nativeProjectDirectory, "certs", "absolute-mobile-update.pem"), "utf8");
20024
+ const generatedCertificate = await readFile9(join8(config.nativeProjectDirectory, "certs", "absolute-mobile-update.pem"), "utf8");
19797
20025
  if (generatedCertificate !== configuredSigning.certificatePem)
19798
20026
  throw new TypeError("Generated Expo code-signing certificate does not match mobile config.");
19799
20027
  };
19800
20028
  var expoApplicationConfigCheck = async (config, projectRoot) => {
19801
- const path = join7(config.nativeProjectDirectory, "app.json");
20029
+ const path = join8(config.nativeProjectDirectory, "app.json");
19802
20030
  try {
19803
20031
  const root = await readJsonObject(path);
19804
20032
  if (!isRecord3(root.expo))
@@ -19821,12 +20049,12 @@ var expoApplicationConfigCheck = async (config, projectRoot) => {
19821
20049
  }
19822
20050
  };
19823
20051
  var expoEmbeddedAssetsCheck = async (config) => {
19824
- const generated = join7(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
19825
- const assetsRoot = join7(config.nativeProjectDirectory, "assets", "absolute");
20052
+ const generated = join8(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
20053
+ const assetsRoot = join8(config.nativeProjectDirectory, "assets", "absolute");
19826
20054
  try {
19827
20055
  const [source, manifest] = await Promise.all([
19828
- readFile8(generated, "utf8"),
19829
- readJsonObject(join7(config.bundleDirectory, "absolute-mobile-manifest.json"))
20056
+ readFile9(generated, "utf8"),
20057
+ readJsonObject(join8(config.bundleDirectory, "absolute-mobile-manifest.json"))
19830
20058
  ]);
19831
20059
  if (source.includes("embedded AbsoluteJS bundle is unavailable") || typeof manifest.appBuild !== "string" || !source.includes(JSON.stringify(manifest.appBuild)) || !source.includes(JSON.stringify(config.productionOrigin))) {
19832
20060
  throw new TypeError("Generated Expo assets do not contain the prepared production release identity.");
@@ -19841,7 +20069,7 @@ var expoEmbeddedAssetsCheck = async (config) => {
19841
20069
  }))).sort();
19842
20070
  if (sourceFiles.length === 0 || embeddedFiles.length !== 1)
19843
20071
  throw new TypeError("Generated Expo asset count does not match the prepared mobile bundle.");
19844
- const contents = await Promise.all(sourceFiles.map((path) => readFile8(join7(config.bundleDirectory, path))));
20072
+ const contents = await Promise.all(sourceFiles.map((path) => readFile9(join8(config.bundleDirectory, path))));
19845
20073
  let offset = 0;
19846
20074
  const entries = sourceFiles.map((path, index) => {
19847
20075
  const entry = {
@@ -19852,7 +20080,7 @@ var expoEmbeddedAssetsCheck = async (config) => {
19852
20080
  offset += entry.length;
19853
20081
  return entry;
19854
20082
  });
19855
- const archive = await readFile8(join7(assetsRoot, embeddedFiles[0] ?? ""));
20083
+ const archive = await readFile9(join8(assetsRoot, embeddedFiles[0] ?? ""));
19856
20084
  if (!archive.equals(Buffer.concat(contents)) || entries.some((entry) => !source.includes(JSON.stringify(entry))))
19857
20085
  throw new TypeError("Generated Expo asset bytes differ from the prepared mobile bundle.");
19858
20086
  return pass("expo.bundle-projection", `Expo embeds the complete signed mobile bundle as one opaque archive containing ${sourceFiles.length} file(s).`, generated);
@@ -19864,20 +20092,20 @@ var sourceFiles = async (root, extensions) => {
19864
20092
  if (!await pathExists2(root))
19865
20093
  return [];
19866
20094
  const files = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: root, onlyFiles: true }));
19867
- return files.filter((file) => extensions.has(extname(file))).map((file) => join7(root, file));
20095
+ return files.filter((file) => extensions.has(extname(file))).map((file) => join8(root, file));
19868
20096
  };
19869
20097
  var containsPattern = async (paths, pattern) => {
19870
- const sources = await Promise.all(paths.map((path) => readFile8(path, "utf8")));
20098
+ const sources = await Promise.all(paths.map((path) => readFile9(path, "utf8")));
19871
20099
  const index = sources.findIndex((source) => pattern.test(source));
19872
20100
  return index === NOT_FOUND3 ? undefined : paths[index];
19873
20101
  };
19874
20102
  var androidNativeSecurityCheck = async (androidRoot) => {
19875
- const manifestPath = join7(androidRoot, "app/src/main/AndroidManifest.xml");
20103
+ const manifestPath = join8(androidRoot, "app/src/main/AndroidManifest.xml");
19876
20104
  try {
19877
- const manifest = await readFile8(manifestPath, "utf8");
20105
+ const manifest = await readFile9(manifestPath, "utf8");
19878
20106
  if (/android:debuggable=["']true["']/u.test(manifest))
19879
20107
  throw new TypeError("Android release manifest explicitly enables application debugging.");
19880
- const sources = await sourceFiles(join7(androidRoot, "app/src/main"), new Set([".java", ".kt"]));
20108
+ const sources = await sourceFiles(join8(androidRoot, "app/src/main"), new Set([".java", ".kt"]));
19881
20109
  const debugSource = await containsPattern(sources, /setWebContentsDebuggingEnabled\s*\(\s*true\s*\)/u);
19882
20110
  if (debugSource)
19883
20111
  return fail("android.native-debugging", "Android application source unconditionally enables WebView debugging.", debugSource, "Remove the unconditional WebView debugging call; use the platform debug-build behavior during development.");
@@ -19887,7 +20115,7 @@ var androidNativeSecurityCheck = async (androidRoot) => {
19887
20115
  }
19888
20116
  };
19889
20117
  var androidExportedComponentsCheck = async (manifestPath) => {
19890
- const source = await readFile8(manifestPath, "utf8").catch(() => "");
20118
+ const source = await readFile9(manifestPath, "utf8").catch(() => "");
19891
20119
  const exported = [
19892
20120
  ...source.matchAll(/<(?:activity|activity-alias|provider|receiver|service)\b[^>]*>/giu)
19893
20121
  ].map(([tag]) => tag).filter((tag) => /android:exported=["']true["']/iu.test(tag)).map((tag) => tag.match(/android:name=["']([^"']+)["']/iu)?.[1]).filter((name) => Boolean(name) && name !== ".MainActivity");
@@ -19895,7 +20123,7 @@ var androidExportedComponentsCheck = async (manifestPath) => {
19895
20123
  };
19896
20124
  var androidDeepLinkProjectionCheck = async (config, manifestPath) => {
19897
20125
  try {
19898
- const source = await readFile8(manifestPath, "utf8");
20126
+ const source = await readFile9(manifestPath, "utf8");
19899
20127
  const hasWebHost = (host) => [...source.matchAll(/<data\b[^>]*>/giu)].some(([tag]) => tag.includes('android:scheme="https"') && tag.includes(`android:host="${host}"`));
19900
20128
  if (!source.includes('android:autoVerify="true"') || !source.includes("android.intent.category.BROWSABLE") || config.deepLinkHosts.some((host) => !hasWebHost(host)) || config.deepLinkScheme && !source.includes(`android:scheme="${config.deepLinkScheme}"`))
19901
20129
  throw new TypeError("Android App Link or custom-scheme projection does not match mobile config.");
@@ -19909,15 +20137,15 @@ var nativeUpdateWatchdogCheck = async (config, platform) => {
19909
20137
  return;
19910
20138
  const timeout = String(config.updates.bootTimeoutMs);
19911
20139
  if (platform === "ios") {
19912
- const path = join7(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
19913
- const configPath = join7(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
20140
+ const path = join8(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
20141
+ const configPath = join8(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
19914
20142
  const [source, nativeConfig] = await Promise.all([
19915
- readFile8(path, "utf8").catch(() => ""),
19916
- readFile8(configPath, "utf8").catch(() => "")
20143
+ readFile9(path, "utf8").catch(() => ""),
20144
+ readFile9(configPath, "utf8").catch(() => "")
19917
20145
  ]);
19918
20146
  return source.includes("AbsoluteMobileUpdateWatchdogPlugin") && source.includes("recoverInterruptedBoot()") && source.includes(`.milliseconds(${timeout})`) && source.includes("quarantinedReleases") && nativeConfig.includes("AbsoluteMobileUpdateWatchdogPlugin") ? pass("ios.update-watchdog", `The native update boot watchdog is projected with a ${timeout}ms deadline.`, path) : fail("ios.update-watchdog", "The iOS update boot watchdog does not match mobile config.", path, "Run `absolute mobile sync ios` before building the release.");
19919
20147
  }
19920
- const sourceRoot = join7(config.nativeProjectDirectory, "android/app/src/main");
20148
+ const sourceRoot = join8(config.nativeProjectDirectory, "android/app/src/main");
19921
20149
  const sources = await sourceFiles(sourceRoot, new Set([".java", ".kt"]));
19922
20150
  const [activity, plugin] = await Promise.all([
19923
20151
  containsPattern(sources, /recoverInterruptedBoot\s*\(\s*this\s*\)/u),
@@ -19930,27 +20158,27 @@ var nativeObservabilityProjectionCheck = async (config, platform) => {
19930
20158
  return;
19931
20159
  const nativeRoot = config.nativeProjectDirectory;
19932
20160
  if (config.engine === "expo") {
19933
- const moduleRoot = join7(nativeRoot, "modules", "absolute-mobile-observability");
19934
- const configPath = join7(moduleRoot, "expo-module.config.json");
19935
- const sourcePath = platform === "android" ? join7(moduleRoot, "android/src/main/java/expo/modules/absolutemobileobservability/AbsoluteMobileObservabilityModule.kt") : join7(moduleRoot, "ios/AbsoluteMobileObservabilityModule.swift");
20161
+ const moduleRoot = join8(nativeRoot, "modules", "absolute-mobile-observability");
20162
+ const configPath = join8(moduleRoot, "expo-module.config.json");
20163
+ const sourcePath = platform === "android" ? join8(moduleRoot, "android/src/main/java/expo/modules/absolutemobileobservability/AbsoluteMobileObservabilityModule.kt") : join8(moduleRoot, "ios/AbsoluteMobileObservabilityModule.swift");
19936
20164
  const [moduleConfig, source] = await Promise.all([
19937
- readFile8(configPath, "utf8").catch(() => ""),
19938
- readFile8(sourcePath, "utf8").catch(() => "")
20165
+ readFile9(configPath, "utf8").catch(() => ""),
20166
+ readFile9(sourcePath, "utf8").catch(() => "")
19939
20167
  ]);
19940
20168
  const valid = moduleConfig.includes("AbsoluteMobileObservabilityModule") && source.includes("AbsoluteMobileObservability") && (platform === "android" ? source.includes("getHistoricalProcessExitReasons") : source.includes("MXMetricManagerSubscriber"));
19941
20169
  return valid ? pass(`${platform}.native-observability`, `The Expo ${platform} native process collector is projected and autolinkable.`, sourcePath) : fail(`${platform}.native-observability`, `The Expo ${platform} native process collector is missing or incomplete.`, sourcePath, `Run \`absolute mobile build ${platform}\` before building the release.`);
19942
20170
  }
19943
20171
  if (platform === "ios") {
19944
- const delegatePath = join7(nativeRoot, "ios/App/App/AppDelegate.swift");
19945
- const configPath = join7(nativeRoot, "ios/App/App/capacitor.config.json");
20172
+ const delegatePath = join8(nativeRoot, "ios/App/App/AppDelegate.swift");
20173
+ const configPath = join8(nativeRoot, "ios/App/App/capacitor.config.json");
19946
20174
  const [delegate, nativeConfig] = await Promise.all([
19947
- readFile8(delegatePath, "utf8").catch(() => ""),
19948
- readFile8(configPath, "utf8").catch(() => "")
20175
+ readFile9(delegatePath, "utf8").catch(() => ""),
20176
+ readFile9(configPath, "utf8").catch(() => "")
19949
20177
  ]);
19950
20178
  const valid = delegate.includes("AbsoluteMobileObservabilityPlugin") && delegate.includes("MXMetricManagerSubscriber") && nativeConfig.includes("AbsoluteMobileObservabilityPlugin");
19951
20179
  return valid ? pass("ios.native-observability", "The iOS MetricKit native process collector is projected and registered.", delegatePath) : fail("ios.native-observability", "The iOS MetricKit native process collector is missing or unregistered.", delegatePath, "Run `absolute mobile sync ios` before building the release.");
19952
20180
  }
19953
- const sourceRoot = join7(nativeRoot, "android/app/src/main");
20181
+ const sourceRoot = join8(nativeRoot, "android/app/src/main");
19954
20182
  const sources = await sourceFiles(sourceRoot, new Set([".java", ".kt"]));
19955
20183
  const [registration, plugin] = await Promise.all([
19956
20184
  containsPattern(sources, /registerPlugin\s*\(\s*AbsoluteMobileObservabilityPlugin(?:::class\.java|\.class)\s*\)/u),
@@ -19971,14 +20199,14 @@ var iosNativeSecurityCheck = async (iosRoot) => {
19971
20199
  return pass("ios.native-debugging", "iOS source does not enable release debugger attachment or WebView inspection.", entitlementPaths[0] ?? iosRoot);
19972
20200
  };
19973
20201
  var iosDeepLinkProjectionCheck = async (config, iosRoot) => {
19974
- const infoPath = join7(iosRoot, "App/App/Info.plist");
19975
- const entitlementsPath = join7(iosRoot, "App/AbsoluteJS.entitlements");
19976
- const projectPath2 = join7(iosRoot, "App/App.xcodeproj/project.pbxproj");
20202
+ const infoPath = join8(iosRoot, "App/App/Info.plist");
20203
+ const entitlementsPath = join8(iosRoot, "App/AbsoluteJS.entitlements");
20204
+ const projectPath2 = join8(iosRoot, "App/App.xcodeproj/project.pbxproj");
19977
20205
  try {
19978
20206
  const [info, entitlements, project] = await Promise.all([
19979
- readFile8(infoPath, "utf8"),
19980
- readFile8(entitlementsPath, "utf8"),
19981
- readFile8(projectPath2, "utf8")
20207
+ readFile9(infoPath, "utf8"),
20208
+ readFile9(entitlementsPath, "utf8"),
20209
+ readFile9(projectPath2, "utf8")
19982
20210
  ]);
19983
20211
  if (config.deepLinkScheme && (!info.includes("<key>CFBundleURLTypes</key>") || !info.includes(`<string>${config.deepLinkScheme}</string>`)))
19984
20212
  throw new TypeError("iOS custom URL scheme does not match mobile config.");
@@ -19994,7 +20222,7 @@ var iosDeepLinkProjectionCheck = async (config, iosRoot) => {
19994
20222
  var syncSchemaReleaseCheck = (projectRoot) => {
19995
20223
  if (!projectUsesAbsoluteSync(projectRoot))
19996
20224
  return;
19997
- const manifestPath = join7(projectRoot, "package.json");
20225
+ const manifestPath = join8(projectRoot, "package.json");
19998
20226
  try {
19999
20227
  const schema = discoverAbsoluteSyncSchema(projectRoot);
20000
20228
  const versions = schema.components.map((component) => `${component.id}@${component.version}`).join(", ");
@@ -20024,8 +20252,8 @@ var IOS_USAGE_KEYS = {
20024
20252
  var androidDevicePermissionCheck = async (config, permissions) => {
20025
20253
  if (!config.platforms.includes("android") || permissions.length === 0)
20026
20254
  return;
20027
- const path = join7(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
20028
- const source = await readFile8(path, "utf8");
20255
+ const path = join8(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
20256
+ const source = await readFile9(path, "utf8");
20029
20257
  const missing = permissions.filter((permission) => !source.includes(`android:name="${permission}"`) && !source.includes(`android:name='${permission}'`));
20030
20258
  if (missing.length === 0)
20031
20259
  return;
@@ -20034,9 +20262,9 @@ var androidDevicePermissionCheck = async (config, permissions) => {
20034
20262
  var iosDevicePermissionCheck = async (config, purposes) => {
20035
20263
  if (!config.platforms.includes("ios") || purposes.length === 0)
20036
20264
  return;
20037
- const iosRoot = join7(config.nativeProjectDirectory, "ios");
20038
- const path = config.engine === "expo" ? await uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist") : join7(iosRoot, "App/App/Info.plist");
20039
- const source = await readFile8(path, "utf8");
20265
+ const iosRoot = join8(config.nativeProjectDirectory, "ios");
20266
+ const path = config.engine === "expo" ? await uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist") : join8(iosRoot, "App/App/Info.plist");
20267
+ const source = await readFile9(path, "utf8");
20040
20268
  const missing = purposes.filter((purpose) => !source.includes(`<key>${IOS_USAGE_KEYS[purpose]}</key>`));
20041
20269
  if (missing.length === 0)
20042
20270
  return;
@@ -20046,7 +20274,7 @@ var expoIosPrivacyCapabilityCheck = async (iosRoot, project, requirements) => {
20046
20274
  if (requirements.iosPrivacyAccessedApis.length === 0)
20047
20275
  return;
20048
20276
  const privacyPath = await uniqueExpoIosFile(iosRoot, "**/PrivacyInfo.xcprivacy", "privacy manifest");
20049
- const privacy = await readFile8(privacyPath, "utf8");
20277
+ const privacy = await readFile9(privacyPath, "utf8");
20050
20278
  const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
20051
20279
  if (!missing && project.includes(privacyPath.split(/[\\/]/u).at(-1) ?? ""))
20052
20280
  return;
@@ -20056,15 +20284,15 @@ var expoIosPushCapabilityCheck = async (iosRoot, requirements) => {
20056
20284
  if (!requirements.iosPushNotifications)
20057
20285
  return;
20058
20286
  const entitlementsPath = await uniqueExpoIosFile(iosRoot, "**/*.entitlements", "entitlements");
20059
- const entitlements = await readFile8(entitlementsPath, "utf8");
20287
+ const entitlements = await readFile9(entitlementsPath, "utf8");
20060
20288
  if (entitlements.includes("<key>aps-environment</key>"))
20061
20289
  return;
20062
20290
  return fail("mobile.device-capabilities", "Expo iOS push entitlement does not match detected capabilities.", entitlementsPath, "Run `absolute mobile build ios` to regenerate native push integration.");
20063
20291
  };
20064
20292
  var expoIosCapabilityProjectionCheck = async (config, requirements) => {
20065
- const iosRoot = join7(config.nativeProjectDirectory, "ios");
20293
+ const iosRoot = join8(config.nativeProjectDirectory, "ios");
20066
20294
  const projectPath2 = await uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project");
20067
- const project = await readFile8(projectPath2, "utf8");
20295
+ const project = await readFile9(projectPath2, "utf8");
20068
20296
  const privacy = await expoIosPrivacyCapabilityCheck(iosRoot, project, requirements);
20069
20297
  if (privacy)
20070
20298
  return privacy;
@@ -20075,28 +20303,28 @@ var iosCapabilityProjectionCheck = async (config, requirements) => {
20075
20303
  return;
20076
20304
  if (config.engine === "expo")
20077
20305
  return expoIosCapabilityProjectionCheck(config, requirements);
20078
- const appRoot = join7(config.nativeProjectDirectory, "ios/App/App");
20079
- const infoPath = join7(appRoot, "Info.plist");
20080
- const info = await readFile8(infoPath, "utf8").catch(() => "");
20306
+ const appRoot = join8(config.nativeProjectDirectory, "ios/App/App");
20307
+ const infoPath = join8(appRoot, "Info.plist");
20308
+ const info = await readFile9(infoPath, "utf8").catch(() => "");
20081
20309
  if (requirements.iosSystemBars && !/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<true\s*\/>/u.test(info))
20082
20310
  return fail("mobile.device-capabilities", "iOS system-bar capability is missing its required view-controller setting.", infoPath, "Run `absolute mobile sync ios` to regenerate native capability settings.");
20083
20311
  if (requirements.iosPrivacyAccessedApis.length > 0) {
20084
- const privacyPath = join7(appRoot, "PrivacyInfo.xcprivacy");
20085
- const projectPath2 = join7(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
20312
+ const privacyPath = join8(appRoot, "PrivacyInfo.xcprivacy");
20313
+ const projectPath2 = join8(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
20086
20314
  const [privacy, project] = await Promise.all([
20087
- readFile8(privacyPath, "utf8").catch(() => ""),
20088
- readFile8(projectPath2, "utf8").catch(() => "")
20315
+ readFile9(privacyPath, "utf8").catch(() => ""),
20316
+ readFile9(projectPath2, "utf8").catch(() => "")
20089
20317
  ]);
20090
20318
  const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
20091
20319
  if (missing || !project.includes("PrivacyInfo.xcprivacy in Resources"))
20092
20320
  return fail("mobile.device-capabilities", "iOS privacy manifest or target membership does not match detected native capabilities.", privacyPath, "Run `absolute mobile sync ios` to regenerate and target PrivacyInfo.xcprivacy.");
20093
20321
  }
20094
20322
  if (requirements.iosPushNotifications) {
20095
- const entitlementsPath = join7(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
20096
- const delegatePath = join7(appRoot, "AppDelegate.swift");
20323
+ const entitlementsPath = join8(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
20324
+ const delegatePath = join8(appRoot, "AppDelegate.swift");
20097
20325
  const [entitlements, delegate] = await Promise.all([
20098
- readFile8(entitlementsPath, "utf8").catch(() => ""),
20099
- readFile8(delegatePath, "utf8").catch(() => "")
20326
+ readFile9(entitlementsPath, "utf8").catch(() => ""),
20327
+ readFile9(delegatePath, "utf8").catch(() => "")
20100
20328
  ]);
20101
20329
  if (!entitlements.includes("<key>aps-environment</key>") || !delegate.includes("capacitorDidRegisterForRemoteNotifications") || !delegate.includes("capacitorDidFailToRegisterForRemoteNotifications"))
20102
20330
  return fail("mobile.device-capabilities", "iOS push entitlement or AppDelegate forwarding does not match detected capabilities.", entitlementsPath, "Run `absolute mobile sync ios` to regenerate native push integration.");
@@ -20104,13 +20332,13 @@ var iosCapabilityProjectionCheck = async (config, requirements) => {
20104
20332
  return;
20105
20333
  };
20106
20334
  var deviceCapabilityReleaseCheck = async (config, projectRoot) => {
20107
- const manifestPath = join7(projectRoot, "package.json");
20335
+ const manifestPath = join8(projectRoot, "package.json");
20108
20336
  try {
20109
20337
  const plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot, config.engine);
20110
20338
  const assertPackages = async () => {
20111
20339
  if (config.engine === "capacitor")
20112
20340
  return assertAbsoluteDeviceCapabilityPackages(projectRoot, plan);
20113
- const generated = await readJsonObject(join7(config.nativeProjectDirectory, "package.json"));
20341
+ const generated = await readJsonObject(join8(config.nativeProjectDirectory, "package.json"));
20114
20342
  const declarations = packageDeclarations(generated);
20115
20343
  const missing = plan.requiredPackages.filter((spec) => {
20116
20344
  const separator = spec.lastIndexOf("@");
@@ -20137,11 +20365,11 @@ var deviceCapabilityReleaseCheck = async (config, projectRoot) => {
20137
20365
  }
20138
20366
  };
20139
20367
  var inspectAndroidRelease = async (config, projectRoot) => {
20140
- const androidRoot = join7(config.nativeProjectDirectory, "android");
20141
- const nativeConfigPath = join7(androidRoot, "app", "src", "main", "assets", "capacitor.config.json");
20142
- const manifestPath = join7(androidRoot, "app", "src", "main", "AndroidManifest.xml");
20143
- const publicRoot = join7(androidRoot, "app", "src", "main", "assets", "public");
20144
- const journalPath = join7(projectRoot, ".absolutejs", "mobile", "dev-session", "journal.json");
20368
+ const androidRoot = join8(config.nativeProjectDirectory, "android");
20369
+ const nativeConfigPath = join8(androidRoot, "app", "src", "main", "assets", "capacitor.config.json");
20370
+ const manifestPath = join8(androidRoot, "app", "src", "main", "AndroidManifest.xml");
20371
+ const publicRoot = join8(androidRoot, "app", "src", "main", "assets", "public");
20372
+ const journalPath = join8(projectRoot, ".absolutejs", "mobile", "dev-session", "journal.json");
20145
20373
  const checks = await Promise.all([
20146
20374
  journalReleaseCheck(journalPath, "android"),
20147
20375
  capacitorConfigReleaseCheck(nativeConfigPath),
@@ -20162,9 +20390,9 @@ var inspectAndroidRelease = async (config, projectRoot) => {
20162
20390
  }));
20163
20391
  };
20164
20392
  var inspectExpoAndroidRelease = async (config, projectRoot) => {
20165
- const androidRoot = join7(config.nativeProjectDirectory, "android");
20166
- const manifestPath = join7(androidRoot, "app", "src", "main", "AndroidManifest.xml");
20167
- const journalPath = join7(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
20393
+ const androidRoot = join8(config.nativeProjectDirectory, "android");
20394
+ const manifestPath = join8(androidRoot, "app", "src", "main", "AndroidManifest.xml");
20395
+ const journalPath = join8(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
20168
20396
  const checks = await Promise.all([
20169
20397
  journalReleaseCheck(journalPath, "android"),
20170
20398
  expoApplicationConfigCheck(config, projectRoot),
@@ -20190,7 +20418,7 @@ var uniqueExpoIosFile = async (iosRoot, pattern, label) => {
20190
20418
  const [path] = paths;
20191
20419
  if (!path)
20192
20420
  throw new TypeError(`Generated Expo iOS project is missing its ${label}.`);
20193
- return join7(iosRoot, path);
20421
+ return join8(iosRoot, path);
20194
20422
  };
20195
20423
  var expoIosNativeProjectionCheck = async (config, iosRoot) => {
20196
20424
  try {
@@ -20200,9 +20428,9 @@ var expoIosNativeProjectionCheck = async (config, iosRoot) => {
20200
20428
  uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project")
20201
20429
  ]);
20202
20430
  const [info, entitlements, project] = await Promise.all([
20203
- readFile8(infoPath, "utf8"),
20204
- readFile8(entitlementsPath, "utf8"),
20205
- readFile8(projectPath2, "utf8")
20431
+ readFile9(infoPath, "utf8"),
20432
+ readFile9(entitlementsPath, "utf8"),
20433
+ readFile9(projectPath2, "utf8")
20206
20434
  ]);
20207
20435
  if (/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info))
20208
20436
  throw new TypeError("Expo iOS App Transport Security permits arbitrary network loads.");
@@ -20218,8 +20446,8 @@ var expoIosNativeProjectionCheck = async (config, iosRoot) => {
20218
20446
  }
20219
20447
  };
20220
20448
  var inspectExpoIosRelease = async (config, projectRoot) => {
20221
- const iosRoot = join7(config.nativeProjectDirectory, "ios");
20222
- const journalPath = join7(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
20449
+ const iosRoot = join8(config.nativeProjectDirectory, "ios");
20450
+ const journalPath = join8(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
20223
20451
  const checks = [
20224
20452
  await journalReleaseCheck(journalPath, "ios")
20225
20453
  ];
@@ -20242,11 +20470,11 @@ var inspectExpoIosRelease = async (config, projectRoot) => {
20242
20470
  }));
20243
20471
  };
20244
20472
  var inspectIosRelease = async (config, projectRoot) => {
20245
- const iosAppRoot = join7(config.nativeProjectDirectory, "ios", "App", "App");
20246
- const nativeConfigPath = join7(iosAppRoot, "capacitor.config.json");
20247
- const infoPath = join7(iosAppRoot, "Info.plist");
20248
- const publicRoot = join7(iosAppRoot, "public");
20249
- const journalPath = join7(projectRoot, ".absolutejs", "mobile", "ios-dev-session", "journal.json");
20473
+ const iosAppRoot = join8(config.nativeProjectDirectory, "ios", "App", "App");
20474
+ const nativeConfigPath = join8(iosAppRoot, "capacitor.config.json");
20475
+ const infoPath = join8(iosAppRoot, "Info.plist");
20476
+ const publicRoot = join8(iosAppRoot, "public");
20477
+ const journalPath = join8(projectRoot, ".absolutejs", "mobile", "ios-dev-session", "journal.json");
20250
20478
  const checks = [
20251
20479
  await journalReleaseCheck(journalPath, "ios")
20252
20480
  ];
@@ -20258,7 +20486,7 @@ var inspectIosRelease = async (config, projectRoot) => {
20258
20486
  }
20259
20487
  if (!await pathExists2(nativeConfigPath)) {
20260
20488
  checks.push(fail("ios.capacitor-config", "The generated iOS Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync ios` before release validation."));
20261
- } else if (isUnsafeCapacitorConfig(await readFile8(nativeConfigPath, "utf8"))) {
20489
+ } else if (isUnsafeCapacitorConfig(await readFile9(nativeConfigPath, "utf8"))) {
20262
20490
  checks.push(fail("ios.capacitor-config", "iOS Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync ios`; do not ship development transport overrides."));
20263
20491
  } else {
20264
20492
  checks.push(pass("ios.capacitor-config", "iOS Capacitor config contains no development transport overrides.", nativeConfigPath));
@@ -20267,12 +20495,12 @@ var inspectIosRelease = async (config, projectRoot) => {
20267
20495
  if (!await pathExists2(infoPath)) {
20268
20496
  checks.push(fail("ios.transport-security", "The iOS Info.plist is missing.", infoPath, "Run `absolute mobile sync ios` before release validation."));
20269
20497
  } else {
20270
- const info = await readFile8(infoPath, "utf8");
20498
+ const info = await readFile9(infoPath, "utf8");
20271
20499
  checks.push(/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info) ? fail("ios.transport-security", "iOS App Transport Security permits arbitrary network loads.", infoPath, "Remove NSAllowsArbitraryLoads from the release Info.plist.") : pass("ios.transport-security", "iOS App Transport Security does not permit arbitrary loads.", infoPath));
20272
20500
  }
20273
20501
  const hmrAsset = await findHmrAsset(publicRoot);
20274
20502
  checks.push(hmrAsset ? fail("ios.hmr-assets", "A packaged iOS asset contains the development HMR client.", hmrAsset, "Rebuild the production mobile bundle and run Capacitor sync again.") : pass("ios.hmr-assets", "Packaged iOS assets contain no development HMR markers.", publicRoot));
20275
- checks.push(await embeddedBundleReleaseCheck(config, projectRoot, "ios", publicRoot), await contentSecurityPolicyCheck(config, "ios", publicRoot), await iosNativeSecurityCheck(join7(config.nativeProjectDirectory, "ios")), await iosDeepLinkProjectionCheck(config, join7(config.nativeProjectDirectory, "ios")));
20503
+ checks.push(await embeddedBundleReleaseCheck(config, projectRoot, "ios", publicRoot), await contentSecurityPolicyCheck(config, "ios", publicRoot), await iosNativeSecurityCheck(join8(config.nativeProjectDirectory, "ios")), await iosDeepLinkProjectionCheck(config, join8(config.nativeProjectDirectory, "ios")));
20276
20504
  const nativeObservability = await nativeObservabilityProjectionCheck(config, "ios");
20277
20505
  if (nativeObservability)
20278
20506
  checks.push(nativeObservability);
@@ -20342,16 +20570,16 @@ import { createHash as createHash2 } from "crypto";
20342
20570
  import {
20343
20571
  access as access5,
20344
20572
  copyFile,
20345
- mkdir as mkdir6,
20573
+ mkdir as mkdir7,
20346
20574
  mkdtemp,
20347
- readFile as readFile9,
20575
+ readFile as readFile10,
20348
20576
  realpath,
20349
- rename as rename7,
20577
+ rename as rename8,
20350
20578
  rm as rm4,
20351
20579
  stat as stat2,
20352
- writeFile as writeFile8
20580
+ writeFile as writeFile9
20353
20581
  } from "fs/promises";
20354
- import { dirname as dirname6, isAbsolute as isAbsolute2, join as join8, relative as relative4, resolve as resolve5, sep as sep2 } from "path";
20582
+ import { dirname as dirname7, isAbsolute as isAbsolute2, join as join9, relative as relative4, resolve as resolve5, sep as sep2 } from "path";
20355
20583
  var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1;
20356
20584
  var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20357
20585
  var requireManifest = (value) => {
@@ -20430,7 +20658,7 @@ var windowsJarsignerPath = (path, capture) => {
20430
20658
  }
20431
20659
  return translated;
20432
20660
  };
20433
- var sha256File = async (path) => createHash2("sha256").update(await readFile9(path)).digest("hex");
20661
+ var sha256File = async (path) => createHash2("sha256").update(await readFile10(path)).digest("hex");
20434
20662
  var fingerprintExpoAndroidProject = async (nativeDirectory) => {
20435
20663
  const root = await realpath(nativeDirectory);
20436
20664
  const files = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: root, onlyFiles: true }));
@@ -20438,7 +20666,7 @@ var fingerprintExpoAndroidProject = async (nativeDirectory) => {
20438
20666
  const parts = path.replaceAll("\\", "/").split("/");
20439
20667
  return !parts.includes(".gradle") && !parts.includes("build");
20440
20668
  }).sort().map(async (path) => {
20441
- const contents = await readFile9(join8(root, path));
20669
+ const contents = await readFile10(join9(root, path));
20442
20670
  return `${path.replaceAll("\\", "/")}\x00${createHash2("sha256").update(contents).digest("hex")}\x00`;
20443
20671
  }));
20444
20672
  return createHash2("sha256").update(records.join("")).digest("hex");
@@ -20453,11 +20681,11 @@ var safeOutputDirectory = (projectRoot, requested) => {
20453
20681
  return output;
20454
20682
  };
20455
20683
  var installRelease = async (artifactPath, metadata, outputRoot) => {
20456
- const releaseRoot = join8(outputRoot, metadata.releaseId);
20684
+ const releaseRoot = join9(outputRoot, metadata.releaseId);
20457
20685
  const artifactName = "app-release.aab";
20458
- const destination = join8(releaseRoot, artifactName);
20686
+ const destination = join9(releaseRoot, artifactName);
20459
20687
  if (await pathExists3(releaseRoot)) {
20460
- const existing = requireManifestIdentity(JSON.parse(await readFile9(join8(releaseRoot, "release.json"), "utf8")), metadata);
20688
+ const existing = requireManifestIdentity(JSON.parse(await readFile10(join9(releaseRoot, "release.json"), "utf8")), metadata);
20461
20689
  const [installedBytes, installedSha256] = await Promise.all([
20462
20690
  stat2(destination).then(({ size }) => size),
20463
20691
  sha256File(destination)
@@ -20467,17 +20695,17 @@ var installRelease = async (artifactPath, metadata, outputRoot) => {
20467
20695
  }
20468
20696
  return { artifactPath: destination, metadata: existing, releaseRoot };
20469
20697
  }
20470
- await mkdir6(dirname6(releaseRoot), { recursive: true });
20471
- const staging = await mkdtemp(join8(dirname6(releaseRoot), ".android-stage-"));
20698
+ await mkdir7(dirname7(releaseRoot), { recursive: true });
20699
+ const staging = await mkdtemp(join9(dirname7(releaseRoot), ".android-stage-"));
20472
20700
  try {
20473
- await copyFile(artifactPath, join8(staging, artifactName));
20701
+ await copyFile(artifactPath, join9(staging, artifactName));
20474
20702
  const complete = {
20475
20703
  ...metadata,
20476
20704
  artifact: artifactName
20477
20705
  };
20478
- await writeFile8(join8(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
20706
+ await writeFile9(join9(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
20479
20707
  `, { flag: "wx" });
20480
- await rename7(staging, releaseRoot);
20708
+ await rename8(staging, releaseRoot);
20481
20709
  return { artifactPath: destination, metadata: complete, releaseRoot };
20482
20710
  } finally {
20483
20711
  await rm4(staging, { force: true, recursive: true }).catch(() => {
@@ -20505,8 +20733,8 @@ var buildAbsoluteAndroidRelease = async (options) => {
20505
20733
  const projectRoot = resolve5(options.projectRoot);
20506
20734
  const host = options.host ?? detectAbsoluteMobileHost();
20507
20735
  const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host);
20508
- const nativeDirectory = join8(options.config.nativeProjectDirectory, "android");
20509
- const manifest = requireManifest(JSON.parse(await readFile9(join8(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
20736
+ const nativeDirectory = join9(options.config.nativeProjectDirectory, "android");
20737
+ const manifest = requireManifest(JSON.parse(await readFile10(join9(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
20510
20738
  if (manifest.appId !== options.config.appId) {
20511
20739
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
20512
20740
  }
@@ -20595,20 +20823,20 @@ var buildAbsoluteAndroidRelease = async (options) => {
20595
20823
  import { createHash as createHash3 } from "crypto";
20596
20824
  import {
20597
20825
  access as access6,
20598
- mkdir as mkdir7,
20826
+ mkdir as mkdir8,
20599
20827
  mkdtemp as mkdtemp2,
20600
- readFile as readFile10,
20601
- rename as rename8,
20828
+ readFile as readFile11,
20829
+ rename as rename9,
20602
20830
  rm as rm5,
20603
20831
  stat as stat3,
20604
- writeFile as writeFile9
20832
+ writeFile as writeFile10
20605
20833
  } from "fs/promises";
20606
20834
  import { homedir } from "os";
20607
20835
  import {
20608
20836
  basename,
20609
- dirname as dirname7,
20837
+ dirname as dirname8,
20610
20838
  isAbsolute as isAbsolute3,
20611
- join as join9,
20839
+ join as join10,
20612
20840
  relative as relative5,
20613
20841
  resolve as resolve6,
20614
20842
  sep as sep3
@@ -20662,7 +20890,10 @@ var parseAbsoluteAndroidInstalledApp = (appId, output) => {
20662
20890
  var ABSOLUTE_BUNDLETOOL_VERSION = "1.18.3";
20663
20891
  var ABSOLUTE_BUNDLETOOL_SHA256 = "a099cfa1543f55593bc2ed16a70a7c67fe54b1747bb7301f37fdfd6d91028e29";
20664
20892
  var ABSOLUTE_BUNDLETOOL_URL = `https://github.com/google/bundletool/releases/download/${ABSOLUTE_BUNDLETOOL_VERSION}/bundletool-all-${ABSOLUTE_BUNDLETOOL_VERSION}.jar`;
20665
- var EMBEDDED_READY_MARKER = "Expo embedded web content ready";
20893
+ var EMBEDDED_READY_MARKERS = {
20894
+ capacitor: "Capacitor embedded web content ready",
20895
+ expo: "Expo embedded web content ready"
20896
+ };
20666
20897
  var RELEASE_READY_POLL_MS = 250;
20667
20898
  var RELEASE_READY_TIMEOUT_MS = 30000;
20668
20899
  var RELEASE_STABILITY_MS = 2000;
@@ -20675,7 +20906,7 @@ var exists2 = async (path) => {
20675
20906
  return false;
20676
20907
  }
20677
20908
  };
20678
- var sha256 = async (path) => createHash3("sha256").update(await readFile10(path)).digest("hex");
20909
+ var sha256 = async (path) => createHash3("sha256").update(await readFile11(path)).digest("hex");
20679
20910
  var inside = (root, candidate) => {
20680
20911
  const path = relative5(resolve6(root), resolve6(candidate));
20681
20912
  return path === "" || !isAbsolute3(path) && path !== ".." && !path.startsWith(`..${sep3}`);
@@ -20717,7 +20948,7 @@ var parseMetadata = (value) => {
20717
20948
  throw new TypeError("Android release ID does not match its artifact digest.");
20718
20949
  return metadata;
20719
20950
  };
20720
- var absoluteBundletoolPath = (root = process.env.ABSOLUTE_BUNDLETOOL_ROOT ?? join9(homedir(), ".absolutejs", "tools", "bundletool")) => join9(root, ABSOLUTE_BUNDLETOOL_VERSION, "bundletool.jar");
20951
+ var absoluteBundletoolPath = (root = process.env.ABSOLUTE_BUNDLETOOL_ROOT ?? join10(homedir(), ".absolutejs", "tools", "bundletool")) => join10(root, ABSOLUTE_BUNDLETOOL_VERSION, "bundletool.jar");
20721
20952
  var inspectAbsoluteBundletool = async (root) => {
20722
20953
  const path = absoluteBundletoolPath(root);
20723
20954
  if (!await exists2(path))
@@ -20731,9 +20962,9 @@ var readAbsoluteAndroidRelease = async (projectRoot, requested) => {
20731
20962
  const selected = resolve6(root, requested);
20732
20963
  if (!inside(root, selected))
20733
20964
  throw new TypeError("mobile test android --release must remain inside the project.");
20734
- const metadataPath = basename(selected) === "release.json" ? selected : join9(selected, "release.json");
20735
- const releaseRoot = dirname7(metadataPath);
20736
- const metadata = parseMetadata(JSON.parse(await readFile10(metadataPath, "utf8")));
20965
+ const metadataPath = basename(selected) === "release.json" ? selected : join10(selected, "release.json");
20966
+ const releaseRoot = dirname8(metadataPath);
20967
+ const metadata = parseMetadata(JSON.parse(await readFile11(metadataPath, "utf8")));
20737
20968
  const artifactPath = resolve6(releaseRoot, metadata.artifact);
20738
20969
  if (!inside(releaseRoot, artifactPath) || basename(artifactPath) !== metadata.artifact)
20739
20970
  throw new TypeError("Android release artifact path is invalid.");
@@ -20761,12 +20992,12 @@ var ensureAbsoluteBundletool = async (options) => {
20761
20992
  const digest = createHash3("sha256").update(bytes).digest("hex");
20762
20993
  if (digest !== ABSOLUTE_BUNDLETOOL_SHA256)
20763
20994
  throw new TypeError("Downloaded Bundletool failed checksum verification.");
20764
- await mkdir7(dirname7(inspected.path), { recursive: true });
20765
- const staging = await mkdtemp2(join9(dirname7(inspected.path), ".install-"));
20766
- const candidate = join9(staging, "bundletool.jar");
20995
+ await mkdir8(dirname8(inspected.path), { recursive: true });
20996
+ const staging = await mkdtemp2(join10(dirname8(inspected.path), ".install-"));
20997
+ const candidate = join10(staging, "bundletool.jar");
20767
20998
  try {
20768
- await writeFile9(candidate, bytes, { flag: "wx" });
20769
- await rename8(candidate, inspected.path).catch(async (error) => {
20999
+ await writeFile10(candidate, bytes, { flag: "wx" });
21000
+ await rename9(candidate, inspected.path).catch(async (error) => {
20770
21001
  if (await exists2(inspected.path))
20771
21002
  return;
20772
21003
  throw error;
@@ -20803,7 +21034,7 @@ var adb = (path, serial, ...args) => [
20803
21034
  serial,
20804
21035
  ...args
20805
21036
  ];
20806
- var waitForMarker = async (run, adbPath, serial, appId, timeoutMs) => {
21037
+ var waitForMarker = async (run, adbPath, serial, appId, engine, timeoutMs) => {
20807
21038
  const startedAt = performance.now();
20808
21039
  const inspect = async () => {
20809
21040
  if (performance.now() - startedAt >= timeoutMs)
@@ -20814,9 +21045,9 @@ var waitForMarker = async (run, adbPath, serial, appId, timeoutMs) => {
20814
21045
  ]);
20815
21046
  if (process2.exitCode !== 0 || !process2.stdout.trim())
20816
21047
  throw new Error("The installed Android release exited before becoming ready.");
20817
- if (logs.stdout.includes(EMBEDDED_READY_MARKER))
21048
+ if (logs.stdout.includes(EMBEDDED_READY_MARKERS[engine]))
20818
21049
  return;
20819
- const failedPhase = /Expo embedded web phase: (assets-(?:root|directory|destination|module|source|download|copy|read|write|finalize|unexpected)|devices)-failed/u.exec(logs.stdout)?.[1];
21050
+ const failedPhase = engine === "expo" ? /Expo embedded web phase: (assets-(?:root|directory|destination|module|source|download|copy|read|write|finalize|unexpected)|devices)-failed/u.exec(logs.stdout)?.[1] : undefined;
20820
21051
  if (failedPhase)
20821
21052
  throw new Error(`The installed release could not initialize its embedded ${failedPhase} runtime offline.`);
20822
21053
  await Bun.sleep(RELEASE_READY_POLL_MS);
@@ -20834,14 +21065,12 @@ var launch = async (run, adbPath, serial, appId, launchUrl) => {
20834
21065
  return Math.round(performance.now() - startedAt);
20835
21066
  };
20836
21067
  var runAbsoluteAndroidReleaseAcceptance = async (options) => {
20837
- if (options.release.metadata.engine !== "expo")
20838
- throw new TypeError("Installed AAB release acceptance currently requires the Expo engine.");
20839
21068
  if (!options.serial.startsWith("emulator-"))
20840
21069
  throw new TypeError("Installed release acceptance changes network state and therefore requires an Android emulator.");
20841
21070
  const run = options.run ?? defaultRun;
20842
21071
  const startedAt = performance.now();
20843
- await mkdir7(options.artifactDirectory, { recursive: true });
20844
- const apksPath = join9(options.artifactDirectory, "release.apks");
21072
+ await mkdir8(options.artifactDirectory, { recursive: true });
21073
+ const apksPath = join10(options.artifactDirectory, "release.apks");
20845
21074
  const windowsJava = (options.host ?? detectAbsoluteMobileHost()) === "wsl" && options.java.toLowerCase().endsWith(".exe");
20846
21075
  const toolPath = async (path) => {
20847
21076
  if (!windowsJava)
@@ -20896,12 +21125,12 @@ var runAbsoluteAndroidReleaseAcceptance = async (options) => {
20896
21125
  ]);
20897
21126
  await requireSuccess(run, adb(options.adb, options.serial, "shell", "am", "force-stop", options.release.metadata.appId), "Android release stop");
20898
21127
  launchMs = await launch(run, options.adb, options.serial, options.release.metadata.appId, options.launchUrl);
20899
- await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, RELEASE_READY_TIMEOUT_MS);
21128
+ await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, options.release.metadata.engine, RELEASE_READY_TIMEOUT_MS);
20900
21129
  await Bun.sleep(options.stabilityMs ?? RELEASE_STABILITY_MS);
20901
21130
  await requireSuccess(run, adb(options.adb, options.serial, "shell", "am", "force-stop", options.release.metadata.appId), "Android release relaunch stop");
20902
21131
  await requireSuccess(run, adb(options.adb, options.serial, "logcat", "-c"), "Android release relaunch log isolation");
20903
21132
  relaunchMs = await launch(run, options.adb, options.serial, options.release.metadata.appId, options.launchUrl);
20904
- await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, RELEASE_READY_TIMEOUT_MS);
21133
+ await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, options.release.metadata.engine, RELEASE_READY_TIMEOUT_MS);
20905
21134
  } finally {
20906
21135
  await Promise.all([
20907
21136
  requireSuccess(run, adb(options.adb, options.serial, "shell", "svc", "wifi", wifi.stdout.trim() === "1" ? "enable" : "disable"), "Android Wi-Fi restore"),
@@ -20928,6 +21157,452 @@ var runAbsoluteAndroidReleaseAcceptance = async (options) => {
20928
21157
  };
20929
21158
  };
20930
21159
 
21160
+ // src/mobile/iosReleaseAcceptance.ts
21161
+ import { createHash as createHash4 } from "crypto";
21162
+ import {
21163
+ access as access7,
21164
+ mkdir as mkdir9,
21165
+ mkdtemp as mkdtemp3,
21166
+ readFile as readFile12,
21167
+ readdir as readdir5,
21168
+ rm as rm6,
21169
+ stat as stat4
21170
+ } from "fs/promises";
21171
+ import {
21172
+ basename as basename2,
21173
+ dirname as dirname9,
21174
+ isAbsolute as isAbsolute4,
21175
+ join as join11,
21176
+ relative as relative6,
21177
+ resolve as resolve7,
21178
+ sep as sep4
21179
+ } from "path";
21180
+
21181
+ // src/mobile/iosUpgradeConformance.ts
21182
+ var captureCommand3 = async (command) => {
21183
+ const process2 = Bun.spawn(command, { stderr: "pipe", stdout: "pipe" });
21184
+ const [exitCode, stderr, stdout] = await Promise.all([
21185
+ process2.exited,
21186
+ new Response(process2.stderr).text(),
21187
+ new Response(process2.stdout).text()
21188
+ ]);
21189
+ return { exitCode, stderr, stdout };
21190
+ };
21191
+ var requiredOutput = (result, message) => {
21192
+ const value = result.stdout.trim();
21193
+ if (result.exitCode !== 0 || !value)
21194
+ throw new Error(`${message}: ${result.stderr.trim() || value || "no output"}`);
21195
+ return value;
21196
+ };
21197
+ var inspectAbsoluteIosInstalledApp = async (xcrun, udid, appId, run = captureCommand3, plutil = "/usr/bin/plutil") => {
21198
+ const dataContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "data"]), `Could not inspect the installed iOS data container for ${appId}`);
21199
+ const appContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "app"]), `Could not inspect the installed iOS application container for ${appId}`);
21200
+ const info = requiredOutput(await run([
21201
+ plutil,
21202
+ "-convert",
21203
+ "json",
21204
+ "-o",
21205
+ "-",
21206
+ `${appContainer}/Info.plist`
21207
+ ]), `Could not inspect the installed iOS Info.plist for ${appId}`);
21208
+ return parseAbsoluteIosInstalledAppInfo(appId, appContainer, dataContainer, info);
21209
+ };
21210
+ var parseAbsoluteIosInstalledAppInfo = (appId, appContainer, dataContainer, output) => {
21211
+ let value;
21212
+ try {
21213
+ value = JSON.parse(output);
21214
+ } catch {
21215
+ throw new TypeError("Invalid installed iOS application Info.plist JSON.");
21216
+ }
21217
+ if (!value || typeof value !== "object")
21218
+ throw new TypeError("Invalid installed iOS application Info.plist JSON.");
21219
+ const bundleId = Reflect.get(value, "CFBundleIdentifier");
21220
+ if (bundleId !== appId)
21221
+ throw new Error(`Installed iOS application identifier mismatch (${String(bundleId)} != ${appId}).`);
21222
+ const buildNumber = Reflect.get(value, "CFBundleVersion");
21223
+ const version = Reflect.get(value, "CFBundleShortVersionString");
21224
+ return {
21225
+ appContainer,
21226
+ appId,
21227
+ ...typeof buildNumber === "string" || typeof buildNumber === "number" ? { buildNumber: String(buildNumber) } : {},
21228
+ dataContainer,
21229
+ ...typeof version === "string" ? { version } : {}
21230
+ };
21231
+ };
21232
+
21233
+ // src/mobile/iosReleaseAcceptance.ts
21234
+ var READY_MARKERS = {
21235
+ capacitor: "Capacitor embedded web content ready",
21236
+ expo: "Expo embedded web content ready"
21237
+ };
21238
+ var READY_TIMEOUT_MS = 30000;
21239
+ var READY_POLL_MS = 250;
21240
+ var defaultRun2 = async (command, options = {}) => {
21241
+ const process2 = Bun.spawn(command, {
21242
+ cwd: options.cwd,
21243
+ env: options.env,
21244
+ stderr: "pipe",
21245
+ stdin: "ignore",
21246
+ stdout: "pipe"
21247
+ });
21248
+ const [exitCode, stderr, stdout] = await Promise.all([
21249
+ process2.exited,
21250
+ new Response(process2.stderr).text(),
21251
+ new Response(process2.stdout).text()
21252
+ ]);
21253
+ return { exitCode, stderr, stdout };
21254
+ };
21255
+ var exists3 = async (path) => {
21256
+ try {
21257
+ await access7(path);
21258
+ return true;
21259
+ } catch {
21260
+ return false;
21261
+ }
21262
+ };
21263
+ var sha2562 = async (path) => createHash4("sha256").update(await readFile12(path)).digest("hex");
21264
+ var inside2 = (root, candidate) => {
21265
+ const path = relative6(resolve7(root), resolve7(candidate));
21266
+ return path === "" || !isAbsolute4(path) && path !== ".." && !path.startsWith(`..${sep4}`);
21267
+ };
21268
+ var requireSuccess2 = async (run, command, label, options) => {
21269
+ const result = await run(command, options);
21270
+ if (result.exitCode !== 0)
21271
+ throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
21272
+ return result;
21273
+ };
21274
+ var findByExtension = async (root, extension) => {
21275
+ if (!await exists3(root))
21276
+ return;
21277
+ const entries = await readdir5(root, { withFileTypes: true });
21278
+ const matches = await Promise.all(entries.map(async (entry) => {
21279
+ const path = join11(root, entry.name);
21280
+ if (entry.name.endsWith(extension))
21281
+ return path;
21282
+ return entry.isDirectory() ? findByExtension(path, extension) : undefined;
21283
+ }));
21284
+ return matches.find((match) => match !== undefined);
21285
+ };
21286
+ var readAbsoluteIosRelease = async (projectRoot, requested) => {
21287
+ const root = resolve7(projectRoot);
21288
+ const selected = resolve7(root, requested);
21289
+ if (!inside2(root, selected))
21290
+ throw new TypeError("mobile test ios --release must remain inside the project.");
21291
+ const metadataPath = basename2(selected) === "release.json" ? selected : join11(selected, "release.json");
21292
+ const releaseRoot = dirname9(metadataPath);
21293
+ const metadata = requireAbsoluteIosReleaseMetadata(JSON.parse(await readFile12(metadataPath, "utf8")));
21294
+ const artifactPath = resolve7(releaseRoot, metadata.artifact);
21295
+ if (!inside2(releaseRoot, artifactPath))
21296
+ throw new TypeError("iOS release artifact path is invalid.");
21297
+ const [artifactStat, artifactSha256] = await Promise.all([
21298
+ stat4(artifactPath),
21299
+ sha2562(artifactPath)
21300
+ ]);
21301
+ if (artifactStat.size !== metadata.bytes || artifactSha256 !== metadata.sha256)
21302
+ throw new TypeError(`Immutable iOS release ${metadata.releaseId} is missing or modified.`);
21303
+ const registered = metadata.registeredDevice;
21304
+ let registeredArtifactPath;
21305
+ if (registered) {
21306
+ registeredArtifactPath = resolve7(releaseRoot, registered.artifact);
21307
+ if (!inside2(releaseRoot, registeredArtifactPath))
21308
+ throw new TypeError("iOS registered-device artifact path is invalid.");
21309
+ const [registeredStat, registeredSha256] = await Promise.all([
21310
+ stat4(registeredArtifactPath),
21311
+ sha2562(registeredArtifactPath)
21312
+ ]);
21313
+ if (registeredStat.size !== registered.bytes || registeredSha256 !== registered.sha256)
21314
+ throw new TypeError(`Registered-device export for ${metadata.releaseId} is missing or modified.`);
21315
+ }
21316
+ return {
21317
+ artifactPath,
21318
+ metadata,
21319
+ metadataPath,
21320
+ registeredArtifactPath,
21321
+ releaseRoot
21322
+ };
21323
+ };
21324
+ var releaseMarker = (release) => {
21325
+ const marker = READY_MARKERS[release.metadata.engine];
21326
+ return release.metadata.buildNumber === undefined ? marker : `${marker}; version=${release.metadata.marketingVersion}; build=${release.metadata.buildNumber}`;
21327
+ };
21328
+ var waitForSimulatorMarker = async (options) => {
21329
+ const startedAt = performance.now();
21330
+ const poll = async () => {
21331
+ if (performance.now() - startedAt >= READY_TIMEOUT_MS)
21332
+ throw new Error("The installed iOS Release app launched but its embedded content did not become ready.");
21333
+ const logs = await options.run([
21334
+ options.xcrun,
21335
+ "simctl",
21336
+ "spawn",
21337
+ options.udid,
21338
+ "log",
21339
+ "show",
21340
+ "--style",
21341
+ "compact",
21342
+ "--start",
21343
+ options.startedAt.toISOString(),
21344
+ "--predicate",
21345
+ `eventMessage CONTAINS "${releaseMarker(options.release)}"`
21346
+ ]);
21347
+ if (logs.exitCode === 0 && logs.stdout.includes(releaseMarker(options.release)))
21348
+ return;
21349
+ await Bun.sleep(READY_POLL_MS);
21350
+ await poll();
21351
+ };
21352
+ await poll();
21353
+ };
21354
+ var launchSimulator = async (options) => {
21355
+ const startedAt = performance.now();
21356
+ const logStartedAt = new Date;
21357
+ await requireSuccess2(options.run, [
21358
+ options.xcrun,
21359
+ "simctl",
21360
+ "launch",
21361
+ "--terminate-running-process",
21362
+ options.udid,
21363
+ options.release.metadata.appId
21364
+ ], "iOS Simulator Release launch");
21365
+ await waitForSimulatorMarker({ ...options, startedAt: logStartedAt });
21366
+ return Math.round(performance.now() - startedAt);
21367
+ };
21368
+ var runAbsoluteIosSimulatorReleaseAcceptance = async (options) => {
21369
+ const run = options.run ?? defaultRun2;
21370
+ const xcodebuild = options.xcodebuild ?? "/usr/bin/xcodebuild";
21371
+ const xcrun = options.xcrun ?? "/usr/bin/xcrun";
21372
+ const startedAt = performance.now();
21373
+ const iosRoot = join11(options.config.nativeProjectDirectory, "ios");
21374
+ const xcode = await resolveAbsoluteIosXcodeProject(iosRoot);
21375
+ const derivedData = join11(options.artifactDirectory, "DerivedData");
21376
+ await mkdir9(options.artifactDirectory, { recursive: true });
21377
+ await requireSuccess2(run, [
21378
+ xcodebuild,
21379
+ "-workspace",
21380
+ xcode.workspacePath,
21381
+ "-scheme",
21382
+ xcode.scheme,
21383
+ "-configuration",
21384
+ "Release",
21385
+ "-destination",
21386
+ `platform=iOS Simulator,id=${options.udid}`,
21387
+ "-derivedDataPath",
21388
+ derivedData,
21389
+ `MARKETING_VERSION=${options.release.metadata.marketingVersion}`,
21390
+ ...options.release.metadata.buildNumber === undefined ? [] : [
21391
+ `CURRENT_PROJECT_VERSION=${options.release.metadata.buildNumber}`
21392
+ ],
21393
+ "build"
21394
+ ], "iOS Simulator Release build", { cwd: iosRoot });
21395
+ const app = await findByExtension(join11(derivedData, "Build", "Products"), ".app");
21396
+ if (!app)
21397
+ throw new Error("Xcode did not produce a Simulator Release app.");
21398
+ const installStartedAt = performance.now();
21399
+ await requireSuccess2(run, [xcrun, "simctl", "install", options.udid, app], "iOS Simulator Release installation");
21400
+ const installMs = Math.round(performance.now() - installStartedAt);
21401
+ const installed = await inspectAbsoluteIosInstalledApp(xcrun, options.udid, options.release.metadata.appId, (command) => run(command));
21402
+ if (installed.version !== options.release.metadata.marketingVersion)
21403
+ throw new Error(`Installed iOS version ${installed.version ?? "unknown"} does not match release ${options.release.metadata.marketingVersion}.`);
21404
+ if (options.release.metadata.buildNumber !== undefined && installed.buildNumber !== String(options.release.metadata.buildNumber))
21405
+ throw new Error(`Installed iOS build ${installed.buildNumber ?? "unknown"} does not match release ${options.release.metadata.buildNumber}.`);
21406
+ const launchMs = await launchSimulator({
21407
+ release: options.release,
21408
+ run,
21409
+ udid: options.udid,
21410
+ xcrun
21411
+ });
21412
+ await requireSuccess2(run, [
21413
+ xcrun,
21414
+ "simctl",
21415
+ "terminate",
21416
+ options.udid,
21417
+ options.release.metadata.appId
21418
+ ], "iOS Simulator Release termination");
21419
+ const relaunchMs = await launchSimulator({
21420
+ release: options.release,
21421
+ run,
21422
+ udid: options.udid,
21423
+ xcrun
21424
+ });
21425
+ return {
21426
+ artifactBytes: options.release.metadata.bytes,
21427
+ artifactExactness: "source-equivalent",
21428
+ artifactSha256: options.release.metadata.sha256,
21429
+ ...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
21430
+ distribution: "simulator-release",
21431
+ durationMs: Math.round(performance.now() - startedAt),
21432
+ embeddedLocal: true,
21433
+ engine: options.release.metadata.engine,
21434
+ installMs,
21435
+ launchMs,
21436
+ marketingVersion: options.release.metadata.marketingVersion,
21437
+ networkUnavailable: "not-proven",
21438
+ relaunchMs,
21439
+ releaseId: options.release.metadata.releaseId,
21440
+ signed: options.release.metadata.signed,
21441
+ status: "pass",
21442
+ target: "simulator"
21443
+ };
21444
+ };
21445
+ var readUntilMarker = async (reader, marker, output = "") => {
21446
+ const { done, value } = await reader.read();
21447
+ if (done)
21448
+ return { found: false, output };
21449
+ const next = `${output}${new TextDecoder().decode(value)}`;
21450
+ if (next.includes(marker))
21451
+ return { found: true, output: next };
21452
+ return readUntilMarker(reader, marker, next);
21453
+ };
21454
+ var observeDeviceLaunch = async (options) => {
21455
+ const startedAt = performance.now();
21456
+ const process2 = Bun.spawn([
21457
+ options.xcrun,
21458
+ "devicectl",
21459
+ "device",
21460
+ "process",
21461
+ "launch",
21462
+ "--console",
21463
+ "--terminate-existing",
21464
+ "--device",
21465
+ options.device,
21466
+ options.appId
21467
+ ], { stderr: "pipe", stdin: "ignore", stdout: "pipe" });
21468
+ const stdoutReader = process2.stdout.getReader();
21469
+ const stderrReader = process2.stderr.getReader();
21470
+ const requireMarker = async (reader) => {
21471
+ const result = await readUntilMarker(reader, options.marker);
21472
+ if (!result.found)
21473
+ throw new Error(`Console stream closed before readiness.${result.output.trim() ? ` Output: ${result.output.trim()}` : ""}`);
21474
+ return result;
21475
+ };
21476
+ const observed = Promise.any([
21477
+ requireMarker(stdoutReader),
21478
+ requireMarker(stderrReader)
21479
+ ]);
21480
+ try {
21481
+ const result = await Promise.race([
21482
+ observed.then((value) => ({ type: "output", value })).catch(() => ({ type: "closed" })),
21483
+ Bun.sleep(options.timeoutMs ?? READY_TIMEOUT_MS).then(() => ({
21484
+ type: "timeout"
21485
+ }))
21486
+ ]);
21487
+ if (result.type === "output" && result.value.found)
21488
+ return Math.round(performance.now() - startedAt);
21489
+ if (result.type === "closed")
21490
+ throw new Error("The physical iOS app exited before embedded content became ready.");
21491
+ throw new Error("The physical iOS app did not report embedded readiness before the timeout.");
21492
+ } finally {
21493
+ process2.kill();
21494
+ await Promise.all([
21495
+ stdoutReader.cancel().catch(() => {
21496
+ return;
21497
+ }),
21498
+ stderrReader.cancel().catch(() => {
21499
+ return;
21500
+ })
21501
+ ]);
21502
+ stdoutReader.releaseLock();
21503
+ stderrReader.releaseLock();
21504
+ }
21505
+ };
21506
+ var installRegisteredDeviceRelease = async (options) => {
21507
+ if (!options.release.registeredArtifactPath)
21508
+ throw new TypeError("This release has no same-archive registered-device IPA. Rebuild with `absolute mobile build ios --registered-device-artifact`.");
21509
+ const staging = await mkdtemp3(join11(dirname9(options.release.releaseRoot), ".ios-device-"));
21510
+ try {
21511
+ await requireSuccess2(options.run, [
21512
+ "/usr/bin/ditto",
21513
+ "-x",
21514
+ "-k",
21515
+ options.release.registeredArtifactPath,
21516
+ staging
21517
+ ], "iOS registered-device IPA extraction");
21518
+ const app = await findByExtension(staging, ".app");
21519
+ if (!app)
21520
+ throw new Error("The registered-device IPA contains no installable app.");
21521
+ const startedAt = performance.now();
21522
+ await requireSuccess2(options.run, [
21523
+ options.xcrun,
21524
+ "devicectl",
21525
+ "device",
21526
+ "install",
21527
+ "app",
21528
+ "--device",
21529
+ options.device,
21530
+ app
21531
+ ], "iOS registered-device Release installation");
21532
+ return Math.round(performance.now() - startedAt);
21533
+ } finally {
21534
+ await rm6(staging, { force: true, recursive: true });
21535
+ }
21536
+ };
21537
+ var inspectTestFlightInstallation = async (options) => {
21538
+ const apps = await requireSuccess2(options.run, [
21539
+ options.xcrun,
21540
+ "devicectl",
21541
+ "device",
21542
+ "info",
21543
+ "apps",
21544
+ "--device",
21545
+ options.device
21546
+ ], "TestFlight installation inspection");
21547
+ if (!apps.stdout.includes(options.release.metadata.appId))
21548
+ throw new Error("The selected device does not have the requested TestFlight app installed.");
21549
+ return 0;
21550
+ };
21551
+ var runAbsoluteIosDeviceReleaseAcceptance = async (options) => {
21552
+ if (!options.networkUnavailableConfirmed)
21553
+ throw new TypeError("Physical iOS offline acceptance requires explicit confirmation that Airplane Mode is enabled and Wi-Fi is disabled in Settings.");
21554
+ const run = options.run ?? defaultRun2;
21555
+ const xcrun = options.xcrun ?? "/usr/bin/xcrun";
21556
+ const startedAt = performance.now();
21557
+ await requireSuccess2(run, [
21558
+ xcrun,
21559
+ "devicectl",
21560
+ "device",
21561
+ "info",
21562
+ "details",
21563
+ "--device",
21564
+ options.device
21565
+ ], "iOS physical-device availability inspection");
21566
+ const installation = {
21567
+ device: options.device,
21568
+ release: options.release,
21569
+ run,
21570
+ xcrun
21571
+ };
21572
+ const installMs = options.distribution === "registered-device" ? await installRegisteredDeviceRelease(installation) : await inspectTestFlightInstallation(installation);
21573
+ const observe = options.observeLaunch ?? ((launch2) => observeDeviceLaunch({ ...launch2, xcrun }));
21574
+ const marker = releaseMarker(options.release);
21575
+ const launchMs = await observe({
21576
+ appId: options.release.metadata.appId,
21577
+ device: options.device,
21578
+ marker
21579
+ });
21580
+ const relaunchMs = await observe({
21581
+ appId: options.release.metadata.appId,
21582
+ device: options.device,
21583
+ marker
21584
+ });
21585
+ return {
21586
+ artifactBytes: options.release.metadata.bytes,
21587
+ artifactExactness: options.distribution === "testflight" ? "store-delivered" : "archive-equivalent",
21588
+ artifactSha256: options.release.metadata.sha256,
21589
+ ...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
21590
+ distribution: options.distribution === "testflight" ? "apple-processed" : "registered-device",
21591
+ durationMs: Math.round(performance.now() - startedAt),
21592
+ embeddedLocal: true,
21593
+ engine: options.release.metadata.engine,
21594
+ installMs,
21595
+ launchMs,
21596
+ marketingVersion: options.release.metadata.marketingVersion,
21597
+ networkUnavailable: "user-confirmed",
21598
+ relaunchMs,
21599
+ releaseId: options.release.metadata.releaseId,
21600
+ signed: options.release.metadata.signed,
21601
+ status: "pass",
21602
+ target: "device"
21603
+ };
21604
+ };
21605
+
20931
21606
  // src/mobile/iosDeviceAcceptance.ts
20932
21607
  var absoluteIosDeviceAcceptanceCommands = (options) => {
20933
21608
  const xcrun = options.xcrun ?? "/usr/bin/xcrun";
@@ -20946,20 +21621,20 @@ var absoluteIosDeviceAcceptanceCommands = (options) => {
20946
21621
  ]
20947
21622
  };
20948
21623
  };
20949
- var requireSuccess2 = (result, message) => {
21624
+ var requireSuccess3 = (result, message) => {
20950
21625
  if (result.exitCode !== 0)
20951
21626
  throw new Error(message);
20952
21627
  return result;
20953
21628
  };
20954
21629
  var testAbsoluteIosPhysicalDevice = async (options) => {
20955
21630
  const commands = absoluteIosDeviceAcceptanceCommands(options);
20956
- requireSuccess2(await options.capture(commands.details), "The selected physical iOS device is unavailable. Pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode.");
20957
- const apps = requireSuccess2(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
21631
+ requireSuccess3(await options.capture(commands.details), "The selected physical iOS device is unavailable. Pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode.");
21632
+ const apps = requireSuccess3(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
20958
21633
  if (!apps.stdout.includes(options.appId))
20959
21634
  throw new Error("The AbsoluteJS app is not installed on the selected physical iOS device. Start bun dev with the same --ios-device value first.");
20960
21635
  const now = options.now ?? performance.now.bind(performance);
20961
21636
  const startedAt = now();
20962
- requireSuccess2(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
21637
+ requireSuccess3(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
20963
21638
  await options.waitForHmr();
20964
21639
  return {
20965
21640
  hmrConnected: true,
@@ -20969,7 +21644,7 @@ var testAbsoluteIosPhysicalDevice = async (options) => {
20969
21644
  };
20970
21645
 
20971
21646
  // src/mobile/iosConformance.ts
20972
- import { readFile as readFile11, stat as stat4 } from "fs/promises";
21647
+ import { readFile as readFile13, stat as stat5 } from "fs/promises";
20973
21648
  var HMR_LINE = new RegExp(String.raw`\[hmr:ios\]\s+([^\n]*?)\s+(applied in|falling back to reload after|failed after)\s+(\d+)ms(?:; server\s+(\d+)ms, client\s+(\d+)ms)?`, "u");
20974
21649
  var parseAbsoluteIosHmrLog = (line) => {
20975
21650
  const match = HMR_LINE.exec(line);
@@ -21001,13 +21676,13 @@ var waitForAbsoluteIosHmrLog = async (options) => {
21001
21676
  const sleep = options.sleep ?? Bun.sleep;
21002
21677
  const timeoutMs = options.timeoutMs ?? 30000;
21003
21678
  const deadline = Date.now() + timeoutMs;
21004
- let offset = options.startOffset ?? await stat4(options.logPath).then(({ size }) => size).catch(() => 0);
21679
+ let offset = options.startOffset ?? await stat5(options.logPath).then(({ size }) => size).catch(() => 0);
21005
21680
  let buffered = "";
21006
21681
  const poll = async () => {
21007
21682
  if (Date.now() > deadline)
21008
21683
  throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
21009
21684
  options.signal?.throwIfAborted();
21010
- const contents = await readFile11(options.logPath).catch(() => Buffer.alloc(0));
21685
+ const contents = await readFile13(options.logPath).catch(() => Buffer.alloc(0));
21011
21686
  if (contents.byteLength < offset) {
21012
21687
  offset = 0;
21013
21688
  buffered = "";
@@ -21028,8 +21703,8 @@ var waitForAbsoluteIosHmrLog = async (options) => {
21028
21703
  };
21029
21704
 
21030
21705
  // src/mobile/nativeTestReport.ts
21031
- import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
21032
- import { join as join10 } from "path";
21706
+ import { mkdir as mkdir10, readFile as readFile14, writeFile as writeFile11 } from "fs/promises";
21707
+ import { join as join12 } from "path";
21033
21708
  var secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
21034
21709
  var bearerPattern = /bearer\s+[^\s,;]+/giu;
21035
21710
  var coordinatePattern = /\b(latitude|longitude|lat|lng)(\s*[=:]\s*)-?\d+(?:\.\d+)?/giu;
@@ -21056,8 +21731,10 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
21056
21731
  let developmentDetails = run.status === "pass" ? `The app launched and connected to native HMR in ${run.durationMs}ms.${routeDetails}` : `The automated native run failed after ${run.durationMs}ms: ${run.error ?? "No error detail was available."}`;
21057
21732
  if (run.release)
21058
21733
  developmentDetails = run.status === "pass" ? `The ${run.release.engine} release was installed from its generated APK set, launched offline in ${run.release.launchMs}ms, and relaunched in ${run.release.relaunchMs}ms.` : `Installed ${run.release.engine} release acceptance failed after ${run.durationMs}ms.`;
21734
+ if (run.iosRelease)
21735
+ developmentDetails = run.status === "pass" ? `The ${run.iosRelease.engine} iOS release rendered embedded local content in ${run.iosRelease.launchMs}ms and relaunched in ${run.iosRelease.relaunchMs}ms.` : `Installed ${run.iosRelease.engine} iOS release acceptance failed after ${run.durationMs}ms.`;
21059
21736
  let hmrDetails = "Correlated edit timing was not requested. Rerun with --wait-for-hmr.";
21060
- if (run.release)
21737
+ if (run.release || run.iosRelease)
21061
21738
  hmrDetails = "HMR is intentionally not part of installed production-release acceptance.";
21062
21739
  if (run.hmr)
21063
21740
  hmrDetails = `Observed native HMR ${run.hmr.outcome} in ${run.hmr.durationMs}ms${run.hmr.serverMs === undefined ? "" : ` (server ${run.hmr.serverMs}ms, client ${run.hmr.clientMs}ms)`}.`;
@@ -21096,6 +21773,18 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
21096
21773
  result: run.release.embeddedOffline ? "PASS" : "FAIL"
21097
21774
  });
21098
21775
  }
21776
+ if (run.iosRelease) {
21777
+ const release = run.iosRelease;
21778
+ checks.push({
21779
+ details: `Validated immutable release ${release.releaseId} (${release.artifactBytes} bytes, SHA-256 ${release.artifactSha256}); ${release.distribution} evidence is ${release.artifactExactness}, installed in ${release.installMs}ms.`,
21780
+ id: "AUTO-IOS-RELEASE-01",
21781
+ result: run.status === "pass" ? "PASS" : "FAIL"
21782
+ }, {
21783
+ details: release.networkUnavailable === "user-confirmed" ? "The tester explicitly confirmed Airplane Mode was enabled and Wi-Fi was disabled before both physical-device launches." : "Simulator Release proved embedded local rendering, but does not claim physical-device network-unavailable behavior.",
21784
+ id: "AUTO-IOS-RELEASE-OFFLINE-01",
21785
+ result: release.networkUnavailable === "user-confirmed" ? "PASS" : "SKIPPED"
21786
+ });
21787
+ }
21099
21788
  if (run.upgrade) {
21100
21789
  const { upgrade } = run;
21101
21790
  checks.push({
@@ -21158,7 +21847,7 @@ var createAbsoluteNativeTestReport = (options) => ({
21158
21847
  run: options.run
21159
21848
  });
21160
21849
  var readPackageVersionForNativeReport = async (packageJsonPath) => {
21161
- const manifest = JSON.parse(await readFile12(packageJsonPath, "utf8"));
21850
+ const manifest = JSON.parse(await readFile14(packageJsonPath, "utf8"));
21162
21851
  if (typeof manifest !== "object" || manifest === null)
21163
21852
  return "unknown";
21164
21853
  const version = Reflect.get(manifest, "version");
@@ -21196,13 +21885,13 @@ ${table(report.manualChecks)}
21196
21885
  `;
21197
21886
  };
21198
21887
  var writeAbsoluteNativeTestReport = async (directory, report) => {
21199
- await mkdir8(directory, { recursive: true });
21200
- const jsonPath = join10(directory, "report.json");
21201
- const markdownPath = join10(directory, "report.md");
21888
+ await mkdir10(directory, { recursive: true });
21889
+ const jsonPath = join12(directory, "report.json");
21890
+ const markdownPath = join12(directory, "report.md");
21202
21891
  await Promise.all([
21203
- writeFile10(jsonPath, `${JSON.stringify(report, null, 2)}
21892
+ writeFile11(jsonPath, `${JSON.stringify(report, null, 2)}
21204
21893
  `),
21205
- writeFile10(markdownPath, renderAbsoluteNativeTestReport(report))
21894
+ writeFile11(markdownPath, renderAbsoluteNativeTestReport(report))
21206
21895
  ]);
21207
21896
  return { directory, jsonPath, markdownPath };
21208
21897
  };
@@ -21313,7 +22002,7 @@ var createAbsoluteIosPartnerReport = (options) => {
21313
22002
  absolutejsVersion: options.absolutejsVersion,
21314
22003
  bunVersion: options.bunVersion,
21315
22004
  macosVersion: options.macosVersion,
21316
- provider: "capacitor",
22005
+ provider: options.provider ?? "capacitor",
21317
22006
  xcodeVersion: options.xcodeVersion
21318
22007
  },
21319
22008
  run: {
@@ -21454,8 +22143,8 @@ var createAbsoluteAndroidTestReport = (options) => {
21454
22143
  };
21455
22144
 
21456
22145
  // src/mobile/releasePublisher.ts
21457
- import { access as access7 } from "fs/promises";
21458
- import { isAbsolute as isAbsolute4, relative as relative6, resolve as resolve7, sep as sep4 } from "path";
22146
+ import { access as access8 } from "fs/promises";
22147
+ import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve8, sep as sep5 } from "path";
21459
22148
  import { pathToFileURL as pathToFileURL2 } from "url";
21460
22149
  var prepareAbsoluteIosRelease = async (publisher, options) => {
21461
22150
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -21481,17 +22170,17 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
21481
22170
  var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21482
22171
  var isPublisher = (value) => isRecord6(value) && typeof value.publish === "function";
21483
22172
  var publisherModulePath = (projectRoot, requested) => {
21484
- const root = resolve7(projectRoot);
21485
- const path = resolve7(root, requested);
21486
- const projectRelative = relative6(root, path);
21487
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep4}`) || isAbsolute4(projectRelative)) {
22173
+ const root = resolve8(projectRoot);
22174
+ const path = resolve8(root, requested);
22175
+ const projectRelative = relative7(root, path);
22176
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute5(projectRelative)) {
21488
22177
  throw new TypeError("mobile publish --registry must remain inside the project.");
21489
22178
  }
21490
22179
  return path;
21491
22180
  };
21492
22181
  var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
21493
22182
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
21494
- await access7(modulePath).catch(() => {
22183
+ await access8(modulePath).catch(() => {
21495
22184
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
21496
22185
  });
21497
22186
  const loaded = await import(pathToFileURL2(modulePath).href);
@@ -21553,8 +22242,8 @@ var publishAbsoluteIosRelease = async (options) => {
21553
22242
  };
21554
22243
 
21555
22244
  // src/mobile/mobileInspect.ts
21556
- import { access as access8, readFile as readFile13 } from "fs/promises";
21557
- import { join as join11, relative as relative7, resolve as resolve8 } from "path";
22245
+ import { access as access9, readFile as readFile15 } from "fs/promises";
22246
+ import { join as join13, relative as relative8, resolve as resolve9 } from "path";
21558
22247
  var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
21559
22248
  var MOBILE_PACKAGE_NAMES = new Set([
21560
22249
  "@absolutejs/absolute",
@@ -21569,19 +22258,19 @@ var MOBILE_PACKAGE_NAMES = new Set([
21569
22258
  ]);
21570
22259
  var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21571
22260
  var portablePath2 = (projectRoot, path) => {
21572
- const value = relative7(resolve8(projectRoot), resolve8(path)).replaceAll("\\", "/");
22261
+ const value = relative8(resolve9(projectRoot), resolve9(path)).replaceAll("\\", "/");
21573
22262
  return value || ".";
21574
22263
  };
21575
22264
  var pathExists4 = async (path) => {
21576
22265
  try {
21577
- await access8(path);
22266
+ await access9(path);
21578
22267
  return true;
21579
22268
  } catch {
21580
22269
  return false;
21581
22270
  }
21582
22271
  };
21583
22272
  var readObject2 = async (path) => {
21584
- const value = JSON.parse(await readFile13(path, "utf8"));
22273
+ const value = JSON.parse(await readFile15(path, "utf8"));
21585
22274
  if (!isObject2(value))
21586
22275
  throw new TypeError("JSON root must be an object.");
21587
22276
  return value;
@@ -21593,13 +22282,13 @@ var addPackageDeclarations = (declarations, value) => {
21593
22282
  declarations.set(name, declared);
21594
22283
  };
21595
22284
  var packageInspections = async (projectRoot, additionalNames) => {
21596
- const project = await readObject2(join11(projectRoot, "package.json"));
22285
+ const project = await readObject2(join13(projectRoot, "package.json"));
21597
22286
  const declarations = new Map;
21598
22287
  for (const field2 of ["dependencies", "devDependencies"])
21599
22288
  addPackageDeclarations(declarations, project[field2]);
21600
22289
  const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
21601
22290
  return Promise.all(names.map(async (name) => {
21602
- const installedManifest = await readObject2(join11(projectRoot, "node_modules", name, "package.json")).catch(() => {
22291
+ const installedManifest = await readObject2(join13(projectRoot, "node_modules", name, "package.json")).catch(() => {
21603
22292
  return;
21604
22293
  });
21605
22294
  const installed = installedManifest?.version;
@@ -21658,7 +22347,7 @@ var inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) =>
21658
22347
  },
21659
22348
  format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
21660
22349
  nativeProjects: await Promise.all(config.platforms.map(async (platform) => {
21661
- const path = join11(config.nativeProjectDirectory, platform);
22350
+ const path = join13(config.nativeProjectDirectory, platform);
21662
22351
  return {
21663
22352
  initialized: await pathExists4(path),
21664
22353
  path: portablePath2(projectRoot, path),
@@ -21715,8 +22404,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
21715
22404
 
21716
22405
  // src/mobile/ciWorkflow.ts
21717
22406
  import { existsSync } from "fs";
21718
- import { access as access9, mkdir as mkdir9, readFile as readFile14, writeFile as writeFile11 } from "fs/promises";
21719
- import { dirname as dirname8, extname as extname2, relative as relative8, resolve as resolve9, sep as sep5 } from "path";
22407
+ import { access as access10, mkdir as mkdir11, readFile as readFile16, writeFile as writeFile12 } from "fs/promises";
22408
+ import { dirname as dirname10, extname as extname2, relative as relative9, resolve as resolve10, sep as sep6 } from "path";
21720
22409
  var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
21721
22410
  var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
21722
22411
  var CI_ENV_INDENTATION = 6;
@@ -21735,9 +22424,9 @@ var RESERVED_SECRET_NAMES = new Set([
21735
22424
  "APP_STORE_CONNECT_KEY_ID",
21736
22425
  "APP_STORE_CONNECT_PRIVATE_KEY_BASE64"
21737
22426
  ]);
21738
- var exists3 = async (path) => {
22427
+ var exists4 = async (path) => {
21739
22428
  try {
21740
- await access9(path);
22429
+ await access10(path);
21741
22430
  return true;
21742
22431
  } catch {
21743
22432
  return false;
@@ -21745,10 +22434,10 @@ var exists3 = async (path) => {
21745
22434
  };
21746
22435
  var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
21747
22436
  var projectPath2 = (projectRoot, value, field2, options = {}) => {
21748
- const root = resolve9(projectRoot);
21749
- const path = resolve9(root, value);
21750
- const portable = relative8(root, path).replaceAll("\\", "/");
21751
- if (portable === ".." || portable.startsWith(`..${sep5}`) || portable.startsWith("../") || portable === "") {
22437
+ const root = resolve10(projectRoot);
22438
+ const path = resolve10(root, value);
22439
+ const portable = relative9(root, path).replaceAll("\\", "/");
22440
+ if (portable === ".." || portable.startsWith(`..${sep6}`) || portable.startsWith("../") || portable === "") {
21752
22441
  throw new TypeError(`${field2} must remain inside the project root.`);
21753
22442
  }
21754
22443
  if (/\r|\n/u.test(portable) || portable.startsWith("-"))
@@ -21758,11 +22447,11 @@ var projectPath2 = (projectRoot, value, field2, options = {}) => {
21758
22447
  return portable;
21759
22448
  };
21760
22449
  var workflowOutputPath = (projectRoot, value) => {
21761
- const root = resolve9(projectRoot);
21762
- const workflows = resolve9(root, ".github/workflows");
21763
- const path = resolve9(root, value ?? ".github/workflows/absolute-mobile.yml");
21764
- const portable = relative8(workflows, path);
21765
- if (portable === ".." || portable.startsWith(`..${sep5}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
22450
+ const root = resolve10(projectRoot);
22451
+ const workflows = resolve10(root, ".github/workflows");
22452
+ const path = resolve10(root, value ?? ".github/workflows/absolute-mobile.yml");
22453
+ const portable = relative9(workflows, path);
22454
+ if (portable === ".." || portable.startsWith(`..${sep6}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
21766
22455
  throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
21767
22456
  }
21768
22457
  return path;
@@ -22191,12 +22880,12 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
22191
22880
  var writeAbsoluteMobileGithubWorkflow = async (options) => {
22192
22881
  const path = workflowOutputPath(options.projectRoot, options.outputPath);
22193
22882
  const generated = createAbsoluteMobileGithubWorkflow(options);
22194
- const previous = await exists3(path) ? await readFile14(path, "utf8") : undefined;
22883
+ const previous = await exists4(path) ? await readFile16(path, "utf8") : undefined;
22195
22884
  if (previous !== undefined && previous !== generated.workflow && !options.force)
22196
- throw new TypeError(`${relative8(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
22885
+ throw new TypeError(`${relative9(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
22197
22886
  if (previous !== generated.workflow) {
22198
- await mkdir9(dirname8(path), { recursive: true });
22199
- await writeFile11(path, generated.workflow);
22887
+ await mkdir11(dirname10(path), { recursive: true });
22888
+ await writeFile12(path, generated.workflow);
22200
22889
  }
22201
22890
  return {
22202
22891
  changed: previous !== generated.workflow,
@@ -22209,46 +22898,46 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
22209
22898
  };
22210
22899
 
22211
22900
  // src/mobile/updateSigning.ts
22212
- import { createHash as createHash4, sign, verify } from "crypto";
22901
+ import { createHash as createHash5, sign, verify } from "crypto";
22213
22902
  import {
22214
22903
  cp,
22215
- mkdir as mkdir10,
22216
- mkdtemp as mkdtemp3,
22217
- readdir as readdir4,
22218
- readFile as readFile15,
22219
- rename as rename9,
22220
- rm as rm6,
22221
- stat as stat5,
22222
- writeFile as writeFile12
22904
+ mkdir as mkdir12,
22905
+ mkdtemp as mkdtemp4,
22906
+ readdir as readdir6,
22907
+ readFile as readFile17,
22908
+ rename as rename10,
22909
+ rm as rm7,
22910
+ stat as stat6,
22911
+ writeFile as writeFile13
22223
22912
  } from "fs/promises";
22224
- import { dirname as dirname9, join as join12, relative as relative9, resolve as resolve10 } from "path";
22913
+ import { dirname as dirname11, join as join14, relative as relative10, resolve as resolve11 } from "path";
22225
22914
  var UPDATE_MANIFEST_FILE = "update.json";
22226
22915
  var UPDATE_FILES_DIRECTORY = "files";
22227
- var sha2562 = (value) => createHash4("sha256").update(value).digest("hex");
22916
+ var sha2563 = (value) => createHash5("sha256").update(value).digest("hex");
22228
22917
  var listFiles = async (root, directory = root) => {
22229
- const entries = await readdir4(directory, { withFileTypes: true });
22918
+ const entries = await readdir6(directory, { withFileTypes: true });
22230
22919
  const paths = await Promise.all(entries.map(async (entry) => {
22231
- const path = join12(directory, entry.name);
22920
+ const path = join14(directory, entry.name);
22232
22921
  if (entry.isDirectory())
22233
22922
  return listFiles(root, path);
22234
22923
  if (!entry.isFile())
22235
22924
  throw new TypeError("Mobile updates cannot contain links or special files.");
22236
- return [relative9(root, path).replaceAll("\\", "/")];
22925
+ return [relative10(root, path).replaceAll("\\", "/")];
22237
22926
  }));
22238
22927
  return paths.flat().sort((left, right) => left.localeCompare(right));
22239
22928
  };
22240
22929
  var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
22241
- const source = join12(root, path);
22930
+ const source = join14(root, path);
22242
22931
  const [metadata, contents] = await Promise.all([
22243
- stat5(source),
22244
- readFile15(source)
22932
+ stat6(source),
22933
+ readFile17(source)
22245
22934
  ]);
22246
- return { bytes: metadata.size, path, sha256: sha2562(contents) };
22935
+ return { bytes: metadata.size, path, sha256: sha2563(contents) };
22247
22936
  }));
22248
- var releaseIdFor = (value) => `amu_${sha2562(canonicalizeAbsoluteMobileUpdate(value))}`;
22937
+ var releaseIdFor = (value) => `amu_${sha2563(canonicalizeAbsoluteMobileUpdate(value))}`;
22249
22938
  var buildAbsoluteMobileUpdate = async (options) => {
22250
- const bundleDirectory = resolve10(options.bundleDirectory);
22251
- const outputRoot = resolve10(options.outputDirectory);
22939
+ const bundleDirectory = resolve11(options.bundleDirectory);
22940
+ const outputRoot = resolve11(options.outputDirectory);
22252
22941
  if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
22253
22942
  throw new TypeError("Mobile update output must be outside the embedded bundle.");
22254
22943
  const paths = await listFiles(bundleDirectory);
@@ -22276,30 +22965,30 @@ var buildAbsoluteMobileUpdate = async (options) => {
22276
22965
  value: signature.toString("base64")
22277
22966
  }
22278
22967
  });
22279
- await mkdir10(outputRoot, { recursive: true });
22280
- const outputDirectory = join12(outputRoot, manifest.releaseId);
22281
- const staging = await mkdtemp3(join12(outputRoot, ".stage-"));
22968
+ await mkdir12(outputRoot, { recursive: true });
22969
+ const outputDirectory = join14(outputRoot, manifest.releaseId);
22970
+ const staging = await mkdtemp4(join14(outputRoot, ".stage-"));
22282
22971
  try {
22283
- await cp(bundleDirectory, join12(staging, UPDATE_FILES_DIRECTORY), {
22972
+ await cp(bundleDirectory, join14(staging, UPDATE_FILES_DIRECTORY), {
22284
22973
  force: true,
22285
22974
  recursive: true
22286
22975
  });
22287
- await writeFile12(join12(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
22976
+ await writeFile13(join14(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
22288
22977
  `);
22289
- await rename9(staging, outputDirectory);
22978
+ await rename10(staging, outputDirectory);
22290
22979
  } catch (error) {
22291
- await rm6(staging, { force: true, recursive: true });
22980
+ await rm7(staging, { force: true, recursive: true });
22292
22981
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
22293
22982
  throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
22294
22983
  throw error;
22295
22984
  }
22296
22985
  return {
22297
22986
  manifest,
22298
- manifestPath: join12(outputDirectory, UPDATE_MANIFEST_FILE),
22987
+ manifestPath: join14(outputDirectory, UPDATE_MANIFEST_FILE),
22299
22988
  outputDirectory
22300
22989
  };
22301
22990
  };
22302
- var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile15(join12(resolve10(directory), UPDATE_MANIFEST_FILE), "utf8")));
22991
+ var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile17(join14(resolve11(directory), UPDATE_MANIFEST_FILE), "utf8")));
22303
22992
  var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
22304
22993
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
22305
22994
  const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
@@ -22309,8 +22998,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
22309
22998
  };
22310
22999
 
22311
23000
  // src/mobile/expoUpdate.ts
22312
- import { access as access10, mkdir as mkdir11, readFile as readFile16, writeFile as writeFile13 } from "fs/promises";
22313
- import { dirname as dirname10, extname as extname3, join as join13 } from "path";
23001
+ import { access as access11, mkdir as mkdir13, readFile as readFile18, writeFile as writeFile14 } from "fs/promises";
23002
+ import { dirname as dirname12, extname as extname3, join as join15 } from "path";
22314
23003
  var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
22315
23004
  var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
22316
23005
  var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -22376,7 +23065,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
22376
23065
  };
22377
23066
  };
22378
23067
  var finalizeAbsoluteExpoUpdateExport = async (options) => {
22379
- const metadata = JSON.parse(await readFile16(join13(options.exportDirectory, "metadata.json"), "utf8"));
23068
+ const metadata = JSON.parse(await readFile18(join15(options.exportDirectory, "metadata.json"), "utf8"));
22380
23069
  if (!object2(metadata) || !object2(metadata.fileMetadata))
22381
23070
  throw new TypeError("Expo export metadata.json is invalid.");
22382
23071
  const descriptor = parseAbsoluteExpoUpdateDescriptor({
@@ -22387,33 +23076,33 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
22387
23076
  runtimeVersion: options.runtimeVersion
22388
23077
  });
22389
23078
  const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
22390
- await Promise.all(referenced.map(({ path }) => access10(join13(options.exportDirectory, path)).catch(() => {
23079
+ await Promise.all(referenced.map(({ path }) => access11(join15(options.exportDirectory, path)).catch(() => {
22391
23080
  throw new TypeError(`Expo export metadata references missing asset ${path}.`);
22392
23081
  })));
22393
- const destination = join13(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
22394
- await mkdir11(dirname10(destination), { recursive: true });
22395
- await writeFile13(destination, `${JSON.stringify(descriptor, null, "\t")}
23082
+ const destination = join15(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
23083
+ await mkdir13(dirname12(destination), { recursive: true });
23084
+ await writeFile14(destination, `${JSON.stringify(descriptor, null, "\t")}
22396
23085
  `);
22397
23086
  return { descriptor, path: destination };
22398
23087
  };
22399
23088
 
22400
23089
  // src/mobile/expoCodeSigning.ts
22401
23090
  var import_code_signing_certificates = __toESM(require_main(), 1);
22402
- import { access as access11, mkdir as mkdir12, writeFile as writeFile14 } from "fs/promises";
22403
- import { dirname as dirname11, isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep6 } from "path";
22404
- var exists4 = async (path) => access11(path).then(() => true).catch(() => false);
22405
- var inside2 = (root, path) => {
22406
- const location = relative10(root, path);
22407
- return location === "" || location !== ".." && !location.startsWith(`..${sep6}`) && !isAbsolute5(location);
23091
+ import { access as access12, mkdir as mkdir14, writeFile as writeFile15 } from "fs/promises";
23092
+ import { dirname as dirname13, isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep7 } from "path";
23093
+ var exists5 = async (path) => access12(path).then(() => true).catch(() => false);
23094
+ var inside3 = (root, path) => {
23095
+ const location = relative11(root, path);
23096
+ return location === "" || location !== ".." && !location.startsWith(`..${sep7}`) && !isAbsolute6(location);
22408
23097
  };
22409
23098
  var generateAbsoluteExpoCodeSigning = async (options) => {
22410
- const root = resolve11(options.projectRoot);
22411
- const certificatePath = resolve11(root, options.certificatePath);
22412
- const privateKeyPath = resolve11(root, options.privateKeyPath);
22413
- const publicKeyPath = resolve11(root, options.publicKeyPath ?? resolve11(dirname11(privateKeyPath), "public-key.pem"));
22414
- if (!inside2(root, certificatePath))
23099
+ const root = resolve12(options.projectRoot);
23100
+ const certificatePath = resolve12(root, options.certificatePath);
23101
+ const privateKeyPath = resolve12(root, options.privateKeyPath);
23102
+ const publicKeyPath = resolve12(root, options.publicKeyPath ?? resolve12(dirname13(privateKeyPath), "public-key.pem"));
23103
+ if (!inside3(root, certificatePath))
22415
23104
  throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
22416
- if (inside2(root, privateKeyPath) || inside2(root, publicKeyPath))
23105
+ if (inside3(root, privateKeyPath) || inside3(root, publicKeyPath))
22417
23106
  throw new TypeError("Expo code-signing private/public key output must remain outside the project and source control.");
22418
23107
  const commonName = options.commonName.trim();
22419
23108
  if (!commonName || commonName.length > 64)
@@ -22422,7 +23111,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22422
23111
  if (!Number.isSafeInteger(validityYears) || validityYears < 1 || validityYears > 20)
22423
23112
  throw new TypeError("Expo code-signing validity must be an integer from 1 through 20 years.");
22424
23113
  const collisions = (await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map(async (path) => ({
22425
- exists: await exists4(path),
23114
+ exists: await exists5(path),
22426
23115
  path
22427
23116
  })))).filter((entry) => entry.exists);
22428
23117
  if (collisions.length > 0)
@@ -22440,11 +23129,11 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22440
23129
  import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
22441
23130
  const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
22442
23131
  const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
22443
- await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir12(dirname11(path), { recursive: true })));
23132
+ await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir14(dirname13(path), { recursive: true })));
22444
23133
  await Promise.all([
22445
- writeFile14(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
22446
- writeFile14(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
22447
- writeFile14(publicKeyPath, publicKeyPEM, { flag: "wx", mode: 420 })
23134
+ writeFile15(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
23135
+ writeFile15(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
23136
+ writeFile15(publicKeyPath, publicKeyPEM, { flag: "wx", mode: 420 })
22448
23137
  ]);
22449
23138
  return {
22450
23139
  certificatePath,
@@ -22455,8 +23144,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22455
23144
  };
22456
23145
 
22457
23146
  // src/mobile/updatePublisher.ts
22458
- import { access as access12 } from "fs/promises";
22459
- import { isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep7 } from "path";
23147
+ import { access as access13 } from "fs/promises";
23148
+ import { isAbsolute as isAbsolute7, relative as relative12, resolve as resolve13, sep as sep8 } from "path";
22460
23149
  import { pathToFileURL as pathToFileURL3 } from "url";
22461
23150
  var lifecycleMethod = (publisher, name) => {
22462
23151
  const method = publisher[name];
@@ -22565,16 +23254,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
22565
23254
  var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22566
23255
  var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
22567
23256
  var projectPath3 = (projectRoot, requested, label) => {
22568
- const root = resolve12(projectRoot);
22569
- const path = resolve12(root, requested);
22570
- const projectRelative = relative11(root, path);
22571
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep7}`) || isAbsolute6(projectRelative))
23257
+ const root = resolve13(projectRoot);
23258
+ const path = resolve13(root, requested);
23259
+ const projectRelative = relative12(root, path);
23260
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep8}`) || isAbsolute7(projectRelative))
22572
23261
  throw new TypeError(`${label} must remain inside the project.`);
22573
23262
  return path;
22574
23263
  };
22575
23264
  var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
22576
23265
  const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
22577
- await access12(modulePath).catch(() => {
23266
+ await access13(modulePath).catch(() => {
22578
23267
  throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
22579
23268
  });
22580
23269
  const loaded = await import(pathToFileURL3(modulePath).href);
@@ -22672,7 +23361,7 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
22672
23361
  ];
22673
23362
  var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
22674
23363
  var directProjectPackages = async (projectRoot) => {
22675
- const manifest = JSON.parse(await readFile17(join14(projectRoot, "package.json"), "utf8"));
23364
+ const manifest = JSON.parse(await readFile19(join16(projectRoot, "package.json"), "utf8"));
22676
23365
  if (!isRecord7(manifest))
22677
23366
  throw new TypeError("Application package.json must contain an object.");
22678
23367
  const names = new Set;
@@ -22686,7 +23375,7 @@ var directProjectPackages = async (projectRoot) => {
22686
23375
  };
22687
23376
  var resolvedPackageVersion = async (projectRoot, packageName) => {
22688
23377
  try {
22689
- const manifest = JSON.parse(await readFile17(join14(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
23378
+ const manifest = JSON.parse(await readFile19(join16(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
22690
23379
  return isRecord7(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
22691
23380
  } catch {
22692
23381
  return;
@@ -22736,18 +23425,8 @@ var requireMobileConfig = (value) => {
22736
23425
  }
22737
23426
  return value;
22738
23427
  };
22739
- var capacitorExecutable = async (projectRoot) => {
22740
- const executable = join14(projectRoot, "node_modules", ".bin", "cap");
22741
- try {
22742
- await access13(executable);
22743
- return executable;
22744
- } catch {
22745
- throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
22746
- }
22747
- };
22748
23428
  var runCapacitor = async (projectRoot, args) => {
22749
- const executable = await capacitorExecutable(projectRoot);
22750
- const process2 = Bun.spawn([executable, ...args], {
23429
+ const process2 = Bun.spawn(["bun", "x", "--no-install", "cap", ...args], {
22751
23430
  cwd: projectRoot,
22752
23431
  stderr: "inherit",
22753
23432
  stdin: "inherit",
@@ -22755,14 +23434,14 @@ var runCapacitor = async (projectRoot, args) => {
22755
23434
  });
22756
23435
  const exitCode = await process2.exited;
22757
23436
  if (exitCode !== 0) {
22758
- throw new TypeError(`Capacitor exited with status ${exitCode}.`);
23437
+ throw new TypeError(`Capacitor exited with status ${exitCode}. Ensure this app directly depends on: ${CAPACITOR_PACKAGES.join(" ")}`);
22759
23438
  }
22760
23439
  };
22761
23440
  var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
22762
23441
  var expoExecutable = async (project) => {
22763
- const executable = join14(project, "node_modules", ".bin", "expo");
23442
+ const executable = join16(project, "node_modules", ".bin", "expo");
22764
23443
  try {
22765
- await access13(executable);
23444
+ await access14(executable);
22766
23445
  return executable;
22767
23446
  } catch {
22768
23447
  throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
@@ -22794,14 +23473,14 @@ var runExpo = async (project, args, options = {}) => {
22794
23473
  };
22795
23474
  var ensureExpoPackages = async (project, args) => {
22796
23475
  try {
22797
- const manifest = JSON.parse(await readFile17(join14(project, "package.json"), "utf8"));
23476
+ const manifest = JSON.parse(await readFile19(join16(project, "package.json"), "utf8"));
22798
23477
  const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
22799
23478
  if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
22800
23479
  throw new TypeError("Generated Expo dependencies are invalid.");
22801
23480
  await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
22802
23481
  if (typeof expected !== "string")
22803
23482
  throw new TypeError("Generated Expo dependency version is invalid.");
22804
- const installed = JSON.parse(await readFile17(join14(project, "node_modules", name, "package.json"), "utf8"));
23483
+ const installed = JSON.parse(await readFile19(join16(project, "node_modules", name, "package.json"), "utf8"));
22805
23484
  const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
22806
23485
  if (typeof actual !== "string")
22807
23486
  throw new TypeError("Installed Expo dependency version is invalid.");
@@ -22959,6 +23638,7 @@ var initialize = async (args) => {
22959
23638
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile);
22960
23639
  await applyAbsoluteNativeUpdates(mobile);
22961
23640
  await applyAbsoluteNativeObservability(mobile);
23641
+ await applyAbsoluteNativeReleaseReadiness(mobile);
22962
23642
  };
22963
23643
  var sync = async (args) => {
22964
23644
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
@@ -22994,10 +23674,11 @@ var sync = async (args) => {
22994
23674
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, platforms);
22995
23675
  await applyAbsoluteNativeUpdates(mobile, platforms);
22996
23676
  await applyAbsoluteNativeObservability(mobile, platforms);
23677
+ await applyAbsoluteNativeReleaseReadiness(mobile, platforms);
22997
23678
  };
22998
23679
  var associations = async (args) => {
22999
23680
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
23000
- const outputDirectory = resolve13(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
23681
+ const outputDirectory = resolve14(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
23001
23682
  if (args.includes("--verify")) {
23002
23683
  const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
23003
23684
  console.log(`Verified ${result2.results.length} hosted association files`);
@@ -23049,7 +23730,7 @@ var generateGithubCi = async (args) => {
23049
23730
  const publicResult = {
23050
23731
  changed: result.changed,
23051
23732
  format: result.format,
23052
- path: relative12(projectRoot, result.path).replaceAll("\\", "/"),
23733
+ path: relative13(projectRoot, result.path).replaceAll("\\", "/"),
23053
23734
  platforms: result.platforms,
23054
23735
  publishing: result.publishing,
23055
23736
  requiredSecrets: result.requiredSecrets
@@ -23160,9 +23841,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
23160
23841
  temporaryDirectory: undefined
23161
23842
  };
23162
23843
  await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
23163
- const temporaryParent = join14(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
23164
- await mkdir13(temporaryParent, { recursive: true });
23165
- const temporaryDirectory = await mkdtemp4(join14(temporaryParent, ".stage-"));
23844
+ const temporaryParent = join16(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
23845
+ await mkdir15(temporaryParent, { recursive: true });
23846
+ const temporaryDirectory = await mkdtemp5(join16(temporaryParent, ".stage-"));
23166
23847
  try {
23167
23848
  await runExpo(mobile.nativeProjectDirectory, [
23168
23849
  "export",
@@ -23174,7 +23855,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
23174
23855
  temporaryDirectory,
23175
23856
  "--clear"
23176
23857
  ], { production: true });
23177
- const generatedApp = JSON.parse(await readFile17(join14(mobile.nativeProjectDirectory, "app.json"), "utf8"));
23858
+ const generatedApp = JSON.parse(await readFile19(join16(mobile.nativeProjectDirectory, "app.json"), "utf8"));
23178
23859
  const expoConfig = isRecord7(generatedApp) ? generatedApp.expo : undefined;
23179
23860
  if (!isRecord7(expoConfig))
23180
23861
  throw new TypeError("Generated Expo app configuration is invalid.");
@@ -23192,7 +23873,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
23192
23873
  temporaryDirectory
23193
23874
  };
23194
23875
  } catch (error) {
23195
- await rm7(temporaryDirectory, { force: true, recursive: true });
23876
+ await rm8(temporaryDirectory, { force: true, recursive: true });
23196
23877
  throw error;
23197
23878
  }
23198
23879
  };
@@ -23217,11 +23898,11 @@ var buildMobileUpdate = async (args) => {
23217
23898
  let expoExportDirectory;
23218
23899
  try {
23219
23900
  await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
23220
- const embedded = JSON.parse(await readFile17(join14(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
23901
+ const embedded = JSON.parse(await readFile19(join16(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
23221
23902
  const runtimeFingerprint = isRecord7(embedded) ? embedded.nativeRuntime : undefined;
23222
23903
  if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
23223
23904
  throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
23224
- const privateKey = await readFile17(resolve13(projectRoot, signingKeyPath));
23905
+ const privateKey = await readFile19(resolve14(projectRoot, signingKeyPath));
23225
23906
  const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
23226
23907
  const derivedPublicKey = createPublicKey2(privateKey).export({
23227
23908
  format: "der",
@@ -23242,7 +23923,7 @@ var buildMobileUpdate = async (args) => {
23242
23923
  channel: mobile.updates.channel,
23243
23924
  classification,
23244
23925
  keyId,
23245
- outputDirectory: valueAfter(args, "--outdir") ?? join14(projectRoot, ".absolutejs", "mobile", "updates"),
23926
+ outputDirectory: valueAfter(args, "--outdir") ?? join16(projectRoot, ".absolutejs", "mobile", "updates"),
23246
23927
  privateKey,
23247
23928
  runtimeFingerprint
23248
23929
  });
@@ -23254,7 +23935,7 @@ var buildMobileUpdate = async (args) => {
23254
23935
  return result;
23255
23936
  } finally {
23256
23937
  if (expoExportDirectory)
23257
- await rm7(expoExportDirectory, { force: true, recursive: true });
23938
+ await rm8(expoExportDirectory, { force: true, recursive: true });
23258
23939
  sendTelemetryEvent("mobile:update-build", {
23259
23940
  classification: classification ?? "invalid",
23260
23941
  durationMs: Math.round(performance.now() - startedAt),
@@ -23284,7 +23965,7 @@ var generateExpoUpdateSigning = async (args) => {
23284
23965
  ...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
23285
23966
  ...validityYears === undefined ? {} : { validityYears }
23286
23967
  });
23287
- const certificatePath = relative12(projectRoot, result.certificatePath).replaceAll("\\", "/");
23968
+ const certificatePath = relative13(projectRoot, result.certificatePath).replaceAll("\\", "/");
23288
23969
  sendTelemetryEvent("mobile:update-code-signing-generated", {
23289
23970
  engine: "expo",
23290
23971
  validityYears: validityYears ?? 10
@@ -23346,7 +24027,7 @@ var provisionMobileUpdate = async (args) => {
23346
24027
  publicKeys: mobile.updates.publicKeys,
23347
24028
  storage: requestedStorage
23348
24029
  });
23349
- console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative12(projectRoot, path).replaceAll("\\", "/")}.`);
24030
+ console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative13(projectRoot, path).replaceAll("\\", "/")}.`);
23350
24031
  if (requestedStorage === "local")
23351
24032
  console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
23352
24033
  else
@@ -23758,6 +24439,7 @@ var prepareCapacitorIosReleaseProject = async (mobile, projectRoot) => {
23758
24439
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["ios"]);
23759
24440
  await applyAbsoluteNativeUpdates(mobile, ["ios"]);
23760
24441
  await applyAbsoluteNativeObservability(mobile, ["ios"]);
24442
+ await applyAbsoluteNativeReleaseReadiness(mobile, ["ios"]);
23761
24443
  };
23762
24444
  var prepareIosReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoIosReleaseProject(mobile, projectRoot, args) : prepareCapacitorIosReleaseProject(mobile, projectRoot);
23763
24445
  var prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
@@ -23770,6 +24452,7 @@ var prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
23770
24452
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["android"]);
23771
24453
  await applyAbsoluteNativeUpdates(mobile, ["android"]);
23772
24454
  await applyAbsoluteNativeObservability(mobile, ["android"]);
24455
+ await applyAbsoluteNativeReleaseReadiness(mobile, ["android"]);
23773
24456
  };
23774
24457
  var prepareAndroidReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoAndroidReleaseProject(mobile, projectRoot, args) : prepareCapacitorAndroidReleaseProject(mobile, projectRoot);
23775
24458
  var buildAndroid = async (args, prepareVersionCode) => {
@@ -23821,7 +24504,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
23821
24504
  const durationMs = Math.round(performance.now() - startedAt);
23822
24505
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
23823
24506
  console.log(`Artifact: ${release.artifactPath}`);
23824
- console.log(`Metadata: ${join14(release.releaseRoot, "release.json")}`);
24507
+ console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
23825
24508
  return release;
23826
24509
  } finally {
23827
24510
  sendTelemetryEvent("mobile:android-release-build", {
@@ -23909,6 +24592,7 @@ var buildLocalIosRelease = async (options) => {
23909
24592
  developmentTeam: process.env.ABSOLUTE_IOS_DEVELOPMENT_TEAM,
23910
24593
  ...options.mobile.engine === "expo" ? { env: expoProductionEnvironment() } : {},
23911
24594
  outputDirectory: valueAfter(options.args, "--outdir"),
24595
+ registeredDeviceArtifact: options.args.includes("--registered-device-artifact"),
23912
24596
  ...options.prepareBuildNumber === undefined ? {} : { prepareBuildNumber: options.prepareBuildNumber },
23913
24597
  projectRoot: options.projectRoot
23914
24598
  });
@@ -23939,6 +24623,8 @@ var buildIos = async (args, prepareBuildNumber) => {
23939
24623
  const remoteProfile = requestedRemote !== undefined || process.platform !== "darwin" ? await getAbsoluteRemoteMacProfile(requestedRemote, remoteProfilePath()) : undefined;
23940
24624
  if (process.platform !== "darwin" && !remoteProfile)
23941
24625
  throw new TypeError("iOS release builds require macOS or a paired Remote Mac. Run `absolute mobile pair mac <name> <user@host>`.");
24626
+ if (remoteProfile && args.includes("--registered-device-artifact"))
24627
+ throw new TypeError("--registered-device-artifact currently requires a local Mac; paired Remote Mac transfer support is not yet available.");
23942
24628
  let success = false;
23943
24629
  const cancellation = remoteProfile ? new AbortController : undefined;
23944
24630
  let stopListeningForCancellation = () => {
@@ -23977,7 +24663,9 @@ var buildIos = async (args, prepareBuildNumber) => {
23977
24663
  const durationMs = Math.round(performance.now() - startedAt);
23978
24664
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
23979
24665
  console.log(`Artifact: ${release.artifactPath}`);
23980
- console.log(`Metadata: ${join14(release.releaseRoot, "release.json")}`);
24666
+ if (release.registeredArtifactPath)
24667
+ console.log(`Registered-device artifact: ${release.registeredArtifactPath}`);
24668
+ console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
23981
24669
  return release;
23982
24670
  } finally {
23983
24671
  stopListeningForCancellation();
@@ -24089,7 +24777,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
24089
24777
  checks.push({
24090
24778
  id: "sync.storage-schema",
24091
24779
  label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
24092
- path: join14(projectRoot, "package.json"),
24780
+ path: join16(projectRoot, "package.json"),
24093
24781
  platform: "host",
24094
24782
  status: "pass"
24095
24783
  });
@@ -24097,7 +24785,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
24097
24785
  checks.push({
24098
24786
  id: "sync.storage-schema",
24099
24787
  label: "Offline schema metadata is invalid",
24100
- path: join14(projectRoot, "package.json"),
24788
+ path: join16(projectRoot, "package.json"),
24101
24789
  platform: "host",
24102
24790
  remediation: error instanceof Error ? error.message : String(error),
24103
24791
  status: "fail"
@@ -24164,7 +24852,7 @@ AbsoluteJS can configure ${installPlatform} emulation:`);
24164
24852
  Emulator setup verification:`);
24165
24853
  printDoctorChecks(result.checks.filter((check) => check.platform === "host" || check.platform === installPlatform));
24166
24854
  };
24167
- var captureCommand3 = (command) => {
24855
+ var captureCommand4 = (command) => {
24168
24856
  const result = Bun.spawnSync(command, {
24169
24857
  stderr: "pipe",
24170
24858
  stdin: "ignore",
@@ -24185,7 +24873,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
24185
24873
  }
24186
24874
  return { https: args.includes("--https"), port };
24187
24875
  }
24188
- const instances = listLiveInstances().filter((instance2) => resolve13(instance2.cwd) === resolve13(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24876
+ const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24189
24877
  if (instances.length !== 1) {
24190
24878
  throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for Android to report ready, then run `absolute mobile test android`." : "Multiple dev servers are running for this project. Select one with mobile test android --port <port>.");
24191
24879
  }
@@ -24214,7 +24902,7 @@ var requireAndroidAdb = async () => {
24214
24902
  return adb2;
24215
24903
  };
24216
24904
  var selectAndroidSerial = (adb2, explicitSerial) => {
24217
- const devices = captureCommand3([adb2, "devices"]);
24905
+ const devices = captureCommand4([adb2, "devices"]);
24218
24906
  if (devices.exitCode !== 0) {
24219
24907
  throw new Error(`Could not list Android targets: ${devices.stderr.trim() || devices.stdout.trim()}`);
24220
24908
  }
@@ -24234,7 +24922,7 @@ var selectAndroidSerial = (adb2, explicitSerial) => {
24234
24922
  var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial, json) => {
24235
24923
  if (explicitSerial)
24236
24924
  return selectAndroidSerial(adb2, explicitSerial);
24237
- const current = captureCommand3([adb2, "devices"]);
24925
+ const current = captureCommand4([adb2, "devices"]);
24238
24926
  const ready = current.exitCode === 0 ? parseAdbDevices(current.stdout).find((serial) => serial.startsWith("emulator-")) : undefined;
24239
24927
  if (ready)
24240
24928
  return ready;
@@ -24253,11 +24941,11 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
24253
24941
  launched.unref();
24254
24942
  const startedAt = performance.now();
24255
24943
  const bootedSerial = () => {
24256
- const devices = captureCommand3([adb2, "devices"]);
24944
+ const devices = captureCommand4([adb2, "devices"]);
24257
24945
  const serial = devices.exitCode === 0 ? parseAdbDevices(devices.stdout).find((candidate) => candidate.startsWith("emulator-")) : undefined;
24258
24946
  if (!serial)
24259
24947
  return;
24260
- const booted = captureCommand3([
24948
+ const booted = captureCommand4([
24261
24949
  adb2,
24262
24950
  "-s",
24263
24951
  serial,
@@ -24279,8 +24967,8 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
24279
24967
  return waitForBoot();
24280
24968
  };
24281
24969
  var safeArtifactRoot = (projectRoot, value) => {
24282
- const root = resolve13(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
24283
- if (root !== projectRoot && !root.startsWith(`${resolve13(projectRoot)}/`)) {
24970
+ const root = resolve14(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
24971
+ if (root !== projectRoot && !root.startsWith(`${resolve14(projectRoot)}/`)) {
24284
24972
  throw new TypeError("mobile test --artifacts must remain inside the project.");
24285
24973
  }
24286
24974
  return root;
@@ -24308,12 +24996,12 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
24308
24996
  });
24309
24997
  };
24310
24998
  var writeAndroidFailureArtifacts = async (options) => {
24311
- await mkdir13(options.artifactRoot, { recursive: true });
24312
- const screenshot = options.session ? await options.session.screenshot(join14(options.artifactRoot, "android-failure.png")).catch(() => {
24999
+ await mkdir15(options.artifactRoot, { recursive: true });
25000
+ const screenshot = options.session ? await options.session.screenshot(join16(options.artifactRoot, "android-failure.png")).catch(() => {
24313
25001
  return;
24314
25002
  }) : undefined;
24315
- const diagnosticPath = join14(options.artifactRoot, "android-failure.json");
24316
- await writeFile15(diagnosticPath, `${JSON.stringify({
25003
+ const diagnosticPath = join16(options.artifactRoot, "android-failure.json");
25004
+ await writeFile16(diagnosticPath, `${JSON.stringify({
24317
25005
  diagnostics: options.session?.diagnostics ?? [],
24318
25006
  error: options.error instanceof Error ? options.error.message : String(options.error),
24319
25007
  platform: "android",
@@ -24334,7 +25022,7 @@ var captureAndroidReleaseScreenshot = async (adb2, serial, destination) => {
24334
25022
  ]);
24335
25023
  if (exitCode !== 0)
24336
25024
  throw new Error(`Android release screenshot failed: ${stderr.trim() || `status ${exitCode}`}`);
24337
- await writeFile15(destination, bytes);
25025
+ await writeFile16(destination, bytes);
24338
25026
  return destination;
24339
25027
  };
24340
25028
  var printAndroidReleaseAcceptance = (result, json) => {
@@ -24351,8 +25039,6 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
24351
25039
  throw new TypeError("mobile test android --release requires a release directory or release.json path.");
24352
25040
  if (!mobile.platforms.includes("android"))
24353
25041
  throw new TypeError("mobile test android requires android in mobile.platforms.");
24354
- if (mobile.engine !== "expo")
24355
- throw new TypeError("mobile test android --release currently requires mobile.engine: expo; use the ordinary test command for Capacitor.");
24356
25042
  const release = await readAbsoluteAndroidRelease(projectRoot, requested);
24357
25043
  if (release.metadata.appId !== mobile.appId)
24358
25044
  throw new TypeError(`Android release app ID ${release.metadata.appId} does not match mobile.appId ${mobile.appId}.`);
@@ -24390,7 +25076,7 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
24390
25076
  release,
24391
25077
  serial
24392
25078
  });
24393
- const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join14(reportRoot, "android-release.png")) : undefined;
25079
+ const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join16(reportRoot, "android-release.png")) : undefined;
24394
25080
  sendTelemetryEvent("mobile:android-release-conformance", {
24395
25081
  durationMs: result.durationMs,
24396
25082
  engine: result.engine,
@@ -24437,9 +25123,9 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
24437
25123
  platform: "android",
24438
25124
  success: false
24439
25125
  });
24440
- await mkdir13(artifactRoot, { recursive: true });
24441
- const diagnosticPath = join14(artifactRoot, "android-release-failure.json");
24442
- await writeFile15(diagnosticPath, `${JSON.stringify({
25126
+ await mkdir15(artifactRoot, { recursive: true });
25127
+ const diagnosticPath = join16(artifactRoot, "android-release-failure.json");
25128
+ await writeFile16(diagnosticPath, `${JSON.stringify({
24443
25129
  error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
24444
25130
  platform: "android",
24445
25131
  provider: mobile.engine,
@@ -24533,7 +25219,7 @@ var testAndroid = async (args) => {
24533
25219
  console.log(JSON.stringify(report, null, 2));
24534
25220
  else
24535
25221
  printAndroidTestReport(report);
24536
- const screenshot = reportRoot ? await session.screenshot(join14(artifactRoot, "android-emulator.png")) : undefined;
25222
+ const screenshot = reportRoot ? await session.screenshot(join16(artifactRoot, "android-emulator.png")) : undefined;
24537
25223
  await writeRequestedAndroidReport({
24538
25224
  adb: adb2,
24539
25225
  args,
@@ -24602,14 +25288,14 @@ var requireIosTestContext = (args, projectRoot) => {
24602
25288
  const port = Number(explicit);
24603
25289
  if (!Number.isInteger(port) || port < 1 || port > 65535)
24604
25290
  throw new TypeError("mobile test --port must be a valid TCP port.");
24605
- const instance2 = listLiveInstances().find((candidate) => resolve13(candidate.cwd) === resolve13(projectRoot) && candidate.source === "dev" && candidate.port === port);
25291
+ const instance2 = listLiveInstances().find((candidate) => resolve14(candidate.cwd) === resolve14(projectRoot) && candidate.source === "dev" && candidate.port === port);
24606
25292
  return {
24607
25293
  https: instance2?.https ?? args.includes("--https"),
24608
25294
  instance: instance2,
24609
25295
  port
24610
25296
  };
24611
25297
  }
24612
- const instances = listLiveInstances().filter((instance2) => resolve13(instance2.cwd) === resolve13(projectRoot) && instance2.source === "dev" && instance2.port !== null);
25298
+ const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24613
25299
  if (instances.length !== 1)
24614
25300
  throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for iOS to report ready, then run `absolute mobile test ios`." : "Multiple dev servers are running for this project. Select one with mobile test ios --port <port>.");
24615
25301
  const [instance] = instances;
@@ -24699,11 +25385,11 @@ var physicalIosCapture = async (options) => {
24699
25385
  remote: false,
24700
25386
  xcodeVersion: requireCapturedCommand(["/usr/bin/xcodebuild", "-version"], "Xcode version inspection").stdout.trim(),
24701
25387
  xcrun: "/usr/bin/xcrun",
24702
- capture: async (command) => captureCommand3(command)
25388
+ capture: async (command) => captureCommand4(command)
24703
25389
  };
24704
25390
  };
24705
25391
  var selectIosSimulator = (xcrun, explicitUdid) => {
24706
- const result = captureCommand3([
25392
+ const result = captureCommand4([
24707
25393
  xcrun,
24708
25394
  "simctl",
24709
25395
  "list",
@@ -24725,16 +25411,37 @@ var selectIosSimulator = (xcrun, explicitUdid) => {
24725
25411
  throw new TypeError("No ready AbsoluteJS iOS simulator was found. Start `bun dev` and wait for the iOS target to become ready.");
24726
25412
  return selected;
24727
25413
  };
25414
+ var selectOrStartIosReleaseSimulator = async (xcrun, explicitUdid) => {
25415
+ const result = captureCommand4([
25416
+ xcrun,
25417
+ "simctl",
25418
+ "list",
25419
+ "devices",
25420
+ "available",
25421
+ "-j"
25422
+ ]);
25423
+ if (result.exitCode !== 0)
25424
+ throw new Error(`Could not list iOS simulators: ${result.stderr.trim() || result.stdout.trim()}`);
25425
+ const devices = parseIosSimulators(result.stdout).filter((device) => device.isAvailable);
25426
+ const selected = explicitUdid ? devices.find((device) => device.udid === explicitUdid) : devices.find((device) => device.name === ABSOLUTE_IOS_SIMULATOR_NAME);
25427
+ if (!selected)
25428
+ throw new TypeError(explicitUdid ? `iOS simulator ${explicitUdid} is unavailable.` : "No managed AbsoluteJS iOS simulator was found. Run `absolute mobile doctor ios --fix`.");
25429
+ if (selected.state !== "Booted") {
25430
+ requireCapturedCommand([xcrun, "simctl", "boot", selected.udid], "iOS Simulator boot");
25431
+ requireCapturedCommand([xcrun, "simctl", "bootstatus", selected.udid, "-b"], "iOS Simulator readiness");
25432
+ }
25433
+ return { ...selected, state: "Booted" };
25434
+ };
24728
25435
  var requireCapturedCommand = (command, label) => {
24729
- const result = captureCommand3(command);
25436
+ const result = captureCommand4(command);
24730
25437
  if (result.exitCode !== 0)
24731
25438
  throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
24732
25439
  return result;
24733
25440
  };
24734
25441
  var writeIosFailureArtifacts = async (options) => {
24735
- await mkdir13(options.artifactRoot, { recursive: true });
24736
- const screenshot = join14(options.artifactRoot, "ios-failure.png");
24737
- const screenshotResult = captureCommand3([
25442
+ await mkdir15(options.artifactRoot, { recursive: true });
25443
+ const screenshot = join16(options.artifactRoot, "ios-failure.png");
25444
+ const screenshotResult = captureCommand4([
24738
25445
  options.xcrun,
24739
25446
  "simctl",
24740
25447
  "io",
@@ -24742,8 +25449,8 @@ var writeIosFailureArtifacts = async (options) => {
24742
25449
  "screenshot",
24743
25450
  screenshot
24744
25451
  ]);
24745
- const diagnosticPath = join14(options.artifactRoot, "ios-failure.json");
24746
- await writeFile15(diagnosticPath, `${JSON.stringify({
25452
+ const diagnosticPath = join16(options.artifactRoot, "ios-failure.json");
25453
+ await writeFile16(diagnosticPath, `${JSON.stringify({
24747
25454
  appId: options.appId,
24748
25455
  error: options.error instanceof Error ? options.error.message : String(options.error),
24749
25456
  platform: "ios",
@@ -24770,8 +25477,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
24770
25477
  var absolutejsVersionForReport = async () => {
24771
25478
  let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
24772
25479
  const versions = await Promise.all([
24773
- resolve13(import.meta.dir, "..", "..", "package.json"),
24774
- resolve13(import.meta.dir, "..", "..", "..", "package.json")
25480
+ resolve14(import.meta.dir, "..", "..", "package.json"),
25481
+ resolve14(import.meta.dir, "..", "..", "..", "package.json")
24775
25482
  ].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
24776
25483
  for (const version of versions) {
24777
25484
  if (version === "unknown")
@@ -24818,7 +25525,11 @@ var writeRequestedIosReport = async (options) => {
24818
25525
  const metadata = options.metadata ?? (options.xcrun ? await iosReportMetadata(options.xcrun) : undefined);
24819
25526
  if (!metadata)
24820
25527
  throw new Error("iOS report metadata could not be inspected.");
24821
- const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({ ...metadata, run: options.run }));
25528
+ const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({
25529
+ ...metadata,
25530
+ ...options.provider ? { provider: options.provider } : {},
25531
+ run: options.run
25532
+ }));
24822
25533
  const print = options.args.includes("--json") ? console.error : console.log;
24823
25534
  print(`iOS partner report: ${paths.markdownPath}`);
24824
25535
  print(`Return this report directory: ${paths.directory}`);
@@ -24935,8 +25646,167 @@ var testPhysicalIos = async (options) => {
24935
25646
  throw error;
24936
25647
  }
24937
25648
  };
25649
+ var printIosReleaseAcceptance = (result, json) => {
25650
+ if (json) {
25651
+ console.log(JSON.stringify(result, null, 2));
25652
+ return;
25653
+ }
25654
+ console.log(`\u2713 ${result.artifactExactness} ${result.engine} iOS release evidence passed via ${result.distribution}.`);
25655
+ console.log(`\u2713 Embedded local content launched in ${getDurationString(result.launchMs)} and relaunched in ${getDurationString(result.relaunchMs)}.`);
25656
+ console.log(result.networkUnavailable === "user-confirmed" ? "\u2713 Physical-device network-unavailable state was explicitly confirmed." : "\u2139 Simulator evidence does not claim physical-device offline behavior.");
25657
+ };
25658
+ var iosReleaseRun = (result, appId, targetId) => ({
25659
+ appId,
25660
+ durationMs: result.durationMs,
25661
+ hmrConnected: false,
25662
+ iosRelease: {
25663
+ artifactBytes: result.artifactBytes,
25664
+ artifactExactness: result.artifactExactness,
25665
+ artifactSha256: result.artifactSha256,
25666
+ distribution: result.distribution,
25667
+ embeddedLocal: result.embeddedLocal,
25668
+ engine: result.engine,
25669
+ installMs: result.installMs,
25670
+ launchMs: result.launchMs,
25671
+ networkUnavailable: result.networkUnavailable,
25672
+ relaunchMs: result.relaunchMs,
25673
+ releaseId: result.releaseId,
25674
+ signed: result.signed
25675
+ },
25676
+ status: "pass",
25677
+ targetId,
25678
+ targetKind: result.target
25679
+ });
25680
+ var runIosPhysicalReleaseTarget = async (options) => {
25681
+ const confirmed = options.args.includes("--yes") || await confirmInstall("On the selected iPhone, enable Airplane Mode and then disable Wi-Fi in Settings. Confirm both are still disabled and continue with two release launches?");
25682
+ if (!confirmed)
25683
+ throw new TypeError("Physical iOS offline acceptance was cancelled because network-unavailable state was not confirmed.");
25684
+ const targetId = normalizeAbsoluteIosDeviceIdentifier(options.device);
25685
+ const result = await runAbsoluteIosDeviceReleaseAcceptance({
25686
+ device: targetId,
25687
+ distribution: options.args.includes("--testflight") ? "testflight" : "registered-device",
25688
+ networkUnavailableConfirmed: true,
25689
+ release: options.release,
25690
+ xcrun: options.xcrun
25691
+ });
25692
+ return { result, targetId };
25693
+ };
25694
+ var runIosSimulatorReleaseTarget = async (options) => {
25695
+ const simulator = await selectOrStartIosReleaseSimulator(options.xcrun, valueAfter(options.args, "--udid") ?? valueAfter(options.args, "--serial"));
25696
+ const xcodebuild = requireCapturedCommand([options.xcrun, "--find", "xcodebuild"], "Xcode build-tool inspection").stdout.trim();
25697
+ const result = await runAbsoluteIosSimulatorReleaseAcceptance({
25698
+ artifactDirectory: options.artifactRoot,
25699
+ config: options.mobile,
25700
+ release: options.release,
25701
+ udid: simulator.udid,
25702
+ xcodebuild,
25703
+ xcrun: options.xcrun
25704
+ });
25705
+ return { result, targetId: simulator.udid };
25706
+ };
25707
+ var testIosRelease = async (args, mobile, projectRoot) => {
25708
+ const requested = valueAfter(args, "--release");
25709
+ if (!requested || requested.startsWith("--"))
25710
+ throw new TypeError("mobile test ios --release requires a release directory or release.json path.");
25711
+ if (!mobile.platforms.includes("ios"))
25712
+ throw new TypeError("mobile test ios requires ios in mobile.platforms.");
25713
+ if (args.includes("--remote"))
25714
+ throw new TypeError("Installed iOS release acceptance currently runs on a local Mac. Remote Mac acceptance will be added to the paired-host protocol.");
25715
+ const release = await readAbsoluteIosRelease(projectRoot, requested);
25716
+ if (release.metadata.appId !== mobile.appId)
25717
+ throw new TypeError(`iOS release app ID ${release.metadata.appId} does not match mobile.appId ${mobile.appId}.`);
25718
+ if (release.metadata.engine !== mobile.engine)
25719
+ throw new TypeError(`iOS release engine ${release.metadata.engine} does not match configured engine ${mobile.engine}.`);
25720
+ if (mobile.engine === "expo" && mobile.expoNativeRoutes[mobile.entry])
25721
+ throw new TypeError("Embedded-local Expo release acceptance requires mobile.entry to be a web route; native Expo routes may depend on trusted-server page data.");
25722
+ const requestedDevice = valueAfter(args, "--device");
25723
+ if (args.includes("--device") && !requestedDevice)
25724
+ throw new TypeError("mobile test ios --release --device requires a device identifier or name.");
25725
+ if (args.includes("--testflight") && !requestedDevice)
25726
+ throw new TypeError("mobile test ios --release --testflight requires --device.");
25727
+ if (requestedDevice && (valueAfter(args, "--udid") || valueAfter(args, "--serial")))
25728
+ throw new TypeError("mobile test ios --release --device cannot be combined with a simulator selector.");
25729
+ const xcrun = await requireIosXcrun();
25730
+ const reportRoot = nativeReportRoot(args, projectRoot, "ios");
25731
+ const artifactRoot = reportRoot ?? safeArtifactRoot(projectRoot, valueAfter(args, "--artifacts") ?? `.absolutejs/mobile/test-artifacts/${release.metadata.releaseId}`);
25732
+ const startedAt = performance.now();
25733
+ let targetId = requestedDevice ?? "ios-simulator";
25734
+ try {
25735
+ const acceptance = requestedDevice ? await runIosPhysicalReleaseTarget({
25736
+ args,
25737
+ device: requestedDevice,
25738
+ release,
25739
+ xcrun
25740
+ }) : await runIosSimulatorReleaseTarget({
25741
+ args,
25742
+ artifactRoot,
25743
+ mobile,
25744
+ release,
25745
+ xcrun
25746
+ });
25747
+ const { result, targetId: acceptedTargetId } = acceptance;
25748
+ targetId = acceptedTargetId;
25749
+ sendTelemetryEvent("mobile:ios-release-conformance", {
25750
+ artifactExactness: result.artifactExactness,
25751
+ distribution: result.distribution,
25752
+ durationMs: result.durationMs,
25753
+ engine: result.engine,
25754
+ installMs: result.installMs,
25755
+ launchMs: result.launchMs,
25756
+ platform: "ios",
25757
+ relaunchMs: result.relaunchMs,
25758
+ success: true
25759
+ });
25760
+ await writeRequestedIosReport({
25761
+ args,
25762
+ projectRoot,
25763
+ provider: mobile.engine,
25764
+ run: iosReleaseRun(result, mobile.appId, targetId),
25765
+ xcrun
25766
+ });
25767
+ printIosReleaseAcceptance(result, args.includes("--json"));
25768
+ return result;
25769
+ } catch (error) {
25770
+ const durationMs = Math.round(performance.now() - startedAt);
25771
+ sendTelemetryEvent("mobile:ios-release-conformance", {
25772
+ durationMs,
25773
+ engine: mobile.engine,
25774
+ platform: "ios",
25775
+ success: false
25776
+ });
25777
+ await mkdir15(artifactRoot, { recursive: true });
25778
+ const diagnosticPath = join16(artifactRoot, "ios-release-failure.json");
25779
+ await writeFile16(diagnosticPath, `${JSON.stringify({
25780
+ error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
25781
+ platform: "ios",
25782
+ provider: mobile.engine,
25783
+ releaseId: release.metadata.releaseId,
25784
+ status: "fail"
25785
+ }, null, 2)}
25786
+ `);
25787
+ if (reportRoot)
25788
+ await writeRequestedIosReport({
25789
+ args,
25790
+ projectRoot,
25791
+ provider: mobile.engine,
25792
+ run: {
25793
+ appId: mobile.appId,
25794
+ durationMs,
25795
+ error: "Installed iOS release acceptance failed; inspect the local diagnostic artifact before sharing evidence.",
25796
+ hmrConnected: false,
25797
+ status: "fail",
25798
+ targetId,
25799
+ targetKind: requestedDevice ? "device" : "simulator"
25800
+ },
25801
+ xcrun
25802
+ });
25803
+ throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}`, { cause: error });
25804
+ }
25805
+ };
24938
25806
  var testIos = async (args) => {
24939
25807
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
25808
+ if (args.includes("--release"))
25809
+ return testIosRelease(args, mobile, projectRoot);
24940
25810
  requireCapacitorEngine(mobile, "mobile test ios");
24941
25811
  const { https, instance, port } = requireIosTestContext(args, projectRoot);
24942
25812
  if (!mobile.platforms.includes("ios"))
@@ -24996,8 +25866,8 @@ var testIos = async (args) => {
24996
25866
  mobile.appId
24997
25867
  ], "iOS app launch");
24998
25868
  await waitForIosHmrClient({ https, port, timeoutMs });
24999
- await mkdir13(artifactRoot, { recursive: true });
25000
- const screenshot = join14(artifactRoot, "ios-simulator.png");
25869
+ await mkdir15(artifactRoot, { recursive: true });
25870
+ const screenshot = join16(artifactRoot, "ios-simulator.png");
25001
25871
  requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
25002
25872
  const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
25003
25873
  const report = {
@@ -25183,7 +26053,7 @@ var runMobile = async (args) => {
25183
26053
  await publishIos(args.slice(2));
25184
26054
  return;
25185
26055
  }
25186
- throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path]");
26056
+ throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--registered-device-artifact] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path] | test ios [--release release-dir [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
25187
26057
  };
25188
26058
  export {
25189
26059
  runMobile