@absolutejs/absolute 0.20.0-beta.104 → 0.20.0-beta.106

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 +45 -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-g8vmvr0w.js → compile-hsbs1xf6.js} +2 -2
  6. package/dist/cli/{dev-m4r4zs2k.js → dev-f6b6hkke.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-67kedtn7.js → index-hrzd7e5x.js} +1 -1
  11. package/dist/cli/{index-3sdvzrv2.js → index-jf1f6npt.js} +233 -4
  12. package/dist/cli/{index-27hhdgtg.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-njmnd7vc.js → mobile-w4my2dc7.js} +1008 -173
  18. package/dist/cli/{remoteMacProtocol-kqvkp0qd.js → remoteMacProtocol-dybpgazw.js} +6 -2
  19. package/dist/cli/{start-39aqmr5n.js → start-5grq7nfr.js} +3 -3
  20. package/dist/mobile/index.js +1445 -575
  21. package/dist/mobile/index.js.map +10 -9
  22. package/dist/mobile/remoteMacAgentEntry.js +341 -294
  23. package/dist/src/mobile/index.d.ts +1 -0
  24. package/dist/src/mobile/iosRelease.d.ts +31 -7
  25. package/dist/src/mobile/iosReleaseAcceptance.d.ts +66 -0
  26. package/dist/src/mobile/iosTestReport.d.ts +1 -0
  27. package/dist/src/mobile/nativeReleaseReadiness.d.ts +1 -1
  28. package/dist/src/mobile/nativeTestReport.d.ts +16 -0
  29. package/dist/src/mobile/remoteMacProtocol.d.ts +51 -4
  30. package/dist/src/mobile/remoteMacWire.d.ts +1 -1
  31. package/dist/src/mobile/shellReleaseReadiness.d.ts +1 -1
  32. package/package.json +1 -1
@@ -7,12 +7,12 @@ import {
7
7
  } from "./index-zh6hhrwy.js";
8
8
  import {
9
9
  start
10
- } from "./index-27hhdgtg.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-67kedtn7.js";
15
+ } from "./index-hrzd7e5x.js";
16
16
  import"./index-bzs19w3r.js";
17
17
  import"./index-s2tazax7.js";
18
18
  import {
@@ -29,16 +29,19 @@ import {
29
29
  inspectAbsoluteRemoteMacWorkspace,
30
30
  listAbsoluteRemoteMacProfiles,
31
31
  pairAbsoluteRemoteMac,
32
- removeAbsoluteRemoteMacProfile
33
- } from "./index-3sdvzrv2.js";
32
+ removeAbsoluteRemoteMacProfile,
33
+ runAbsoluteRemoteIosReleaseAcceptance
34
+ } from "./index-jf1f6npt.js";
34
35
  import {
35
36
  ABSOLUTE_IOS_SIMULATOR_NAME,
36
37
  parseIosSimulators,
37
38
  repairAbsoluteIosDevSession
38
- } from "./index-wpfvpsk7.js";
39
+ } from "./index-5skbydh0.js";
39
40
  import {
40
- buildAbsoluteIosRelease
41
- } from "./index-qxnptvwn.js";
41
+ buildAbsoluteIosRelease,
42
+ requireAbsoluteIosReleaseMetadata,
43
+ resolveAbsoluteIosXcodeProject
44
+ } from "./index-q1d4ns4n.js";
42
45
  import"./index-fc9vxw56.js";
43
46
  import {
44
47
  listLiveInstances
@@ -67,7 +70,7 @@ import {
67
70
  syncAbsoluteExpoWebAssets,
68
71
  unsignedAbsoluteMobileUpdate,
69
72
  writeAbsoluteExpoProject
70
- } from "./index-d5fr1xp3.js";
73
+ } from "./index-0zcebhm4.js";
71
74
  import {
72
75
  buildAbsoluteExpoAndroidWslBundle
73
76
  } from "./index-28nxv57v.js";
@@ -4412,9 +4415,9 @@ var require_sha256 = __commonJS(function(exports, module) {
4412
4415
  var forge = require_forge();
4413
4416
  require_md();
4414
4417
  require_util();
4415
- var sha2563 = module.exports = forge.sha256 = forge.sha256 || {};
4416
- forge.md.sha256 = forge.md.algorithms.sha256 = sha2563;
4417
- sha2563.create = function() {
4418
+ var sha2564 = module.exports = forge.sha256 = forge.sha256 || {};
4419
+ forge.md.sha256 = forge.md.algorithms.sha256 = sha2564;
4420
+ sha2564.create = function() {
4418
4421
  if (!_initialized) {
4419
4422
  _init();
4420
4423
  }
@@ -16776,15 +16779,15 @@ var require_main = __commonJS(function(exports) {
16776
16779
 
16777
16780
  // src/cli/scripts/mobile.ts
16778
16781
  import {
16779
- access as access13,
16780
- mkdir as mkdir14,
16781
- mkdtemp as mkdtemp4,
16782
- readFile as readFile18,
16783
- rm as rm7,
16782
+ access as access14,
16783
+ mkdir as mkdir15,
16784
+ mkdtemp as mkdtemp5,
16785
+ readFile as readFile19,
16786
+ rm as rm8,
16784
16787
  writeFile as writeFile16
16785
16788
  } from "fs/promises";
16786
16789
  import { createPublicKey as createPublicKey2 } from "crypto";
16787
- import { join as join15, relative as relative12, resolve as resolve13 } from "path";
16790
+ import { join as join16, relative as relative13, resolve as resolve14 } from "path";
16788
16791
  import { createInterface } from "readline/promises";
16789
16792
 
16790
16793
  // src/mobile/nativeDeepLinks.ts
@@ -18459,6 +18462,8 @@ import { mkdir as mkdir3, readFile as readFile6, readdir as readdir3, rename as
18459
18462
  import { dirname as dirname3, join as join6 } from "path";
18460
18463
  var START3 = "// absolutejs:release-readiness:start";
18461
18464
  var END3 = "// absolutejs:release-readiness:end";
18465
+ var IOS_PLUGIN_START = "// absolutejs:release-readiness-plugin:start";
18466
+ var IOS_PLUGIN_END = "// absolutejs:release-readiness-plugin:end";
18462
18467
  var ANDROID_PLUGIN3 = "AbsoluteReleaseReadinessPlugin.java";
18463
18468
  var optionalSource4 = async (path) => {
18464
18469
  try {
@@ -18580,9 +18585,70 @@ public final class AbsoluteReleaseReadinessPlugin extends Plugin {
18580
18585
  }
18581
18586
  }
18582
18587
  `;
18583
- var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.platforms) => {
18584
- if (config.engine !== "capacitor" || !platforms.includes("android"))
18585
- return { changed: false };
18588
+ var iosPluginRegion3 = `${IOS_PLUGIN_START}
18589
+ @objc(AbsoluteReleaseReadinessPlugin)
18590
+ public final class AbsoluteReleaseReadinessPlugin: CAPPlugin, CAPBridgedPlugin {
18591
+ public let identifier = "AbsoluteReleaseReadinessPlugin"
18592
+ public let jsName = "AbsoluteReleaseReadiness"
18593
+ public let pluginMethods: [CAPPluginMethod] = [
18594
+ CAPPluginMethod(name: "ready", returnType: CAPPluginReturnPromise)
18595
+ ]
18596
+
18597
+ @objc public func ready(_ call: CAPPluginCall) {
18598
+ let info = Bundle.main.infoDictionary ?? [:]
18599
+ let version = info["CFBundleShortVersionString"] as? String ?? "unknown"
18600
+ let build = info["CFBundleVersion"] as? String ?? "unknown"
18601
+ NSLog("AbsoluteJS: Capacitor embedded web content ready; version=%@; build=%@", version, build)
18602
+ call.resolve(["ready": true])
18603
+ }
18604
+ }
18605
+ ${IOS_PLUGIN_END}
18606
+ `;
18607
+ var replaceIosPluginRegion = (appDelegate, existingStart, existingEnd) => {
18608
+ const from = appDelegate.lastIndexOf(`
18609
+ `, existingStart) + 1;
18610
+ const newline = appDelegate.indexOf(`
18611
+ `, existingEnd + IOS_PLUGIN_END.length);
18612
+ const through = newline < 0 ? appDelegate.length : newline + 1;
18613
+ return `${appDelegate.slice(0, from)}${iosPluginRegion3}${appDelegate.slice(through)}`;
18614
+ };
18615
+ var configureIos5 = async (config) => {
18616
+ const appDelegatePath = join6(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
18617
+ const capacitorConfigPath = join6(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
18618
+ const appDelegate = await readFile6(appDelegatePath, "utf8");
18619
+ const existingStart = appDelegate.indexOf(IOS_PLUGIN_START);
18620
+ const existingEnd = appDelegate.indexOf(IOS_PLUGIN_END);
18621
+ if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
18622
+ throw new TypeError("AbsoluteJS iOS release-readiness markers are malformed.");
18623
+ const source = existingStart < 0 ? `${appDelegate.trimEnd()}
18624
+
18625
+ ${iosPluginRegion3}` : replaceIosPluginRegion(appDelegate, existingStart, existingEnd);
18626
+ let capacitorConfig;
18627
+ try {
18628
+ capacitorConfig = JSON.parse(await readFile6(capacitorConfigPath, "utf8"));
18629
+ } catch {
18630
+ throw new TypeError("iOS capacitor.config.json is invalid; run Capacitor sync before projecting release readiness.");
18631
+ }
18632
+ if (typeof capacitorConfig !== "object" || capacitorConfig === null)
18633
+ throw new TypeError("iOS capacitor.config.json must contain an object.");
18634
+ const packageClassList = Reflect.get(capacitorConfig, "packageClassList");
18635
+ if (!Array.isArray(packageClassList))
18636
+ throw new TypeError("iOS capacitor.config.json has no packageClassList; run Capacitor sync before projecting release readiness.");
18637
+ const classes = packageClassList.filter((value) => typeof value === "string");
18638
+ const nextConfig = `${JSON.stringify({
18639
+ ...capacitorConfig,
18640
+ packageClassList: [
18641
+ ...new Set([...classes, "AbsoluteReleaseReadinessPlugin"])
18642
+ ]
18643
+ }, null, "\t")}
18644
+ `;
18645
+ const changed = await Promise.all([
18646
+ writeChanged4(appDelegatePath, source),
18647
+ writeChanged4(capacitorConfigPath, nextConfig)
18648
+ ]);
18649
+ return changed.some(Boolean);
18650
+ };
18651
+ var configureAndroid5 = async (config) => {
18586
18652
  const mainActivityPath = await activityPath(config);
18587
18653
  const activity = await readFile6(mainActivityPath, "utf8");
18588
18654
  const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
@@ -18593,6 +18659,15 @@ var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.plat
18593
18659
  writeChanged4(mainActivityPath, injectRegistration(activity)),
18594
18660
  writeChanged4(pluginPath, pluginSource(packageName))
18595
18661
  ]);
18662
+ return changed.some(Boolean);
18663
+ };
18664
+ var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.platforms) => {
18665
+ if (config.engine !== "capacitor")
18666
+ return { changed: false };
18667
+ const changed = await Promise.all([
18668
+ platforms.includes("android") ? configureAndroid5(config) : false,
18669
+ platforms.includes("ios") ? configureIos5(config) : false
18670
+ ]);
18596
18671
  return { changed: changed.some(Boolean) };
18597
18672
  };
18598
18673
 
@@ -21083,6 +21158,473 @@ var runAbsoluteAndroidReleaseAcceptance = async (options) => {
21083
21158
  };
21084
21159
  };
21085
21160
 
21161
+ // src/mobile/iosReleaseAcceptance.ts
21162
+ import { createHash as createHash4 } from "crypto";
21163
+ import {
21164
+ access as access7,
21165
+ mkdir as mkdir9,
21166
+ mkdtemp as mkdtemp3,
21167
+ readFile as readFile12,
21168
+ readdir as readdir5,
21169
+ rm as rm6,
21170
+ stat as stat4
21171
+ } from "fs/promises";
21172
+ import {
21173
+ basename as basename2,
21174
+ dirname as dirname9,
21175
+ isAbsolute as isAbsolute4,
21176
+ join as join11,
21177
+ relative as relative6,
21178
+ resolve as resolve7,
21179
+ sep as sep4
21180
+ } from "path";
21181
+
21182
+ // src/mobile/iosUpgradeConformance.ts
21183
+ var captureCommand3 = async (command) => {
21184
+ const process2 = Bun.spawn(command, { stderr: "pipe", stdout: "pipe" });
21185
+ const [exitCode, stderr, stdout] = await Promise.all([
21186
+ process2.exited,
21187
+ new Response(process2.stderr).text(),
21188
+ new Response(process2.stdout).text()
21189
+ ]);
21190
+ return { exitCode, stderr, stdout };
21191
+ };
21192
+ var requiredOutput = (result, message) => {
21193
+ const value = result.stdout.trim();
21194
+ if (result.exitCode !== 0 || !value)
21195
+ throw new Error(`${message}: ${result.stderr.trim() || value || "no output"}`);
21196
+ return value;
21197
+ };
21198
+ var inspectAbsoluteIosInstalledApp = async (xcrun, udid, appId, run = captureCommand3, plutil = "/usr/bin/plutil") => {
21199
+ const dataContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "data"]), `Could not inspect the installed iOS data container for ${appId}`);
21200
+ const appContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "app"]), `Could not inspect the installed iOS application container for ${appId}`);
21201
+ const info = requiredOutput(await run([
21202
+ plutil,
21203
+ "-convert",
21204
+ "json",
21205
+ "-o",
21206
+ "-",
21207
+ `${appContainer}/Info.plist`
21208
+ ]), `Could not inspect the installed iOS Info.plist for ${appId}`);
21209
+ return parseAbsoluteIosInstalledAppInfo(appId, appContainer, dataContainer, info);
21210
+ };
21211
+ var parseAbsoluteIosInstalledAppInfo = (appId, appContainer, dataContainer, output) => {
21212
+ let value;
21213
+ try {
21214
+ value = JSON.parse(output);
21215
+ } catch {
21216
+ throw new TypeError("Invalid installed iOS application Info.plist JSON.");
21217
+ }
21218
+ if (!value || typeof value !== "object")
21219
+ throw new TypeError("Invalid installed iOS application Info.plist JSON.");
21220
+ const bundleId = Reflect.get(value, "CFBundleIdentifier");
21221
+ if (bundleId !== appId)
21222
+ throw new Error(`Installed iOS application identifier mismatch (${String(bundleId)} != ${appId}).`);
21223
+ const buildNumber = Reflect.get(value, "CFBundleVersion");
21224
+ const version = Reflect.get(value, "CFBundleShortVersionString");
21225
+ return {
21226
+ appContainer,
21227
+ appId,
21228
+ ...typeof buildNumber === "string" || typeof buildNumber === "number" ? { buildNumber: String(buildNumber) } : {},
21229
+ dataContainer,
21230
+ ...typeof version === "string" ? { version } : {}
21231
+ };
21232
+ };
21233
+
21234
+ // src/mobile/iosReleaseAcceptance.ts
21235
+ var READY_MARKERS = {
21236
+ capacitor: "Capacitor embedded web content ready",
21237
+ expo: "Expo embedded web content ready"
21238
+ };
21239
+ var READY_TIMEOUT_MS = 30000;
21240
+ var READY_POLL_MS = 250;
21241
+ var defaultRun2 = async (command, options = {}) => {
21242
+ const process2 = Bun.spawn(command, {
21243
+ cwd: options.cwd,
21244
+ env: options.env,
21245
+ signal: options.signal,
21246
+ stderr: "pipe",
21247
+ stdin: "ignore",
21248
+ stdout: "pipe"
21249
+ });
21250
+ const [exitCode, stderr, stdout] = await Promise.all([
21251
+ process2.exited,
21252
+ new Response(process2.stderr).text(),
21253
+ new Response(process2.stdout).text()
21254
+ ]);
21255
+ return { exitCode, stderr, stdout };
21256
+ };
21257
+ var exists3 = async (path) => {
21258
+ try {
21259
+ await access7(path);
21260
+ return true;
21261
+ } catch {
21262
+ return false;
21263
+ }
21264
+ };
21265
+ var sha2562 = async (path) => createHash4("sha256").update(await readFile12(path)).digest("hex");
21266
+ var inside2 = (root, candidate) => {
21267
+ const path = relative6(resolve7(root), resolve7(candidate));
21268
+ return path === "" || !isAbsolute4(path) && path !== ".." && !path.startsWith(`..${sep4}`);
21269
+ };
21270
+ var requireSuccess2 = async (run, command, label, options) => {
21271
+ const result = await run(command, options);
21272
+ if (result.exitCode !== 0)
21273
+ throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
21274
+ return result;
21275
+ };
21276
+ var findByExtension = async (root, extension) => {
21277
+ if (!await exists3(root))
21278
+ return;
21279
+ const entries = await readdir5(root, { withFileTypes: true });
21280
+ const matches = await Promise.all(entries.map(async (entry) => {
21281
+ const path = join11(root, entry.name);
21282
+ if (entry.name.endsWith(extension))
21283
+ return path;
21284
+ return entry.isDirectory() ? findByExtension(path, extension) : undefined;
21285
+ }));
21286
+ return matches.find((match) => match !== undefined);
21287
+ };
21288
+ var readAbsoluteIosRelease = async (projectRoot, requested) => {
21289
+ const root = resolve7(projectRoot);
21290
+ const selected = resolve7(root, requested);
21291
+ if (!inside2(root, selected))
21292
+ throw new TypeError("mobile test ios --release must remain inside the project.");
21293
+ const metadataPath = basename2(selected) === "release.json" ? selected : join11(selected, "release.json");
21294
+ const releaseRoot = dirname9(metadataPath);
21295
+ const metadata = requireAbsoluteIosReleaseMetadata(JSON.parse(await readFile12(metadataPath, "utf8")));
21296
+ const artifactPath = resolve7(releaseRoot, metadata.artifact);
21297
+ if (!inside2(releaseRoot, artifactPath))
21298
+ throw new TypeError("iOS release artifact path is invalid.");
21299
+ const [artifactStat, artifactSha256] = await Promise.all([
21300
+ stat4(artifactPath),
21301
+ sha2562(artifactPath)
21302
+ ]);
21303
+ if (artifactStat.size !== metadata.bytes || artifactSha256 !== metadata.sha256)
21304
+ throw new TypeError(`Immutable iOS release ${metadata.releaseId} is missing or modified.`);
21305
+ const registered = metadata.registeredDevice;
21306
+ let registeredArtifactPath;
21307
+ if (registered) {
21308
+ registeredArtifactPath = resolve7(releaseRoot, registered.artifact);
21309
+ if (!inside2(releaseRoot, registeredArtifactPath))
21310
+ throw new TypeError("iOS registered-device artifact path is invalid.");
21311
+ const [registeredStat, registeredSha256] = await Promise.all([
21312
+ stat4(registeredArtifactPath),
21313
+ sha2562(registeredArtifactPath)
21314
+ ]);
21315
+ if (registeredStat.size !== registered.bytes || registeredSha256 !== registered.sha256)
21316
+ throw new TypeError(`Registered-device export for ${metadata.releaseId} is missing or modified.`);
21317
+ }
21318
+ return {
21319
+ artifactPath,
21320
+ metadata,
21321
+ metadataPath,
21322
+ registeredArtifactPath,
21323
+ releaseRoot
21324
+ };
21325
+ };
21326
+ var releaseMarker = (release) => {
21327
+ const marker = READY_MARKERS[release.metadata.engine];
21328
+ return release.metadata.buildNumber === undefined ? marker : `${marker}; version=${release.metadata.marketingVersion}; build=${release.metadata.buildNumber}`;
21329
+ };
21330
+ var waitForSimulatorMarker = async (options) => {
21331
+ const startedAt = performance.now();
21332
+ const poll = async () => {
21333
+ options.signal?.throwIfAborted();
21334
+ if (performance.now() - startedAt >= READY_TIMEOUT_MS)
21335
+ throw new Error("The installed iOS Release app launched but its embedded content did not become ready.");
21336
+ const logs = await options.run([
21337
+ options.xcrun,
21338
+ "simctl",
21339
+ "spawn",
21340
+ options.udid,
21341
+ "log",
21342
+ "show",
21343
+ "--style",
21344
+ "compact",
21345
+ "--start",
21346
+ options.startedAt.toISOString(),
21347
+ "--predicate",
21348
+ `eventMessage CONTAINS "${releaseMarker(options.release)}"`
21349
+ ]);
21350
+ if (logs.exitCode === 0 && logs.stdout.includes(releaseMarker(options.release)))
21351
+ return;
21352
+ await Bun.sleep(READY_POLL_MS);
21353
+ await poll();
21354
+ };
21355
+ await poll();
21356
+ };
21357
+ var launchSimulator = async (options) => {
21358
+ const startedAt = performance.now();
21359
+ const logStartedAt = new Date;
21360
+ await requireSuccess2(options.run, [
21361
+ options.xcrun,
21362
+ "simctl",
21363
+ "launch",
21364
+ "--terminate-running-process",
21365
+ options.udid,
21366
+ options.release.metadata.appId
21367
+ ], "iOS Simulator Release launch");
21368
+ await waitForSimulatorMarker({ ...options, startedAt: logStartedAt });
21369
+ return Math.round(performance.now() - startedAt);
21370
+ };
21371
+ var runAbsoluteIosSimulatorReleaseAcceptance = async (options) => {
21372
+ const baseRun = options.run ?? defaultRun2;
21373
+ const run = (command, commandOptions = {}) => baseRun(command, {
21374
+ ...commandOptions,
21375
+ ...options.signal ? { signal: options.signal } : {}
21376
+ });
21377
+ options.signal?.throwIfAborted();
21378
+ const xcodebuild = options.xcodebuild ?? "/usr/bin/xcodebuild";
21379
+ const xcrun = options.xcrun ?? "/usr/bin/xcrun";
21380
+ const startedAt = performance.now();
21381
+ const iosRoot = join11(options.config.nativeProjectDirectory, "ios");
21382
+ const xcode = await resolveAbsoluteIosXcodeProject(iosRoot);
21383
+ const derivedData = join11(options.artifactDirectory, "DerivedData");
21384
+ await mkdir9(options.artifactDirectory, { recursive: true });
21385
+ await requireSuccess2(run, [
21386
+ xcodebuild,
21387
+ "-workspace",
21388
+ xcode.workspacePath,
21389
+ "-scheme",
21390
+ xcode.scheme,
21391
+ "-configuration",
21392
+ "Release",
21393
+ "-destination",
21394
+ `platform=iOS Simulator,id=${options.udid}`,
21395
+ "-derivedDataPath",
21396
+ derivedData,
21397
+ `MARKETING_VERSION=${options.release.metadata.marketingVersion}`,
21398
+ ...options.release.metadata.buildNumber === undefined ? [] : [
21399
+ `CURRENT_PROJECT_VERSION=${options.release.metadata.buildNumber}`
21400
+ ],
21401
+ "build"
21402
+ ], "iOS Simulator Release build", { cwd: iosRoot });
21403
+ const app = await findByExtension(join11(derivedData, "Build", "Products"), ".app");
21404
+ if (!app)
21405
+ throw new Error("Xcode did not produce a Simulator Release app.");
21406
+ const installStartedAt = performance.now();
21407
+ await requireSuccess2(run, [xcrun, "simctl", "install", options.udid, app], "iOS Simulator Release installation");
21408
+ const installMs = Math.round(performance.now() - installStartedAt);
21409
+ const installed = await inspectAbsoluteIosInstalledApp(xcrun, options.udid, options.release.metadata.appId, (command) => run(command));
21410
+ if (installed.version !== options.release.metadata.marketingVersion)
21411
+ throw new Error(`Installed iOS version ${installed.version ?? "unknown"} does not match release ${options.release.metadata.marketingVersion}.`);
21412
+ if (options.release.metadata.buildNumber !== undefined && installed.buildNumber !== String(options.release.metadata.buildNumber))
21413
+ throw new Error(`Installed iOS build ${installed.buildNumber ?? "unknown"} does not match release ${options.release.metadata.buildNumber}.`);
21414
+ const launchMs = await launchSimulator({
21415
+ release: options.release,
21416
+ run,
21417
+ udid: options.udid,
21418
+ xcrun
21419
+ });
21420
+ await requireSuccess2(run, [
21421
+ xcrun,
21422
+ "simctl",
21423
+ "terminate",
21424
+ options.udid,
21425
+ options.release.metadata.appId
21426
+ ], "iOS Simulator Release termination");
21427
+ const relaunchMs = await launchSimulator({
21428
+ release: options.release,
21429
+ run,
21430
+ udid: options.udid,
21431
+ xcrun
21432
+ });
21433
+ return {
21434
+ artifactBytes: options.release.metadata.bytes,
21435
+ artifactExactness: "source-equivalent",
21436
+ artifactSha256: options.release.metadata.sha256,
21437
+ ...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
21438
+ distribution: "simulator-release",
21439
+ durationMs: Math.round(performance.now() - startedAt),
21440
+ embeddedLocal: true,
21441
+ engine: options.release.metadata.engine,
21442
+ installMs,
21443
+ launchMs,
21444
+ marketingVersion: options.release.metadata.marketingVersion,
21445
+ networkUnavailable: "not-proven",
21446
+ relaunchMs,
21447
+ releaseId: options.release.metadata.releaseId,
21448
+ signed: options.release.metadata.signed,
21449
+ status: "pass",
21450
+ target: "simulator"
21451
+ };
21452
+ };
21453
+ var readUntilMarker = async (reader, marker, output = "") => {
21454
+ const { done, value } = await reader.read();
21455
+ if (done)
21456
+ return { found: false, output };
21457
+ const next = `${output}${new TextDecoder().decode(value)}`;
21458
+ if (next.includes(marker))
21459
+ return { found: true, output: next };
21460
+ return readUntilMarker(reader, marker, next);
21461
+ };
21462
+ var observeDeviceLaunch = async (options) => {
21463
+ const startedAt = performance.now();
21464
+ const process2 = Bun.spawn([
21465
+ options.xcrun,
21466
+ "devicectl",
21467
+ "device",
21468
+ "process",
21469
+ "launch",
21470
+ "--console",
21471
+ "--terminate-existing",
21472
+ "--device",
21473
+ options.device,
21474
+ options.appId
21475
+ ], {
21476
+ signal: options.signal,
21477
+ stderr: "pipe",
21478
+ stdin: "ignore",
21479
+ stdout: "pipe"
21480
+ });
21481
+ const stdoutReader = process2.stdout.getReader();
21482
+ const stderrReader = process2.stderr.getReader();
21483
+ const requireMarker = async (reader) => {
21484
+ const result = await readUntilMarker(reader, options.marker);
21485
+ if (!result.found)
21486
+ throw new Error(`Console stream closed before readiness.${result.output.trim() ? ` Output: ${result.output.trim()}` : ""}`);
21487
+ return result;
21488
+ };
21489
+ const observed = Promise.any([
21490
+ requireMarker(stdoutReader),
21491
+ requireMarker(stderrReader)
21492
+ ]);
21493
+ try {
21494
+ const result = await Promise.race([
21495
+ observed.then((value) => ({ type: "output", value })).catch(() => ({ type: "closed" })),
21496
+ Bun.sleep(options.timeoutMs ?? READY_TIMEOUT_MS).then(() => ({
21497
+ type: "timeout"
21498
+ }))
21499
+ ]);
21500
+ if (result.type === "output" && result.value.found)
21501
+ return Math.round(performance.now() - startedAt);
21502
+ if (result.type === "closed")
21503
+ throw new Error("The physical iOS app exited before embedded content became ready.");
21504
+ throw new Error("The physical iOS app did not report embedded readiness before the timeout.");
21505
+ } finally {
21506
+ process2.kill();
21507
+ await Promise.all([
21508
+ stdoutReader.cancel().catch(() => {
21509
+ return;
21510
+ }),
21511
+ stderrReader.cancel().catch(() => {
21512
+ return;
21513
+ })
21514
+ ]);
21515
+ stdoutReader.releaseLock();
21516
+ stderrReader.releaseLock();
21517
+ }
21518
+ };
21519
+ var installRegisteredDeviceRelease = async (options) => {
21520
+ if (!options.release.registeredArtifactPath)
21521
+ throw new TypeError("This release has no same-archive registered-device IPA. Rebuild with `absolute mobile build ios --registered-device-artifact`.");
21522
+ const staging = await mkdtemp3(join11(dirname9(options.release.releaseRoot), ".ios-device-"));
21523
+ try {
21524
+ await requireSuccess2(options.run, [
21525
+ "/usr/bin/ditto",
21526
+ "-x",
21527
+ "-k",
21528
+ options.release.registeredArtifactPath,
21529
+ staging
21530
+ ], "iOS registered-device IPA extraction");
21531
+ const app = await findByExtension(staging, ".app");
21532
+ if (!app)
21533
+ throw new Error("The registered-device IPA contains no installable app.");
21534
+ const startedAt = performance.now();
21535
+ await requireSuccess2(options.run, [
21536
+ options.xcrun,
21537
+ "devicectl",
21538
+ "device",
21539
+ "install",
21540
+ "app",
21541
+ "--device",
21542
+ options.device,
21543
+ app
21544
+ ], "iOS registered-device Release installation");
21545
+ return Math.round(performance.now() - startedAt);
21546
+ } finally {
21547
+ await rm6(staging, { force: true, recursive: true });
21548
+ }
21549
+ };
21550
+ var inspectTestFlightInstallation = async (options) => {
21551
+ const apps = await requireSuccess2(options.run, [
21552
+ options.xcrun,
21553
+ "devicectl",
21554
+ "device",
21555
+ "info",
21556
+ "apps",
21557
+ "--device",
21558
+ options.device
21559
+ ], "TestFlight installation inspection");
21560
+ if (!apps.stdout.includes(options.release.metadata.appId))
21561
+ throw new Error("The selected device does not have the requested TestFlight app installed.");
21562
+ return 0;
21563
+ };
21564
+ var runAbsoluteIosDeviceReleaseAcceptance = async (options) => {
21565
+ if (!options.networkUnavailableConfirmed)
21566
+ throw new TypeError("Physical iOS offline acceptance requires explicit confirmation that Airplane Mode is enabled and Wi-Fi is disabled in Settings.");
21567
+ const baseRun = options.run ?? defaultRun2;
21568
+ const run = (command, commandOptions = {}) => baseRun(command, {
21569
+ ...commandOptions,
21570
+ ...options.signal ? { signal: options.signal } : {}
21571
+ });
21572
+ options.signal?.throwIfAborted();
21573
+ const xcrun = options.xcrun ?? "/usr/bin/xcrun";
21574
+ const startedAt = performance.now();
21575
+ await requireSuccess2(run, [
21576
+ xcrun,
21577
+ "devicectl",
21578
+ "device",
21579
+ "info",
21580
+ "details",
21581
+ "--device",
21582
+ options.device
21583
+ ], "iOS physical-device availability inspection");
21584
+ const installation = {
21585
+ device: options.device,
21586
+ release: options.release,
21587
+ run,
21588
+ xcrun
21589
+ };
21590
+ const installMs = options.distribution === "registered-device" ? await installRegisteredDeviceRelease(installation) : await inspectTestFlightInstallation(installation);
21591
+ const observe = options.observeLaunch ?? ((launch2) => observeDeviceLaunch({
21592
+ ...launch2,
21593
+ ...options.signal ? { signal: options.signal } : {},
21594
+ xcrun
21595
+ }));
21596
+ const marker = releaseMarker(options.release);
21597
+ const launchMs = await observe({
21598
+ appId: options.release.metadata.appId,
21599
+ device: options.device,
21600
+ marker
21601
+ });
21602
+ const relaunchMs = await observe({
21603
+ appId: options.release.metadata.appId,
21604
+ device: options.device,
21605
+ marker
21606
+ });
21607
+ return {
21608
+ artifactBytes: options.release.metadata.bytes,
21609
+ artifactExactness: options.distribution === "testflight" ? "store-delivered" : "archive-equivalent",
21610
+ artifactSha256: options.release.metadata.sha256,
21611
+ ...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
21612
+ distribution: options.distribution === "testflight" ? "apple-processed" : "registered-device",
21613
+ durationMs: Math.round(performance.now() - startedAt),
21614
+ embeddedLocal: true,
21615
+ engine: options.release.metadata.engine,
21616
+ installMs,
21617
+ launchMs,
21618
+ marketingVersion: options.release.metadata.marketingVersion,
21619
+ networkUnavailable: "user-confirmed",
21620
+ relaunchMs,
21621
+ releaseId: options.release.metadata.releaseId,
21622
+ signed: options.release.metadata.signed,
21623
+ status: "pass",
21624
+ target: "device"
21625
+ };
21626
+ };
21627
+
21086
21628
  // src/mobile/iosDeviceAcceptance.ts
21087
21629
  var absoluteIosDeviceAcceptanceCommands = (options) => {
21088
21630
  const xcrun = options.xcrun ?? "/usr/bin/xcrun";
@@ -21101,20 +21643,20 @@ var absoluteIosDeviceAcceptanceCommands = (options) => {
21101
21643
  ]
21102
21644
  };
21103
21645
  };
21104
- var requireSuccess2 = (result, message) => {
21646
+ var requireSuccess3 = (result, message) => {
21105
21647
  if (result.exitCode !== 0)
21106
21648
  throw new Error(message);
21107
21649
  return result;
21108
21650
  };
21109
21651
  var testAbsoluteIosPhysicalDevice = async (options) => {
21110
21652
  const commands = absoluteIosDeviceAcceptanceCommands(options);
21111
- 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.");
21112
- const apps = requireSuccess2(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
21653
+ 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.");
21654
+ const apps = requireSuccess3(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
21113
21655
  if (!apps.stdout.includes(options.appId))
21114
21656
  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.");
21115
21657
  const now = options.now ?? performance.now.bind(performance);
21116
21658
  const startedAt = now();
21117
- requireSuccess2(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
21659
+ requireSuccess3(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
21118
21660
  await options.waitForHmr();
21119
21661
  return {
21120
21662
  hmrConnected: true,
@@ -21124,7 +21666,7 @@ var testAbsoluteIosPhysicalDevice = async (options) => {
21124
21666
  };
21125
21667
 
21126
21668
  // src/mobile/iosConformance.ts
21127
- import { readFile as readFile12, stat as stat4 } from "fs/promises";
21669
+ import { readFile as readFile13, stat as stat5 } from "fs/promises";
21128
21670
  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");
21129
21671
  var parseAbsoluteIosHmrLog = (line) => {
21130
21672
  const match = HMR_LINE.exec(line);
@@ -21156,13 +21698,13 @@ var waitForAbsoluteIosHmrLog = async (options) => {
21156
21698
  const sleep = options.sleep ?? Bun.sleep;
21157
21699
  const timeoutMs = options.timeoutMs ?? 30000;
21158
21700
  const deadline = Date.now() + timeoutMs;
21159
- let offset = options.startOffset ?? await stat4(options.logPath).then(({ size }) => size).catch(() => 0);
21701
+ let offset = options.startOffset ?? await stat5(options.logPath).then(({ size }) => size).catch(() => 0);
21160
21702
  let buffered = "";
21161
21703
  const poll = async () => {
21162
21704
  if (Date.now() > deadline)
21163
21705
  throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
21164
21706
  options.signal?.throwIfAborted();
21165
- const contents = await readFile12(options.logPath).catch(() => Buffer.alloc(0));
21707
+ const contents = await readFile13(options.logPath).catch(() => Buffer.alloc(0));
21166
21708
  if (contents.byteLength < offset) {
21167
21709
  offset = 0;
21168
21710
  buffered = "";
@@ -21183,8 +21725,8 @@ var waitForAbsoluteIosHmrLog = async (options) => {
21183
21725
  };
21184
21726
 
21185
21727
  // src/mobile/nativeTestReport.ts
21186
- import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
21187
- import { join as join11 } from "path";
21728
+ import { mkdir as mkdir10, readFile as readFile14, writeFile as writeFile11 } from "fs/promises";
21729
+ import { join as join12 } from "path";
21188
21730
  var secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
21189
21731
  var bearerPattern = /bearer\s+[^\s,;]+/giu;
21190
21732
  var coordinatePattern = /\b(latitude|longitude|lat|lng)(\s*[=:]\s*)-?\d+(?:\.\d+)?/giu;
@@ -21211,8 +21753,10 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
21211
21753
  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."}`;
21212
21754
  if (run.release)
21213
21755
  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.`;
21756
+ if (run.iosRelease)
21757
+ 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.`;
21214
21758
  let hmrDetails = "Correlated edit timing was not requested. Rerun with --wait-for-hmr.";
21215
- if (run.release)
21759
+ if (run.release || run.iosRelease)
21216
21760
  hmrDetails = "HMR is intentionally not part of installed production-release acceptance.";
21217
21761
  if (run.hmr)
21218
21762
  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)`}.`;
@@ -21251,6 +21795,24 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
21251
21795
  result: run.release.embeddedOffline ? "PASS" : "FAIL"
21252
21796
  });
21253
21797
  }
21798
+ if (run.iosRelease) {
21799
+ const release = run.iosRelease;
21800
+ checks.push({
21801
+ details: `Validated immutable release ${release.releaseId} (${release.artifactBytes} bytes, SHA-256 ${release.artifactSha256}); ${release.distribution} evidence is ${release.artifactExactness}, executed on the ${release.remote ? "paired Remote Mac" : "local Mac"}, and installed in ${release.installMs}ms.`,
21802
+ id: "AUTO-IOS-RELEASE-01",
21803
+ result: run.status === "pass" ? "PASS" : "FAIL"
21804
+ }, {
21805
+ 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.",
21806
+ id: "AUTO-IOS-RELEASE-OFFLINE-01",
21807
+ result: release.networkUnavailable === "user-confirmed" ? "PASS" : "SKIPPED"
21808
+ });
21809
+ if (release.remote)
21810
+ checks.push({
21811
+ details: "The installed-release lane executed through the paired Remote Mac protocol; immutable inputs and returned evidence were verified on the initiating host.",
21812
+ id: "AUTO-IOS-REMOTE-01",
21813
+ result: run.status === "pass" ? "PASS" : "FAIL"
21814
+ });
21815
+ }
21254
21816
  if (run.upgrade) {
21255
21817
  const { upgrade } = run;
21256
21818
  checks.push({
@@ -21313,7 +21875,7 @@ var createAbsoluteNativeTestReport = (options) => ({
21313
21875
  run: options.run
21314
21876
  });
21315
21877
  var readPackageVersionForNativeReport = async (packageJsonPath) => {
21316
- const manifest = JSON.parse(await readFile13(packageJsonPath, "utf8"));
21878
+ const manifest = JSON.parse(await readFile14(packageJsonPath, "utf8"));
21317
21879
  if (typeof manifest !== "object" || manifest === null)
21318
21880
  return "unknown";
21319
21881
  const version = Reflect.get(manifest, "version");
@@ -21351,9 +21913,9 @@ ${table(report.manualChecks)}
21351
21913
  `;
21352
21914
  };
21353
21915
  var writeAbsoluteNativeTestReport = async (directory, report) => {
21354
- await mkdir9(directory, { recursive: true });
21355
- const jsonPath = join11(directory, "report.json");
21356
- const markdownPath = join11(directory, "report.md");
21916
+ await mkdir10(directory, { recursive: true });
21917
+ const jsonPath = join12(directory, "report.json");
21918
+ const markdownPath = join12(directory, "report.md");
21357
21919
  await Promise.all([
21358
21920
  writeFile11(jsonPath, `${JSON.stringify(report, null, 2)}
21359
21921
  `),
@@ -21468,7 +22030,7 @@ var createAbsoluteIosPartnerReport = (options) => {
21468
22030
  absolutejsVersion: options.absolutejsVersion,
21469
22031
  bunVersion: options.bunVersion,
21470
22032
  macosVersion: options.macosVersion,
21471
- provider: "capacitor",
22033
+ provider: options.provider ?? "capacitor",
21472
22034
  xcodeVersion: options.xcodeVersion
21473
22035
  },
21474
22036
  run: {
@@ -21609,8 +22171,8 @@ var createAbsoluteAndroidTestReport = (options) => {
21609
22171
  };
21610
22172
 
21611
22173
  // src/mobile/releasePublisher.ts
21612
- import { access as access7 } from "fs/promises";
21613
- import { isAbsolute as isAbsolute4, relative as relative6, resolve as resolve7, sep as sep4 } from "path";
22174
+ import { access as access8 } from "fs/promises";
22175
+ import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve8, sep as sep5 } from "path";
21614
22176
  import { pathToFileURL as pathToFileURL2 } from "url";
21615
22177
  var prepareAbsoluteIosRelease = async (publisher, options) => {
21616
22178
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -21636,17 +22198,17 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
21636
22198
  var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21637
22199
  var isPublisher = (value) => isRecord6(value) && typeof value.publish === "function";
21638
22200
  var publisherModulePath = (projectRoot, requested) => {
21639
- const root = resolve7(projectRoot);
21640
- const path = resolve7(root, requested);
21641
- const projectRelative = relative6(root, path);
21642
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep4}`) || isAbsolute4(projectRelative)) {
22201
+ const root = resolve8(projectRoot);
22202
+ const path = resolve8(root, requested);
22203
+ const projectRelative = relative7(root, path);
22204
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute5(projectRelative)) {
21643
22205
  throw new TypeError("mobile publish --registry must remain inside the project.");
21644
22206
  }
21645
22207
  return path;
21646
22208
  };
21647
22209
  var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
21648
22210
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
21649
- await access7(modulePath).catch(() => {
22211
+ await access8(modulePath).catch(() => {
21650
22212
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
21651
22213
  });
21652
22214
  const loaded = await import(pathToFileURL2(modulePath).href);
@@ -21708,8 +22270,8 @@ var publishAbsoluteIosRelease = async (options) => {
21708
22270
  };
21709
22271
 
21710
22272
  // src/mobile/mobileInspect.ts
21711
- import { access as access8, readFile as readFile14 } from "fs/promises";
21712
- import { join as join12, relative as relative7, resolve as resolve8 } from "path";
22273
+ import { access as access9, readFile as readFile15 } from "fs/promises";
22274
+ import { join as join13, relative as relative8, resolve as resolve9 } from "path";
21713
22275
  var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
21714
22276
  var MOBILE_PACKAGE_NAMES = new Set([
21715
22277
  "@absolutejs/absolute",
@@ -21724,19 +22286,19 @@ var MOBILE_PACKAGE_NAMES = new Set([
21724
22286
  ]);
21725
22287
  var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21726
22288
  var portablePath2 = (projectRoot, path) => {
21727
- const value = relative7(resolve8(projectRoot), resolve8(path)).replaceAll("\\", "/");
22289
+ const value = relative8(resolve9(projectRoot), resolve9(path)).replaceAll("\\", "/");
21728
22290
  return value || ".";
21729
22291
  };
21730
22292
  var pathExists4 = async (path) => {
21731
22293
  try {
21732
- await access8(path);
22294
+ await access9(path);
21733
22295
  return true;
21734
22296
  } catch {
21735
22297
  return false;
21736
22298
  }
21737
22299
  };
21738
22300
  var readObject2 = async (path) => {
21739
- const value = JSON.parse(await readFile14(path, "utf8"));
22301
+ const value = JSON.parse(await readFile15(path, "utf8"));
21740
22302
  if (!isObject2(value))
21741
22303
  throw new TypeError("JSON root must be an object.");
21742
22304
  return value;
@@ -21748,13 +22310,13 @@ var addPackageDeclarations = (declarations, value) => {
21748
22310
  declarations.set(name, declared);
21749
22311
  };
21750
22312
  var packageInspections = async (projectRoot, additionalNames) => {
21751
- const project = await readObject2(join12(projectRoot, "package.json"));
22313
+ const project = await readObject2(join13(projectRoot, "package.json"));
21752
22314
  const declarations = new Map;
21753
22315
  for (const field2 of ["dependencies", "devDependencies"])
21754
22316
  addPackageDeclarations(declarations, project[field2]);
21755
22317
  const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
21756
22318
  return Promise.all(names.map(async (name) => {
21757
- const installedManifest = await readObject2(join12(projectRoot, "node_modules", name, "package.json")).catch(() => {
22319
+ const installedManifest = await readObject2(join13(projectRoot, "node_modules", name, "package.json")).catch(() => {
21758
22320
  return;
21759
22321
  });
21760
22322
  const installed = installedManifest?.version;
@@ -21813,7 +22375,7 @@ var inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) =>
21813
22375
  },
21814
22376
  format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
21815
22377
  nativeProjects: await Promise.all(config.platforms.map(async (platform) => {
21816
- const path = join12(config.nativeProjectDirectory, platform);
22378
+ const path = join13(config.nativeProjectDirectory, platform);
21817
22379
  return {
21818
22380
  initialized: await pathExists4(path),
21819
22381
  path: portablePath2(projectRoot, path),
@@ -21870,8 +22432,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
21870
22432
 
21871
22433
  // src/mobile/ciWorkflow.ts
21872
22434
  import { existsSync } from "fs";
21873
- import { access as access9, mkdir as mkdir10, readFile as readFile15, writeFile as writeFile12 } from "fs/promises";
21874
- import { dirname as dirname9, extname as extname2, relative as relative8, resolve as resolve9, sep as sep5 } from "path";
22435
+ import { access as access10, mkdir as mkdir11, readFile as readFile16, writeFile as writeFile12 } from "fs/promises";
22436
+ import { dirname as dirname10, extname as extname2, relative as relative9, resolve as resolve10, sep as sep6 } from "path";
21875
22437
  var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
21876
22438
  var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
21877
22439
  var CI_ENV_INDENTATION = 6;
@@ -21890,9 +22452,9 @@ var RESERVED_SECRET_NAMES = new Set([
21890
22452
  "APP_STORE_CONNECT_KEY_ID",
21891
22453
  "APP_STORE_CONNECT_PRIVATE_KEY_BASE64"
21892
22454
  ]);
21893
- var exists3 = async (path) => {
22455
+ var exists4 = async (path) => {
21894
22456
  try {
21895
- await access9(path);
22457
+ await access10(path);
21896
22458
  return true;
21897
22459
  } catch {
21898
22460
  return false;
@@ -21900,10 +22462,10 @@ var exists3 = async (path) => {
21900
22462
  };
21901
22463
  var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
21902
22464
  var projectPath2 = (projectRoot, value, field2, options = {}) => {
21903
- const root = resolve9(projectRoot);
21904
- const path = resolve9(root, value);
21905
- const portable = relative8(root, path).replaceAll("\\", "/");
21906
- if (portable === ".." || portable.startsWith(`..${sep5}`) || portable.startsWith("../") || portable === "") {
22465
+ const root = resolve10(projectRoot);
22466
+ const path = resolve10(root, value);
22467
+ const portable = relative9(root, path).replaceAll("\\", "/");
22468
+ if (portable === ".." || portable.startsWith(`..${sep6}`) || portable.startsWith("../") || portable === "") {
21907
22469
  throw new TypeError(`${field2} must remain inside the project root.`);
21908
22470
  }
21909
22471
  if (/\r|\n/u.test(portable) || portable.startsWith("-"))
@@ -21913,11 +22475,11 @@ var projectPath2 = (projectRoot, value, field2, options = {}) => {
21913
22475
  return portable;
21914
22476
  };
21915
22477
  var workflowOutputPath = (projectRoot, value) => {
21916
- const root = resolve9(projectRoot);
21917
- const workflows = resolve9(root, ".github/workflows");
21918
- const path = resolve9(root, value ?? ".github/workflows/absolute-mobile.yml");
21919
- const portable = relative8(workflows, path);
21920
- if (portable === ".." || portable.startsWith(`..${sep5}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
22478
+ const root = resolve10(projectRoot);
22479
+ const workflows = resolve10(root, ".github/workflows");
22480
+ const path = resolve10(root, value ?? ".github/workflows/absolute-mobile.yml");
22481
+ const portable = relative9(workflows, path);
22482
+ if (portable === ".." || portable.startsWith(`..${sep6}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
21921
22483
  throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
21922
22484
  }
21923
22485
  return path;
@@ -22346,11 +22908,11 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
22346
22908
  var writeAbsoluteMobileGithubWorkflow = async (options) => {
22347
22909
  const path = workflowOutputPath(options.projectRoot, options.outputPath);
22348
22910
  const generated = createAbsoluteMobileGithubWorkflow(options);
22349
- const previous = await exists3(path) ? await readFile15(path, "utf8") : undefined;
22911
+ const previous = await exists4(path) ? await readFile16(path, "utf8") : undefined;
22350
22912
  if (previous !== undefined && previous !== generated.workflow && !options.force)
22351
- throw new TypeError(`${relative8(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
22913
+ throw new TypeError(`${relative9(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
22352
22914
  if (previous !== generated.workflow) {
22353
- await mkdir10(dirname9(path), { recursive: true });
22915
+ await mkdir11(dirname10(path), { recursive: true });
22354
22916
  await writeFile12(path, generated.workflow);
22355
22917
  }
22356
22918
  return {
@@ -22364,46 +22926,46 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
22364
22926
  };
22365
22927
 
22366
22928
  // src/mobile/updateSigning.ts
22367
- import { createHash as createHash4, sign, verify } from "crypto";
22929
+ import { createHash as createHash5, sign, verify } from "crypto";
22368
22930
  import {
22369
22931
  cp,
22370
- mkdir as mkdir11,
22371
- mkdtemp as mkdtemp3,
22372
- readdir as readdir5,
22373
- readFile as readFile16,
22932
+ mkdir as mkdir12,
22933
+ mkdtemp as mkdtemp4,
22934
+ readdir as readdir6,
22935
+ readFile as readFile17,
22374
22936
  rename as rename10,
22375
- rm as rm6,
22376
- stat as stat5,
22937
+ rm as rm7,
22938
+ stat as stat6,
22377
22939
  writeFile as writeFile13
22378
22940
  } from "fs/promises";
22379
- import { dirname as dirname10, join as join13, relative as relative9, resolve as resolve10 } from "path";
22941
+ import { dirname as dirname11, join as join14, relative as relative10, resolve as resolve11 } from "path";
22380
22942
  var UPDATE_MANIFEST_FILE = "update.json";
22381
22943
  var UPDATE_FILES_DIRECTORY = "files";
22382
- var sha2562 = (value) => createHash4("sha256").update(value).digest("hex");
22944
+ var sha2563 = (value) => createHash5("sha256").update(value).digest("hex");
22383
22945
  var listFiles = async (root, directory = root) => {
22384
- const entries = await readdir5(directory, { withFileTypes: true });
22946
+ const entries = await readdir6(directory, { withFileTypes: true });
22385
22947
  const paths = await Promise.all(entries.map(async (entry) => {
22386
- const path = join13(directory, entry.name);
22948
+ const path = join14(directory, entry.name);
22387
22949
  if (entry.isDirectory())
22388
22950
  return listFiles(root, path);
22389
22951
  if (!entry.isFile())
22390
22952
  throw new TypeError("Mobile updates cannot contain links or special files.");
22391
- return [relative9(root, path).replaceAll("\\", "/")];
22953
+ return [relative10(root, path).replaceAll("\\", "/")];
22392
22954
  }));
22393
22955
  return paths.flat().sort((left, right) => left.localeCompare(right));
22394
22956
  };
22395
22957
  var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
22396
- const source = join13(root, path);
22958
+ const source = join14(root, path);
22397
22959
  const [metadata, contents] = await Promise.all([
22398
- stat5(source),
22399
- readFile16(source)
22960
+ stat6(source),
22961
+ readFile17(source)
22400
22962
  ]);
22401
- return { bytes: metadata.size, path, sha256: sha2562(contents) };
22963
+ return { bytes: metadata.size, path, sha256: sha2563(contents) };
22402
22964
  }));
22403
- var releaseIdFor = (value) => `amu_${sha2562(canonicalizeAbsoluteMobileUpdate(value))}`;
22965
+ var releaseIdFor = (value) => `amu_${sha2563(canonicalizeAbsoluteMobileUpdate(value))}`;
22404
22966
  var buildAbsoluteMobileUpdate = async (options) => {
22405
- const bundleDirectory = resolve10(options.bundleDirectory);
22406
- const outputRoot = resolve10(options.outputDirectory);
22967
+ const bundleDirectory = resolve11(options.bundleDirectory);
22968
+ const outputRoot = resolve11(options.outputDirectory);
22407
22969
  if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
22408
22970
  throw new TypeError("Mobile update output must be outside the embedded bundle.");
22409
22971
  const paths = await listFiles(bundleDirectory);
@@ -22431,30 +22993,30 @@ var buildAbsoluteMobileUpdate = async (options) => {
22431
22993
  value: signature.toString("base64")
22432
22994
  }
22433
22995
  });
22434
- await mkdir11(outputRoot, { recursive: true });
22435
- const outputDirectory = join13(outputRoot, manifest.releaseId);
22436
- const staging = await mkdtemp3(join13(outputRoot, ".stage-"));
22996
+ await mkdir12(outputRoot, { recursive: true });
22997
+ const outputDirectory = join14(outputRoot, manifest.releaseId);
22998
+ const staging = await mkdtemp4(join14(outputRoot, ".stage-"));
22437
22999
  try {
22438
- await cp(bundleDirectory, join13(staging, UPDATE_FILES_DIRECTORY), {
23000
+ await cp(bundleDirectory, join14(staging, UPDATE_FILES_DIRECTORY), {
22439
23001
  force: true,
22440
23002
  recursive: true
22441
23003
  });
22442
- await writeFile13(join13(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
23004
+ await writeFile13(join14(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
22443
23005
  `);
22444
23006
  await rename10(staging, outputDirectory);
22445
23007
  } catch (error) {
22446
- await rm6(staging, { force: true, recursive: true });
23008
+ await rm7(staging, { force: true, recursive: true });
22447
23009
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
22448
23010
  throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
22449
23011
  throw error;
22450
23012
  }
22451
23013
  return {
22452
23014
  manifest,
22453
- manifestPath: join13(outputDirectory, UPDATE_MANIFEST_FILE),
23015
+ manifestPath: join14(outputDirectory, UPDATE_MANIFEST_FILE),
22454
23016
  outputDirectory
22455
23017
  };
22456
23018
  };
22457
- var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile16(join13(resolve10(directory), UPDATE_MANIFEST_FILE), "utf8")));
23019
+ var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile17(join14(resolve11(directory), UPDATE_MANIFEST_FILE), "utf8")));
22458
23020
  var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
22459
23021
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
22460
23022
  const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
@@ -22464,8 +23026,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
22464
23026
  };
22465
23027
 
22466
23028
  // src/mobile/expoUpdate.ts
22467
- import { access as access10, mkdir as mkdir12, readFile as readFile17, writeFile as writeFile14 } from "fs/promises";
22468
- import { dirname as dirname11, extname as extname3, join as join14 } from "path";
23029
+ import { access as access11, mkdir as mkdir13, readFile as readFile18, writeFile as writeFile14 } from "fs/promises";
23030
+ import { dirname as dirname12, extname as extname3, join as join15 } from "path";
22469
23031
  var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
22470
23032
  var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
22471
23033
  var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -22531,7 +23093,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
22531
23093
  };
22532
23094
  };
22533
23095
  var finalizeAbsoluteExpoUpdateExport = async (options) => {
22534
- const metadata = JSON.parse(await readFile17(join14(options.exportDirectory, "metadata.json"), "utf8"));
23096
+ const metadata = JSON.parse(await readFile18(join15(options.exportDirectory, "metadata.json"), "utf8"));
22535
23097
  if (!object2(metadata) || !object2(metadata.fileMetadata))
22536
23098
  throw new TypeError("Expo export metadata.json is invalid.");
22537
23099
  const descriptor = parseAbsoluteExpoUpdateDescriptor({
@@ -22542,11 +23104,11 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
22542
23104
  runtimeVersion: options.runtimeVersion
22543
23105
  });
22544
23106
  const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
22545
- await Promise.all(referenced.map(({ path }) => access10(join14(options.exportDirectory, path)).catch(() => {
23107
+ await Promise.all(referenced.map(({ path }) => access11(join15(options.exportDirectory, path)).catch(() => {
22546
23108
  throw new TypeError(`Expo export metadata references missing asset ${path}.`);
22547
23109
  })));
22548
- const destination = join14(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
22549
- await mkdir12(dirname11(destination), { recursive: true });
23110
+ const destination = join15(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
23111
+ await mkdir13(dirname12(destination), { recursive: true });
22550
23112
  await writeFile14(destination, `${JSON.stringify(descriptor, null, "\t")}
22551
23113
  `);
22552
23114
  return { descriptor, path: destination };
@@ -22554,21 +23116,21 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
22554
23116
 
22555
23117
  // src/mobile/expoCodeSigning.ts
22556
23118
  var import_code_signing_certificates = __toESM(require_main(), 1);
22557
- import { access as access11, mkdir as mkdir13, writeFile as writeFile15 } from "fs/promises";
22558
- import { dirname as dirname12, isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep6 } from "path";
22559
- var exists4 = async (path) => access11(path).then(() => true).catch(() => false);
22560
- var inside2 = (root, path) => {
22561
- const location = relative10(root, path);
22562
- return location === "" || location !== ".." && !location.startsWith(`..${sep6}`) && !isAbsolute5(location);
23119
+ import { access as access12, mkdir as mkdir14, writeFile as writeFile15 } from "fs/promises";
23120
+ import { dirname as dirname13, isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep7 } from "path";
23121
+ var exists5 = async (path) => access12(path).then(() => true).catch(() => false);
23122
+ var inside3 = (root, path) => {
23123
+ const location = relative11(root, path);
23124
+ return location === "" || location !== ".." && !location.startsWith(`..${sep7}`) && !isAbsolute6(location);
22563
23125
  };
22564
23126
  var generateAbsoluteExpoCodeSigning = async (options) => {
22565
- const root = resolve11(options.projectRoot);
22566
- const certificatePath = resolve11(root, options.certificatePath);
22567
- const privateKeyPath = resolve11(root, options.privateKeyPath);
22568
- const publicKeyPath = resolve11(root, options.publicKeyPath ?? resolve11(dirname12(privateKeyPath), "public-key.pem"));
22569
- if (!inside2(root, certificatePath))
23127
+ const root = resolve12(options.projectRoot);
23128
+ const certificatePath = resolve12(root, options.certificatePath);
23129
+ const privateKeyPath = resolve12(root, options.privateKeyPath);
23130
+ const publicKeyPath = resolve12(root, options.publicKeyPath ?? resolve12(dirname13(privateKeyPath), "public-key.pem"));
23131
+ if (!inside3(root, certificatePath))
22570
23132
  throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
22571
- if (inside2(root, privateKeyPath) || inside2(root, publicKeyPath))
23133
+ if (inside3(root, privateKeyPath) || inside3(root, publicKeyPath))
22572
23134
  throw new TypeError("Expo code-signing private/public key output must remain outside the project and source control.");
22573
23135
  const commonName = options.commonName.trim();
22574
23136
  if (!commonName || commonName.length > 64)
@@ -22577,7 +23139,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22577
23139
  if (!Number.isSafeInteger(validityYears) || validityYears < 1 || validityYears > 20)
22578
23140
  throw new TypeError("Expo code-signing validity must be an integer from 1 through 20 years.");
22579
23141
  const collisions = (await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map(async (path) => ({
22580
- exists: await exists4(path),
23142
+ exists: await exists5(path),
22581
23143
  path
22582
23144
  })))).filter((entry) => entry.exists);
22583
23145
  if (collisions.length > 0)
@@ -22595,7 +23157,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22595
23157
  import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
22596
23158
  const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
22597
23159
  const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
22598
- await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir13(dirname12(path), { recursive: true })));
23160
+ await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir14(dirname13(path), { recursive: true })));
22599
23161
  await Promise.all([
22600
23162
  writeFile15(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
22601
23163
  writeFile15(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
@@ -22610,8 +23172,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22610
23172
  };
22611
23173
 
22612
23174
  // src/mobile/updatePublisher.ts
22613
- import { access as access12 } from "fs/promises";
22614
- import { isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep7 } from "path";
23175
+ import { access as access13 } from "fs/promises";
23176
+ import { isAbsolute as isAbsolute7, relative as relative12, resolve as resolve13, sep as sep8 } from "path";
22615
23177
  import { pathToFileURL as pathToFileURL3 } from "url";
22616
23178
  var lifecycleMethod = (publisher, name) => {
22617
23179
  const method = publisher[name];
@@ -22720,16 +23282,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
22720
23282
  var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22721
23283
  var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
22722
23284
  var projectPath3 = (projectRoot, requested, label) => {
22723
- const root = resolve12(projectRoot);
22724
- const path = resolve12(root, requested);
22725
- const projectRelative = relative11(root, path);
22726
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep7}`) || isAbsolute6(projectRelative))
23285
+ const root = resolve13(projectRoot);
23286
+ const path = resolve13(root, requested);
23287
+ const projectRelative = relative12(root, path);
23288
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep8}`) || isAbsolute7(projectRelative))
22727
23289
  throw new TypeError(`${label} must remain inside the project.`);
22728
23290
  return path;
22729
23291
  };
22730
23292
  var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
22731
23293
  const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
22732
- await access12(modulePath).catch(() => {
23294
+ await access13(modulePath).catch(() => {
22733
23295
  throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
22734
23296
  });
22735
23297
  const loaded = await import(pathToFileURL3(modulePath).href);
@@ -22827,7 +23389,7 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
22827
23389
  ];
22828
23390
  var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
22829
23391
  var directProjectPackages = async (projectRoot) => {
22830
- const manifest = JSON.parse(await readFile18(join15(projectRoot, "package.json"), "utf8"));
23392
+ const manifest = JSON.parse(await readFile19(join16(projectRoot, "package.json"), "utf8"));
22831
23393
  if (!isRecord7(manifest))
22832
23394
  throw new TypeError("Application package.json must contain an object.");
22833
23395
  const names = new Set;
@@ -22841,7 +23403,7 @@ var directProjectPackages = async (projectRoot) => {
22841
23403
  };
22842
23404
  var resolvedPackageVersion = async (projectRoot, packageName) => {
22843
23405
  try {
22844
- const manifest = JSON.parse(await readFile18(join15(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
23406
+ const manifest = JSON.parse(await readFile19(join16(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
22845
23407
  return isRecord7(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
22846
23408
  } catch {
22847
23409
  return;
@@ -22905,9 +23467,9 @@ var runCapacitor = async (projectRoot, args) => {
22905
23467
  };
22906
23468
  var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
22907
23469
  var expoExecutable = async (project) => {
22908
- const executable = join15(project, "node_modules", ".bin", "expo");
23470
+ const executable = join16(project, "node_modules", ".bin", "expo");
22909
23471
  try {
22910
- await access13(executable);
23472
+ await access14(executable);
22911
23473
  return executable;
22912
23474
  } catch {
22913
23475
  throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
@@ -22939,14 +23501,14 @@ var runExpo = async (project, args, options = {}) => {
22939
23501
  };
22940
23502
  var ensureExpoPackages = async (project, args) => {
22941
23503
  try {
22942
- const manifest = JSON.parse(await readFile18(join15(project, "package.json"), "utf8"));
23504
+ const manifest = JSON.parse(await readFile19(join16(project, "package.json"), "utf8"));
22943
23505
  const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
22944
23506
  if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
22945
23507
  throw new TypeError("Generated Expo dependencies are invalid.");
22946
23508
  await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
22947
23509
  if (typeof expected !== "string")
22948
23510
  throw new TypeError("Generated Expo dependency version is invalid.");
22949
- const installed = JSON.parse(await readFile18(join15(project, "node_modules", name, "package.json"), "utf8"));
23511
+ const installed = JSON.parse(await readFile19(join16(project, "node_modules", name, "package.json"), "utf8"));
22950
23512
  const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
22951
23513
  if (typeof actual !== "string")
22952
23514
  throw new TypeError("Installed Expo dependency version is invalid.");
@@ -23144,7 +23706,7 @@ var sync = async (args) => {
23144
23706
  };
23145
23707
  var associations = async (args) => {
23146
23708
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
23147
- const outputDirectory = resolve13(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
23709
+ const outputDirectory = resolve14(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
23148
23710
  if (args.includes("--verify")) {
23149
23711
  const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
23150
23712
  console.log(`Verified ${result2.results.length} hosted association files`);
@@ -23196,7 +23758,7 @@ var generateGithubCi = async (args) => {
23196
23758
  const publicResult = {
23197
23759
  changed: result.changed,
23198
23760
  format: result.format,
23199
- path: relative12(projectRoot, result.path).replaceAll("\\", "/"),
23761
+ path: relative13(projectRoot, result.path).replaceAll("\\", "/"),
23200
23762
  platforms: result.platforms,
23201
23763
  publishing: result.publishing,
23202
23764
  requiredSecrets: result.requiredSecrets
@@ -23307,9 +23869,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
23307
23869
  temporaryDirectory: undefined
23308
23870
  };
23309
23871
  await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
23310
- const temporaryParent = join15(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
23311
- await mkdir14(temporaryParent, { recursive: true });
23312
- const temporaryDirectory = await mkdtemp4(join15(temporaryParent, ".stage-"));
23872
+ const temporaryParent = join16(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
23873
+ await mkdir15(temporaryParent, { recursive: true });
23874
+ const temporaryDirectory = await mkdtemp5(join16(temporaryParent, ".stage-"));
23313
23875
  try {
23314
23876
  await runExpo(mobile.nativeProjectDirectory, [
23315
23877
  "export",
@@ -23321,7 +23883,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
23321
23883
  temporaryDirectory,
23322
23884
  "--clear"
23323
23885
  ], { production: true });
23324
- const generatedApp = JSON.parse(await readFile18(join15(mobile.nativeProjectDirectory, "app.json"), "utf8"));
23886
+ const generatedApp = JSON.parse(await readFile19(join16(mobile.nativeProjectDirectory, "app.json"), "utf8"));
23325
23887
  const expoConfig = isRecord7(generatedApp) ? generatedApp.expo : undefined;
23326
23888
  if (!isRecord7(expoConfig))
23327
23889
  throw new TypeError("Generated Expo app configuration is invalid.");
@@ -23339,7 +23901,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
23339
23901
  temporaryDirectory
23340
23902
  };
23341
23903
  } catch (error) {
23342
- await rm7(temporaryDirectory, { force: true, recursive: true });
23904
+ await rm8(temporaryDirectory, { force: true, recursive: true });
23343
23905
  throw error;
23344
23906
  }
23345
23907
  };
@@ -23364,11 +23926,11 @@ var buildMobileUpdate = async (args) => {
23364
23926
  let expoExportDirectory;
23365
23927
  try {
23366
23928
  await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
23367
- const embedded = JSON.parse(await readFile18(join15(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
23929
+ const embedded = JSON.parse(await readFile19(join16(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
23368
23930
  const runtimeFingerprint = isRecord7(embedded) ? embedded.nativeRuntime : undefined;
23369
23931
  if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
23370
23932
  throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
23371
- const privateKey = await readFile18(resolve13(projectRoot, signingKeyPath));
23933
+ const privateKey = await readFile19(resolve14(projectRoot, signingKeyPath));
23372
23934
  const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
23373
23935
  const derivedPublicKey = createPublicKey2(privateKey).export({
23374
23936
  format: "der",
@@ -23389,7 +23951,7 @@ var buildMobileUpdate = async (args) => {
23389
23951
  channel: mobile.updates.channel,
23390
23952
  classification,
23391
23953
  keyId,
23392
- outputDirectory: valueAfter(args, "--outdir") ?? join15(projectRoot, ".absolutejs", "mobile", "updates"),
23954
+ outputDirectory: valueAfter(args, "--outdir") ?? join16(projectRoot, ".absolutejs", "mobile", "updates"),
23393
23955
  privateKey,
23394
23956
  runtimeFingerprint
23395
23957
  });
@@ -23401,7 +23963,7 @@ var buildMobileUpdate = async (args) => {
23401
23963
  return result;
23402
23964
  } finally {
23403
23965
  if (expoExportDirectory)
23404
- await rm7(expoExportDirectory, { force: true, recursive: true });
23966
+ await rm8(expoExportDirectory, { force: true, recursive: true });
23405
23967
  sendTelemetryEvent("mobile:update-build", {
23406
23968
  classification: classification ?? "invalid",
23407
23969
  durationMs: Math.round(performance.now() - startedAt),
@@ -23431,7 +23993,7 @@ var generateExpoUpdateSigning = async (args) => {
23431
23993
  ...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
23432
23994
  ...validityYears === undefined ? {} : { validityYears }
23433
23995
  });
23434
- const certificatePath = relative12(projectRoot, result.certificatePath).replaceAll("\\", "/");
23996
+ const certificatePath = relative13(projectRoot, result.certificatePath).replaceAll("\\", "/");
23435
23997
  sendTelemetryEvent("mobile:update-code-signing-generated", {
23436
23998
  engine: "expo",
23437
23999
  validityYears: validityYears ?? 10
@@ -23493,7 +24055,7 @@ var provisionMobileUpdate = async (args) => {
23493
24055
  publicKeys: mobile.updates.publicKeys,
23494
24056
  storage: requestedStorage
23495
24057
  });
23496
- console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative12(projectRoot, path).replaceAll("\\", "/")}.`);
24058
+ console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative13(projectRoot, path).replaceAll("\\", "/")}.`);
23497
24059
  if (requestedStorage === "local")
23498
24060
  console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
23499
24061
  else
@@ -23905,6 +24467,7 @@ var prepareCapacitorIosReleaseProject = async (mobile, projectRoot) => {
23905
24467
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["ios"]);
23906
24468
  await applyAbsoluteNativeUpdates(mobile, ["ios"]);
23907
24469
  await applyAbsoluteNativeObservability(mobile, ["ios"]);
24470
+ await applyAbsoluteNativeReleaseReadiness(mobile, ["ios"]);
23908
24471
  };
23909
24472
  var prepareIosReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoIosReleaseProject(mobile, projectRoot, args) : prepareCapacitorIosReleaseProject(mobile, projectRoot);
23910
24473
  var prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
@@ -23969,7 +24532,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
23969
24532
  const durationMs = Math.round(performance.now() - startedAt);
23970
24533
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
23971
24534
  console.log(`Artifact: ${release.artifactPath}`);
23972
- console.log(`Metadata: ${join15(release.releaseRoot, "release.json")}`);
24535
+ console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
23973
24536
  return release;
23974
24537
  } finally {
23975
24538
  sendTelemetryEvent("mobile:android-release-build", {
@@ -24057,6 +24620,7 @@ var buildLocalIosRelease = async (options) => {
24057
24620
  developmentTeam: process.env.ABSOLUTE_IOS_DEVELOPMENT_TEAM,
24058
24621
  ...options.mobile.engine === "expo" ? { env: expoProductionEnvironment() } : {},
24059
24622
  outputDirectory: valueAfter(options.args, "--outdir"),
24623
+ registeredDeviceArtifact: options.args.includes("--registered-device-artifact"),
24060
24624
  ...options.prepareBuildNumber === undefined ? {} : { prepareBuildNumber: options.prepareBuildNumber },
24061
24625
  projectRoot: options.projectRoot
24062
24626
  });
@@ -24114,6 +24678,7 @@ var buildIos = async (args, prepareBuildNumber) => {
24114
24678
  },
24115
24679
  ...prepareBuildNumber === undefined ? {} : { prepareBuildNumber },
24116
24680
  project: createAbsoluteRemoteIosDevProject(mobile, projectRoot, remoteProfile),
24681
+ registeredDeviceArtifact: args.includes("--registered-device-artifact"),
24117
24682
  signal: cancellation?.signal
24118
24683
  }) : await buildLocalIosRelease({
24119
24684
  args,
@@ -24125,7 +24690,9 @@ var buildIos = async (args, prepareBuildNumber) => {
24125
24690
  const durationMs = Math.round(performance.now() - startedAt);
24126
24691
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
24127
24692
  console.log(`Artifact: ${release.artifactPath}`);
24128
- console.log(`Metadata: ${join15(release.releaseRoot, "release.json")}`);
24693
+ if (release.registeredArtifactPath)
24694
+ console.log(`Registered-device artifact: ${release.registeredArtifactPath}`);
24695
+ console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
24129
24696
  return release;
24130
24697
  } finally {
24131
24698
  stopListeningForCancellation();
@@ -24237,7 +24804,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
24237
24804
  checks.push({
24238
24805
  id: "sync.storage-schema",
24239
24806
  label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
24240
- path: join15(projectRoot, "package.json"),
24807
+ path: join16(projectRoot, "package.json"),
24241
24808
  platform: "host",
24242
24809
  status: "pass"
24243
24810
  });
@@ -24245,7 +24812,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
24245
24812
  checks.push({
24246
24813
  id: "sync.storage-schema",
24247
24814
  label: "Offline schema metadata is invalid",
24248
- path: join15(projectRoot, "package.json"),
24815
+ path: join16(projectRoot, "package.json"),
24249
24816
  platform: "host",
24250
24817
  remediation: error instanceof Error ? error.message : String(error),
24251
24818
  status: "fail"
@@ -24312,7 +24879,7 @@ AbsoluteJS can configure ${installPlatform} emulation:`);
24312
24879
  Emulator setup verification:`);
24313
24880
  printDoctorChecks(result.checks.filter((check) => check.platform === "host" || check.platform === installPlatform));
24314
24881
  };
24315
- var captureCommand3 = (command) => {
24882
+ var captureCommand4 = (command) => {
24316
24883
  const result = Bun.spawnSync(command, {
24317
24884
  stderr: "pipe",
24318
24885
  stdin: "ignore",
@@ -24333,7 +24900,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
24333
24900
  }
24334
24901
  return { https: args.includes("--https"), port };
24335
24902
  }
24336
- const instances = listLiveInstances().filter((instance2) => resolve13(instance2.cwd) === resolve13(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24903
+ const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24337
24904
  if (instances.length !== 1) {
24338
24905
  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>.");
24339
24906
  }
@@ -24362,7 +24929,7 @@ var requireAndroidAdb = async () => {
24362
24929
  return adb2;
24363
24930
  };
24364
24931
  var selectAndroidSerial = (adb2, explicitSerial) => {
24365
- const devices = captureCommand3([adb2, "devices"]);
24932
+ const devices = captureCommand4([adb2, "devices"]);
24366
24933
  if (devices.exitCode !== 0) {
24367
24934
  throw new Error(`Could not list Android targets: ${devices.stderr.trim() || devices.stdout.trim()}`);
24368
24935
  }
@@ -24382,7 +24949,7 @@ var selectAndroidSerial = (adb2, explicitSerial) => {
24382
24949
  var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial, json) => {
24383
24950
  if (explicitSerial)
24384
24951
  return selectAndroidSerial(adb2, explicitSerial);
24385
- const current = captureCommand3([adb2, "devices"]);
24952
+ const current = captureCommand4([adb2, "devices"]);
24386
24953
  const ready = current.exitCode === 0 ? parseAdbDevices(current.stdout).find((serial) => serial.startsWith("emulator-")) : undefined;
24387
24954
  if (ready)
24388
24955
  return ready;
@@ -24401,11 +24968,11 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
24401
24968
  launched.unref();
24402
24969
  const startedAt = performance.now();
24403
24970
  const bootedSerial = () => {
24404
- const devices = captureCommand3([adb2, "devices"]);
24971
+ const devices = captureCommand4([adb2, "devices"]);
24405
24972
  const serial = devices.exitCode === 0 ? parseAdbDevices(devices.stdout).find((candidate) => candidate.startsWith("emulator-")) : undefined;
24406
24973
  if (!serial)
24407
24974
  return;
24408
- const booted = captureCommand3([
24975
+ const booted = captureCommand4([
24409
24976
  adb2,
24410
24977
  "-s",
24411
24978
  serial,
@@ -24427,8 +24994,8 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
24427
24994
  return waitForBoot();
24428
24995
  };
24429
24996
  var safeArtifactRoot = (projectRoot, value) => {
24430
- const root = resolve13(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
24431
- if (root !== projectRoot && !root.startsWith(`${resolve13(projectRoot)}/`)) {
24997
+ const root = resolve14(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
24998
+ if (root !== projectRoot && !root.startsWith(`${resolve14(projectRoot)}/`)) {
24432
24999
  throw new TypeError("mobile test --artifacts must remain inside the project.");
24433
25000
  }
24434
25001
  return root;
@@ -24456,11 +25023,11 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
24456
25023
  });
24457
25024
  };
24458
25025
  var writeAndroidFailureArtifacts = async (options) => {
24459
- await mkdir14(options.artifactRoot, { recursive: true });
24460
- const screenshot = options.session ? await options.session.screenshot(join15(options.artifactRoot, "android-failure.png")).catch(() => {
25026
+ await mkdir15(options.artifactRoot, { recursive: true });
25027
+ const screenshot = options.session ? await options.session.screenshot(join16(options.artifactRoot, "android-failure.png")).catch(() => {
24461
25028
  return;
24462
25029
  }) : undefined;
24463
- const diagnosticPath = join15(options.artifactRoot, "android-failure.json");
25030
+ const diagnosticPath = join16(options.artifactRoot, "android-failure.json");
24464
25031
  await writeFile16(diagnosticPath, `${JSON.stringify({
24465
25032
  diagnostics: options.session?.diagnostics ?? [],
24466
25033
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -24536,7 +25103,7 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
24536
25103
  release,
24537
25104
  serial
24538
25105
  });
24539
- const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join15(reportRoot, "android-release.png")) : undefined;
25106
+ const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join16(reportRoot, "android-release.png")) : undefined;
24540
25107
  sendTelemetryEvent("mobile:android-release-conformance", {
24541
25108
  durationMs: result.durationMs,
24542
25109
  engine: result.engine,
@@ -24583,8 +25150,8 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
24583
25150
  platform: "android",
24584
25151
  success: false
24585
25152
  });
24586
- await mkdir14(artifactRoot, { recursive: true });
24587
- const diagnosticPath = join15(artifactRoot, "android-release-failure.json");
25153
+ await mkdir15(artifactRoot, { recursive: true });
25154
+ const diagnosticPath = join16(artifactRoot, "android-release-failure.json");
24588
25155
  await writeFile16(diagnosticPath, `${JSON.stringify({
24589
25156
  error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
24590
25157
  platform: "android",
@@ -24679,7 +25246,7 @@ var testAndroid = async (args) => {
24679
25246
  console.log(JSON.stringify(report, null, 2));
24680
25247
  else
24681
25248
  printAndroidTestReport(report);
24682
- const screenshot = reportRoot ? await session.screenshot(join15(artifactRoot, "android-emulator.png")) : undefined;
25249
+ const screenshot = reportRoot ? await session.screenshot(join16(artifactRoot, "android-emulator.png")) : undefined;
24683
25250
  await writeRequestedAndroidReport({
24684
25251
  adb: adb2,
24685
25252
  args,
@@ -24748,14 +25315,14 @@ var requireIosTestContext = (args, projectRoot) => {
24748
25315
  const port = Number(explicit);
24749
25316
  if (!Number.isInteger(port) || port < 1 || port > 65535)
24750
25317
  throw new TypeError("mobile test --port must be a valid TCP port.");
24751
- const instance2 = listLiveInstances().find((candidate) => resolve13(candidate.cwd) === resolve13(projectRoot) && candidate.source === "dev" && candidate.port === port);
25318
+ const instance2 = listLiveInstances().find((candidate) => resolve14(candidate.cwd) === resolve14(projectRoot) && candidate.source === "dev" && candidate.port === port);
24752
25319
  return {
24753
25320
  https: instance2?.https ?? args.includes("--https"),
24754
25321
  instance: instance2,
24755
25322
  port
24756
25323
  };
24757
25324
  }
24758
- const instances = listLiveInstances().filter((instance2) => resolve13(instance2.cwd) === resolve13(projectRoot) && instance2.source === "dev" && instance2.port !== null);
25325
+ const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24759
25326
  if (instances.length !== 1)
24760
25327
  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>.");
24761
25328
  const [instance] = instances;
@@ -24845,11 +25412,11 @@ var physicalIosCapture = async (options) => {
24845
25412
  remote: false,
24846
25413
  xcodeVersion: requireCapturedCommand(["/usr/bin/xcodebuild", "-version"], "Xcode version inspection").stdout.trim(),
24847
25414
  xcrun: "/usr/bin/xcrun",
24848
- capture: async (command) => captureCommand3(command)
25415
+ capture: async (command) => captureCommand4(command)
24849
25416
  };
24850
25417
  };
24851
25418
  var selectIosSimulator = (xcrun, explicitUdid) => {
24852
- const result = captureCommand3([
25419
+ const result = captureCommand4([
24853
25420
  xcrun,
24854
25421
  "simctl",
24855
25422
  "list",
@@ -24871,16 +25438,37 @@ var selectIosSimulator = (xcrun, explicitUdid) => {
24871
25438
  throw new TypeError("No ready AbsoluteJS iOS simulator was found. Start `bun dev` and wait for the iOS target to become ready.");
24872
25439
  return selected;
24873
25440
  };
25441
+ var selectOrStartIosReleaseSimulator = async (xcrun, explicitUdid) => {
25442
+ const result = captureCommand4([
25443
+ xcrun,
25444
+ "simctl",
25445
+ "list",
25446
+ "devices",
25447
+ "available",
25448
+ "-j"
25449
+ ]);
25450
+ if (result.exitCode !== 0)
25451
+ throw new Error(`Could not list iOS simulators: ${result.stderr.trim() || result.stdout.trim()}`);
25452
+ const devices = parseIosSimulators(result.stdout).filter((device) => device.isAvailable);
25453
+ const selected = explicitUdid ? devices.find((device) => device.udid === explicitUdid) : devices.find((device) => device.name === ABSOLUTE_IOS_SIMULATOR_NAME);
25454
+ if (!selected)
25455
+ throw new TypeError(explicitUdid ? `iOS simulator ${explicitUdid} is unavailable.` : "No managed AbsoluteJS iOS simulator was found. Run `absolute mobile doctor ios --fix`.");
25456
+ if (selected.state !== "Booted") {
25457
+ requireCapturedCommand([xcrun, "simctl", "boot", selected.udid], "iOS Simulator boot");
25458
+ requireCapturedCommand([xcrun, "simctl", "bootstatus", selected.udid, "-b"], "iOS Simulator readiness");
25459
+ }
25460
+ return { ...selected, state: "Booted" };
25461
+ };
24874
25462
  var requireCapturedCommand = (command, label) => {
24875
- const result = captureCommand3(command);
25463
+ const result = captureCommand4(command);
24876
25464
  if (result.exitCode !== 0)
24877
25465
  throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
24878
25466
  return result;
24879
25467
  };
24880
25468
  var writeIosFailureArtifacts = async (options) => {
24881
- await mkdir14(options.artifactRoot, { recursive: true });
24882
- const screenshot = join15(options.artifactRoot, "ios-failure.png");
24883
- const screenshotResult = captureCommand3([
25469
+ await mkdir15(options.artifactRoot, { recursive: true });
25470
+ const screenshot = join16(options.artifactRoot, "ios-failure.png");
25471
+ const screenshotResult = captureCommand4([
24884
25472
  options.xcrun,
24885
25473
  "simctl",
24886
25474
  "io",
@@ -24888,7 +25476,7 @@ var writeIosFailureArtifacts = async (options) => {
24888
25476
  "screenshot",
24889
25477
  screenshot
24890
25478
  ]);
24891
- const diagnosticPath = join15(options.artifactRoot, "ios-failure.json");
25479
+ const diagnosticPath = join16(options.artifactRoot, "ios-failure.json");
24892
25480
  await writeFile16(diagnosticPath, `${JSON.stringify({
24893
25481
  appId: options.appId,
24894
25482
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -24916,8 +25504,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
24916
25504
  var absolutejsVersionForReport = async () => {
24917
25505
  let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
24918
25506
  const versions = await Promise.all([
24919
- resolve13(import.meta.dir, "..", "..", "package.json"),
24920
- resolve13(import.meta.dir, "..", "..", "..", "package.json")
25507
+ resolve14(import.meta.dir, "..", "..", "package.json"),
25508
+ resolve14(import.meta.dir, "..", "..", "..", "package.json")
24921
25509
  ].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
24922
25510
  for (const version of versions) {
24923
25511
  if (version === "unknown")
@@ -24964,7 +25552,11 @@ var writeRequestedIosReport = async (options) => {
24964
25552
  const metadata = options.metadata ?? (options.xcrun ? await iosReportMetadata(options.xcrun) : undefined);
24965
25553
  if (!metadata)
24966
25554
  throw new Error("iOS report metadata could not be inspected.");
24967
- const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({ ...metadata, run: options.run }));
25555
+ const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({
25556
+ ...metadata,
25557
+ ...options.provider ? { provider: options.provider } : {},
25558
+ run: options.run
25559
+ }));
24968
25560
  const print = options.args.includes("--json") ? console.error : console.log;
24969
25561
  print(`iOS partner report: ${paths.markdownPath}`);
24970
25562
  print(`Return this report directory: ${paths.directory}`);
@@ -25081,8 +25673,251 @@ var testPhysicalIos = async (options) => {
25081
25673
  throw error;
25082
25674
  }
25083
25675
  };
25676
+ var printIosReleaseAcceptance = (result, json) => {
25677
+ if (json) {
25678
+ console.log(JSON.stringify(result, null, 2));
25679
+ return;
25680
+ }
25681
+ console.log(`\u2713 ${result.artifactExactness} ${result.engine} iOS release evidence passed via ${result.distribution}.`);
25682
+ console.log(`\u2713 Embedded local content launched in ${getDurationString(result.launchMs)} and relaunched in ${getDurationString(result.relaunchMs)}.`);
25683
+ 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.");
25684
+ };
25685
+ var iosReleaseRun = (result, appId, targetId, remote) => ({
25686
+ appId,
25687
+ durationMs: result.durationMs,
25688
+ hmrConnected: false,
25689
+ iosRelease: {
25690
+ artifactBytes: result.artifactBytes,
25691
+ artifactExactness: result.artifactExactness,
25692
+ artifactSha256: result.artifactSha256,
25693
+ distribution: result.distribution,
25694
+ embeddedLocal: result.embeddedLocal,
25695
+ engine: result.engine,
25696
+ installMs: result.installMs,
25697
+ launchMs: result.launchMs,
25698
+ networkUnavailable: result.networkUnavailable,
25699
+ relaunchMs: result.relaunchMs,
25700
+ releaseId: result.releaseId,
25701
+ remote,
25702
+ signed: result.signed
25703
+ },
25704
+ status: "pass",
25705
+ targetId,
25706
+ targetKind: result.target
25707
+ });
25708
+ var runIosPhysicalReleaseTarget = async (options) => {
25709
+ 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?");
25710
+ if (!confirmed)
25711
+ throw new TypeError("Physical iOS offline acceptance was cancelled because network-unavailable state was not confirmed.");
25712
+ const targetId = normalizeAbsoluteIosDeviceIdentifier(options.device);
25713
+ const result = await runAbsoluteIosDeviceReleaseAcceptance({
25714
+ device: targetId,
25715
+ distribution: options.args.includes("--testflight") ? "testflight" : "registered-device",
25716
+ networkUnavailableConfirmed: true,
25717
+ release: options.release,
25718
+ xcrun: options.xcrun
25719
+ });
25720
+ return { result, targetId };
25721
+ };
25722
+ var runIosSimulatorReleaseTarget = async (options) => {
25723
+ const simulator = await selectOrStartIosReleaseSimulator(options.xcrun, valueAfter(options.args, "--udid") ?? valueAfter(options.args, "--serial"));
25724
+ const xcodebuild = requireCapturedCommand([options.xcrun, "--find", "xcodebuild"], "Xcode build-tool inspection").stdout.trim();
25725
+ const result = await runAbsoluteIosSimulatorReleaseAcceptance({
25726
+ artifactDirectory: options.artifactRoot,
25727
+ config: options.mobile,
25728
+ release: options.release,
25729
+ udid: simulator.udid,
25730
+ xcodebuild,
25731
+ xcrun: options.xcrun
25732
+ });
25733
+ return { result, targetId: simulator.udid };
25734
+ };
25735
+ var runRemoteIosReleaseTarget = async (options) => {
25736
+ const physical = options.requestedDevice !== undefined;
25737
+ const confirmed = !physical || 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 on the Remote Mac?");
25738
+ if (!confirmed)
25739
+ throw new TypeError("Physical iOS offline acceptance was cancelled because network-unavailable state was not confirmed.");
25740
+ let distribution = "simulator-release";
25741
+ if (options.requestedDevice)
25742
+ distribution = "registered-device";
25743
+ if (options.args.includes("--testflight"))
25744
+ distribution = "testflight";
25745
+ const simulatorUdid = valueAfter(options.args, "--udid") ?? valueAfter(options.args, "--serial");
25746
+ const remoteAcceptance = await runAbsoluteRemoteIosReleaseAcceptance({
25747
+ ...options.requestedDevice ? {
25748
+ deviceIdentifier: normalizeAbsoluteIosDeviceIdentifier(options.requestedDevice),
25749
+ networkUnavailableConfirmed: true
25750
+ } : {},
25751
+ distribution,
25752
+ project: createAbsoluteRemoteIosDevProject(options.mobile, options.projectRoot, options.profile),
25753
+ release: options.release,
25754
+ signal: options.cancellation?.signal,
25755
+ log: (message) => console.log(`[remote] ${message}`),
25756
+ onPhaseTiming: ({ durationMs, phase }) => {
25757
+ console.log(`[mobile:ios-release-test] ${phase} ${getDurationString(durationMs)}`);
25758
+ sendTelemetryEvent("mobile:ios-release-test-phase", {
25759
+ durationMs: Math.round(durationMs),
25760
+ engine: options.mobile.engine,
25761
+ phase,
25762
+ platform: "ios",
25763
+ provider: "remote-mac"
25764
+ });
25765
+ },
25766
+ ...simulatorUdid ? { simulatorUdid } : {}
25767
+ });
25768
+ return {
25769
+ result: remoteAcceptance.result,
25770
+ targetId: remoteAcceptance.targetId
25771
+ };
25772
+ };
25773
+ var testIosRelease = async (args, mobile, projectRoot) => {
25774
+ const requested = valueAfter(args, "--release");
25775
+ if (!requested || requested.startsWith("--"))
25776
+ throw new TypeError("mobile test ios --release requires a release directory or release.json path.");
25777
+ if (!mobile.platforms.includes("ios"))
25778
+ throw new TypeError("mobile test ios requires ios in mobile.platforms.");
25779
+ const requestedRemote = valueAfter(args, "--remote");
25780
+ if (args.includes("--remote") && (!requestedRemote || requestedRemote.startsWith("-")))
25781
+ throw new TypeError("mobile test ios --release requires --remote <name>.");
25782
+ const remoteProfile = requestedRemote !== undefined || process.platform !== "darwin" ? await getAbsoluteRemoteMacProfile(requestedRemote, remoteProfilePath()) : undefined;
25783
+ if (process.platform !== "darwin" && !remoteProfile)
25784
+ throw new TypeError("iOS release acceptance requires macOS or a paired Remote Mac. Run `absolute mobile pair mac <name> <user@host>`.");
25785
+ const release = await readAbsoluteIosRelease(projectRoot, requested);
25786
+ if (release.metadata.appId !== mobile.appId)
25787
+ throw new TypeError(`iOS release app ID ${release.metadata.appId} does not match mobile.appId ${mobile.appId}.`);
25788
+ if (release.metadata.engine !== mobile.engine)
25789
+ throw new TypeError(`iOS release engine ${release.metadata.engine} does not match configured engine ${mobile.engine}.`);
25790
+ if (mobile.engine === "expo" && mobile.expoNativeRoutes[mobile.entry])
25791
+ 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.");
25792
+ const requestedDevice = valueAfter(args, "--device");
25793
+ if (args.includes("--device") && !requestedDevice)
25794
+ throw new TypeError("mobile test ios --release --device requires a device identifier or name.");
25795
+ if (args.includes("--testflight") && !requestedDevice)
25796
+ throw new TypeError("mobile test ios --release --testflight requires --device.");
25797
+ if (requestedDevice && (valueAfter(args, "--udid") || valueAfter(args, "--serial")))
25798
+ throw new TypeError("mobile test ios --release --device cannot be combined with a simulator selector.");
25799
+ const xcrun = remoteProfile ? undefined : await requireIosXcrun();
25800
+ const reportRoot = nativeReportRoot(args, projectRoot, "ios");
25801
+ const artifactRoot = reportRoot ?? safeArtifactRoot(projectRoot, valueAfter(args, "--artifacts") ?? `.absolutejs/mobile/test-artifacts/${release.metadata.releaseId}`);
25802
+ const startedAt = performance.now();
25803
+ let targetId = requestedDevice ?? "ios-simulator";
25804
+ let remoteReportMetadata;
25805
+ if (remoteProfile && reportRoot) {
25806
+ const macos = await captureAbsoluteRemoteMacCommand(remoteProfile, [
25807
+ "/usr/bin/sw_vers",
25808
+ "-productVersion"
25809
+ ]);
25810
+ if (macos.exitCode !== 0)
25811
+ throw new Error("Remote Mac version inspection failed.");
25812
+ remoteReportMetadata = {
25813
+ absolutejsVersion: await absolutejsVersionForReport(),
25814
+ bunVersion: Bun.version,
25815
+ macosVersion: macos.stdout.trim(),
25816
+ xcodeVersion: remoteProfile.xcodeVersion
25817
+ };
25818
+ }
25819
+ const cancellation = remoteProfile ? new AbortController : undefined;
25820
+ const stopListeningForCancellation = remoteProfile ? listenForRemoteReleaseCancellation(cancellation) : () => {
25821
+ return;
25822
+ };
25823
+ try {
25824
+ let acceptance;
25825
+ if (remoteProfile)
25826
+ acceptance = await runRemoteIosReleaseTarget({
25827
+ args,
25828
+ ...cancellation ? { cancellation } : {},
25829
+ mobile,
25830
+ profile: remoteProfile,
25831
+ projectRoot,
25832
+ release,
25833
+ ...requestedDevice ? { requestedDevice } : {}
25834
+ });
25835
+ else if (!xcrun)
25836
+ throw new Error("Local iOS release acceptance requires xcrun.");
25837
+ else if (requestedDevice)
25838
+ acceptance = await runIosPhysicalReleaseTarget({
25839
+ args,
25840
+ device: requestedDevice,
25841
+ release,
25842
+ xcrun
25843
+ });
25844
+ else
25845
+ acceptance = await runIosSimulatorReleaseTarget({
25846
+ args,
25847
+ artifactRoot,
25848
+ mobile,
25849
+ release,
25850
+ xcrun
25851
+ });
25852
+ const { result, targetId: acceptedTargetId } = acceptance;
25853
+ targetId = acceptedTargetId;
25854
+ sendTelemetryEvent("mobile:ios-release-conformance", {
25855
+ artifactExactness: result.artifactExactness,
25856
+ distribution: result.distribution,
25857
+ durationMs: result.durationMs,
25858
+ engine: result.engine,
25859
+ installMs: result.installMs,
25860
+ launchMs: result.launchMs,
25861
+ platform: "ios",
25862
+ relaunchMs: result.relaunchMs,
25863
+ remote: remoteProfile !== undefined,
25864
+ success: true
25865
+ });
25866
+ await writeRequestedIosReport({
25867
+ args,
25868
+ ...remoteReportMetadata ? { metadata: remoteReportMetadata } : {},
25869
+ projectRoot,
25870
+ provider: mobile.engine,
25871
+ run: iosReleaseRun(result, mobile.appId, targetId, remoteProfile !== undefined),
25872
+ ...xcrun ? { xcrun } : {}
25873
+ });
25874
+ printIosReleaseAcceptance(result, args.includes("--json"));
25875
+ return result;
25876
+ } catch (error) {
25877
+ const durationMs = Math.round(performance.now() - startedAt);
25878
+ sendTelemetryEvent("mobile:ios-release-conformance", {
25879
+ durationMs,
25880
+ engine: mobile.engine,
25881
+ platform: "ios",
25882
+ remote: remoteProfile !== undefined,
25883
+ success: false
25884
+ });
25885
+ await mkdir15(artifactRoot, { recursive: true });
25886
+ const diagnosticPath = join16(artifactRoot, "ios-release-failure.json");
25887
+ await writeFile16(diagnosticPath, `${JSON.stringify({
25888
+ error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
25889
+ platform: "ios",
25890
+ provider: mobile.engine,
25891
+ releaseId: release.metadata.releaseId,
25892
+ status: "fail"
25893
+ }, null, 2)}
25894
+ `);
25895
+ if (reportRoot)
25896
+ await writeRequestedIosReport({
25897
+ args,
25898
+ ...remoteReportMetadata ? { metadata: remoteReportMetadata } : {},
25899
+ projectRoot,
25900
+ provider: mobile.engine,
25901
+ run: {
25902
+ appId: mobile.appId,
25903
+ durationMs,
25904
+ error: "Installed iOS release acceptance failed; inspect the local diagnostic artifact before sharing evidence.",
25905
+ hmrConnected: false,
25906
+ status: "fail",
25907
+ targetId,
25908
+ targetKind: requestedDevice ? "device" : "simulator"
25909
+ },
25910
+ ...xcrun ? { xcrun } : {}
25911
+ });
25912
+ throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}`, { cause: error });
25913
+ } finally {
25914
+ stopListeningForCancellation();
25915
+ }
25916
+ };
25084
25917
  var testIos = async (args) => {
25085
25918
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
25919
+ if (args.includes("--release"))
25920
+ return testIosRelease(args, mobile, projectRoot);
25086
25921
  requireCapacitorEngine(mobile, "mobile test ios");
25087
25922
  const { https, instance, port } = requireIosTestContext(args, projectRoot);
25088
25923
  if (!mobile.platforms.includes("ios"))
@@ -25142,8 +25977,8 @@ var testIos = async (args) => {
25142
25977
  mobile.appId
25143
25978
  ], "iOS app launch");
25144
25979
  await waitForIosHmrClient({ https, port, timeoutMs });
25145
- await mkdir14(artifactRoot, { recursive: true });
25146
- const screenshot = join15(artifactRoot, "ios-simulator.png");
25980
+ await mkdir15(artifactRoot, { recursive: true });
25981
+ const screenshot = join16(artifactRoot, "ios-simulator.png");
25147
25982
  requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
25148
25983
  const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
25149
25984
  const report = {
@@ -25329,7 +26164,7 @@ var runMobile = async (args) => {
25329
26164
  await publishIos(args.slice(2));
25330
26165
  return;
25331
26166
  }
25332
- 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]");
26167
+ 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 [--remote name] [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
25333
26168
  };
25334
26169
  export {
25335
26170
  runMobile