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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/changelog.json +22 -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-9cp5t2n1.js} +4 -4
  7. package/dist/cli/{expoProject-780thyfd.js → expoProject-zkprmjjz.js} +1 -1
  8. package/dist/cli/{index-d5fr1xp3.js → index-0zcebhm4.js} +59 -1
  9. package/dist/cli/{index-wpfvpsk7.js → index-5skbydh0.js} +1 -1
  10. package/dist/cli/{index-3sdvzrv2.js → index-7fxrgx5q.js} +1 -1
  11. package/dist/cli/{index-67kedtn7.js → index-hrzd7e5x.js} +1 -1
  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-fbtq3s5x.js} +896 -172
  18. package/dist/cli/{remoteMacProtocol-kqvkp0qd.js → remoteMacProtocol-bbramzh6.js} +2 -2
  19. package/dist/cli/{start-39aqmr5n.js → start-5grq7nfr.js} +3 -3
  20. package/dist/mobile/index.js +1191 -573
  21. package/dist/mobile/index.js.map +8 -7
  22. package/dist/mobile/remoteMacAgentEntry.js +296 -249
  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 +63 -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 +15 -0
  29. package/dist/src/mobile/remoteMacProtocol.d.ts +7 -1
  30. package/dist/src/mobile/shellReleaseReadiness.d.ts +1 -1
  31. 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 {
@@ -30,15 +30,17 @@ import {
30
30
  listAbsoluteRemoteMacProfiles,
31
31
  pairAbsoluteRemoteMac,
32
32
  removeAbsoluteRemoteMacProfile
33
- } from "./index-3sdvzrv2.js";
33
+ } from "./index-7fxrgx5q.js";
34
34
  import {
35
35
  ABSOLUTE_IOS_SIMULATOR_NAME,
36
36
  parseIosSimulators,
37
37
  repairAbsoluteIosDevSession
38
- } from "./index-wpfvpsk7.js";
38
+ } from "./index-5skbydh0.js";
39
39
  import {
40
- buildAbsoluteIosRelease
41
- } from "./index-qxnptvwn.js";
40
+ buildAbsoluteIosRelease,
41
+ requireAbsoluteIosReleaseMetadata,
42
+ resolveAbsoluteIosXcodeProject
43
+ } from "./index-q1d4ns4n.js";
42
44
  import"./index-fc9vxw56.js";
43
45
  import {
44
46
  listLiveInstances
@@ -67,7 +69,7 @@ import {
67
69
  syncAbsoluteExpoWebAssets,
68
70
  unsignedAbsoluteMobileUpdate,
69
71
  writeAbsoluteExpoProject
70
- } from "./index-d5fr1xp3.js";
72
+ } from "./index-0zcebhm4.js";
71
73
  import {
72
74
  buildAbsoluteExpoAndroidWslBundle
73
75
  } from "./index-28nxv57v.js";
@@ -4412,9 +4414,9 @@ var require_sha256 = __commonJS(function(exports, module) {
4412
4414
  var forge = require_forge();
4413
4415
  require_md();
4414
4416
  require_util();
4415
- var sha2563 = module.exports = forge.sha256 = forge.sha256 || {};
4416
- forge.md.sha256 = forge.md.algorithms.sha256 = sha2563;
4417
- sha2563.create = function() {
4417
+ var sha2564 = module.exports = forge.sha256 = forge.sha256 || {};
4418
+ forge.md.sha256 = forge.md.algorithms.sha256 = sha2564;
4419
+ sha2564.create = function() {
4418
4420
  if (!_initialized) {
4419
4421
  _init();
4420
4422
  }
@@ -16776,15 +16778,15 @@ var require_main = __commonJS(function(exports) {
16776
16778
 
16777
16779
  // src/cli/scripts/mobile.ts
16778
16780
  import {
16779
- access as access13,
16780
- mkdir as mkdir14,
16781
- mkdtemp as mkdtemp4,
16782
- readFile as readFile18,
16783
- rm as rm7,
16781
+ access as access14,
16782
+ mkdir as mkdir15,
16783
+ mkdtemp as mkdtemp5,
16784
+ readFile as readFile19,
16785
+ rm as rm8,
16784
16786
  writeFile as writeFile16
16785
16787
  } from "fs/promises";
16786
16788
  import { createPublicKey as createPublicKey2 } from "crypto";
16787
- import { join as join15, relative as relative12, resolve as resolve13 } from "path";
16789
+ import { join as join16, relative as relative13, resolve as resolve14 } from "path";
16788
16790
  import { createInterface } from "readline/promises";
16789
16791
 
16790
16792
  // src/mobile/nativeDeepLinks.ts
@@ -18459,6 +18461,8 @@ import { mkdir as mkdir3, readFile as readFile6, readdir as readdir3, rename as
18459
18461
  import { dirname as dirname3, join as join6 } from "path";
18460
18462
  var START3 = "// absolutejs:release-readiness:start";
18461
18463
  var END3 = "// absolutejs:release-readiness:end";
18464
+ var IOS_PLUGIN_START = "// absolutejs:release-readiness-plugin:start";
18465
+ var IOS_PLUGIN_END = "// absolutejs:release-readiness-plugin:end";
18462
18466
  var ANDROID_PLUGIN3 = "AbsoluteReleaseReadinessPlugin.java";
18463
18467
  var optionalSource4 = async (path) => {
18464
18468
  try {
@@ -18580,9 +18584,70 @@ public final class AbsoluteReleaseReadinessPlugin extends Plugin {
18580
18584
  }
18581
18585
  }
18582
18586
  `;
18583
- var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.platforms) => {
18584
- if (config.engine !== "capacitor" || !platforms.includes("android"))
18585
- return { changed: false };
18587
+ var iosPluginRegion3 = `${IOS_PLUGIN_START}
18588
+ @objc(AbsoluteReleaseReadinessPlugin)
18589
+ public final class AbsoluteReleaseReadinessPlugin: CAPPlugin, CAPBridgedPlugin {
18590
+ public let identifier = "AbsoluteReleaseReadinessPlugin"
18591
+ public let jsName = "AbsoluteReleaseReadiness"
18592
+ public let pluginMethods: [CAPPluginMethod] = [
18593
+ CAPPluginMethod(name: "ready", returnType: CAPPluginReturnPromise)
18594
+ ]
18595
+
18596
+ @objc public func ready(_ call: CAPPluginCall) {
18597
+ let info = Bundle.main.infoDictionary ?? [:]
18598
+ let version = info["CFBundleShortVersionString"] as? String ?? "unknown"
18599
+ let build = info["CFBundleVersion"] as? String ?? "unknown"
18600
+ NSLog("AbsoluteJS: Capacitor embedded web content ready; version=%@; build=%@", version, build)
18601
+ call.resolve(["ready": true])
18602
+ }
18603
+ }
18604
+ ${IOS_PLUGIN_END}
18605
+ `;
18606
+ var replaceIosPluginRegion = (appDelegate, existingStart, existingEnd) => {
18607
+ const from = appDelegate.lastIndexOf(`
18608
+ `, existingStart) + 1;
18609
+ const newline = appDelegate.indexOf(`
18610
+ `, existingEnd + IOS_PLUGIN_END.length);
18611
+ const through = newline < 0 ? appDelegate.length : newline + 1;
18612
+ return `${appDelegate.slice(0, from)}${iosPluginRegion3}${appDelegate.slice(through)}`;
18613
+ };
18614
+ var configureIos5 = async (config) => {
18615
+ const appDelegatePath = join6(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
18616
+ const capacitorConfigPath = join6(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
18617
+ const appDelegate = await readFile6(appDelegatePath, "utf8");
18618
+ const existingStart = appDelegate.indexOf(IOS_PLUGIN_START);
18619
+ const existingEnd = appDelegate.indexOf(IOS_PLUGIN_END);
18620
+ if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
18621
+ throw new TypeError("AbsoluteJS iOS release-readiness markers are malformed.");
18622
+ const source = existingStart < 0 ? `${appDelegate.trimEnd()}
18623
+
18624
+ ${iosPluginRegion3}` : replaceIosPluginRegion(appDelegate, existingStart, existingEnd);
18625
+ let capacitorConfig;
18626
+ try {
18627
+ capacitorConfig = JSON.parse(await readFile6(capacitorConfigPath, "utf8"));
18628
+ } catch {
18629
+ throw new TypeError("iOS capacitor.config.json is invalid; run Capacitor sync before projecting release readiness.");
18630
+ }
18631
+ if (typeof capacitorConfig !== "object" || capacitorConfig === null)
18632
+ throw new TypeError("iOS capacitor.config.json must contain an object.");
18633
+ const packageClassList = Reflect.get(capacitorConfig, "packageClassList");
18634
+ if (!Array.isArray(packageClassList))
18635
+ throw new TypeError("iOS capacitor.config.json has no packageClassList; run Capacitor sync before projecting release readiness.");
18636
+ const classes = packageClassList.filter((value) => typeof value === "string");
18637
+ const nextConfig = `${JSON.stringify({
18638
+ ...capacitorConfig,
18639
+ packageClassList: [
18640
+ ...new Set([...classes, "AbsoluteReleaseReadinessPlugin"])
18641
+ ]
18642
+ }, null, "\t")}
18643
+ `;
18644
+ const changed = await Promise.all([
18645
+ writeChanged4(appDelegatePath, source),
18646
+ writeChanged4(capacitorConfigPath, nextConfig)
18647
+ ]);
18648
+ return changed.some(Boolean);
18649
+ };
18650
+ var configureAndroid5 = async (config) => {
18586
18651
  const mainActivityPath = await activityPath(config);
18587
18652
  const activity = await readFile6(mainActivityPath, "utf8");
18588
18653
  const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
@@ -18593,6 +18658,15 @@ var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.plat
18593
18658
  writeChanged4(mainActivityPath, injectRegistration(activity)),
18594
18659
  writeChanged4(pluginPath, pluginSource(packageName))
18595
18660
  ]);
18661
+ return changed.some(Boolean);
18662
+ };
18663
+ var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.platforms) => {
18664
+ if (config.engine !== "capacitor")
18665
+ return { changed: false };
18666
+ const changed = await Promise.all([
18667
+ platforms.includes("android") ? configureAndroid5(config) : false,
18668
+ platforms.includes("ios") ? configureIos5(config) : false
18669
+ ]);
18596
18670
  return { changed: changed.some(Boolean) };
18597
18671
  };
18598
18672
 
@@ -21083,6 +21157,452 @@ var runAbsoluteAndroidReleaseAcceptance = async (options) => {
21083
21157
  };
21084
21158
  };
21085
21159
 
21160
+ // src/mobile/iosReleaseAcceptance.ts
21161
+ import { createHash as createHash4 } from "crypto";
21162
+ import {
21163
+ access as access7,
21164
+ mkdir as mkdir9,
21165
+ mkdtemp as mkdtemp3,
21166
+ readFile as readFile12,
21167
+ readdir as readdir5,
21168
+ rm as rm6,
21169
+ stat as stat4
21170
+ } from "fs/promises";
21171
+ import {
21172
+ basename as basename2,
21173
+ dirname as dirname9,
21174
+ isAbsolute as isAbsolute4,
21175
+ join as join11,
21176
+ relative as relative6,
21177
+ resolve as resolve7,
21178
+ sep as sep4
21179
+ } from "path";
21180
+
21181
+ // src/mobile/iosUpgradeConformance.ts
21182
+ var captureCommand3 = async (command) => {
21183
+ const process2 = Bun.spawn(command, { stderr: "pipe", stdout: "pipe" });
21184
+ const [exitCode, stderr, stdout] = await Promise.all([
21185
+ process2.exited,
21186
+ new Response(process2.stderr).text(),
21187
+ new Response(process2.stdout).text()
21188
+ ]);
21189
+ return { exitCode, stderr, stdout };
21190
+ };
21191
+ var requiredOutput = (result, message) => {
21192
+ const value = result.stdout.trim();
21193
+ if (result.exitCode !== 0 || !value)
21194
+ throw new Error(`${message}: ${result.stderr.trim() || value || "no output"}`);
21195
+ return value;
21196
+ };
21197
+ var inspectAbsoluteIosInstalledApp = async (xcrun, udid, appId, run = captureCommand3, plutil = "/usr/bin/plutil") => {
21198
+ const dataContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "data"]), `Could not inspect the installed iOS data container for ${appId}`);
21199
+ const appContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "app"]), `Could not inspect the installed iOS application container for ${appId}`);
21200
+ const info = requiredOutput(await run([
21201
+ plutil,
21202
+ "-convert",
21203
+ "json",
21204
+ "-o",
21205
+ "-",
21206
+ `${appContainer}/Info.plist`
21207
+ ]), `Could not inspect the installed iOS Info.plist for ${appId}`);
21208
+ return parseAbsoluteIosInstalledAppInfo(appId, appContainer, dataContainer, info);
21209
+ };
21210
+ var parseAbsoluteIosInstalledAppInfo = (appId, appContainer, dataContainer, output) => {
21211
+ let value;
21212
+ try {
21213
+ value = JSON.parse(output);
21214
+ } catch {
21215
+ throw new TypeError("Invalid installed iOS application Info.plist JSON.");
21216
+ }
21217
+ if (!value || typeof value !== "object")
21218
+ throw new TypeError("Invalid installed iOS application Info.plist JSON.");
21219
+ const bundleId = Reflect.get(value, "CFBundleIdentifier");
21220
+ if (bundleId !== appId)
21221
+ throw new Error(`Installed iOS application identifier mismatch (${String(bundleId)} != ${appId}).`);
21222
+ const buildNumber = Reflect.get(value, "CFBundleVersion");
21223
+ const version = Reflect.get(value, "CFBundleShortVersionString");
21224
+ return {
21225
+ appContainer,
21226
+ appId,
21227
+ ...typeof buildNumber === "string" || typeof buildNumber === "number" ? { buildNumber: String(buildNumber) } : {},
21228
+ dataContainer,
21229
+ ...typeof version === "string" ? { version } : {}
21230
+ };
21231
+ };
21232
+
21233
+ // src/mobile/iosReleaseAcceptance.ts
21234
+ var READY_MARKERS = {
21235
+ capacitor: "Capacitor embedded web content ready",
21236
+ expo: "Expo embedded web content ready"
21237
+ };
21238
+ var READY_TIMEOUT_MS = 30000;
21239
+ var READY_POLL_MS = 250;
21240
+ var defaultRun2 = async (command, options = {}) => {
21241
+ const process2 = Bun.spawn(command, {
21242
+ cwd: options.cwd,
21243
+ env: options.env,
21244
+ stderr: "pipe",
21245
+ stdin: "ignore",
21246
+ stdout: "pipe"
21247
+ });
21248
+ const [exitCode, stderr, stdout] = await Promise.all([
21249
+ process2.exited,
21250
+ new Response(process2.stderr).text(),
21251
+ new Response(process2.stdout).text()
21252
+ ]);
21253
+ return { exitCode, stderr, stdout };
21254
+ };
21255
+ var exists3 = async (path) => {
21256
+ try {
21257
+ await access7(path);
21258
+ return true;
21259
+ } catch {
21260
+ return false;
21261
+ }
21262
+ };
21263
+ var sha2562 = async (path) => createHash4("sha256").update(await readFile12(path)).digest("hex");
21264
+ var inside2 = (root, candidate) => {
21265
+ const path = relative6(resolve7(root), resolve7(candidate));
21266
+ return path === "" || !isAbsolute4(path) && path !== ".." && !path.startsWith(`..${sep4}`);
21267
+ };
21268
+ var requireSuccess2 = async (run, command, label, options) => {
21269
+ const result = await run(command, options);
21270
+ if (result.exitCode !== 0)
21271
+ throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
21272
+ return result;
21273
+ };
21274
+ var findByExtension = async (root, extension) => {
21275
+ if (!await exists3(root))
21276
+ return;
21277
+ const entries = await readdir5(root, { withFileTypes: true });
21278
+ const matches = await Promise.all(entries.map(async (entry) => {
21279
+ const path = join11(root, entry.name);
21280
+ if (entry.name.endsWith(extension))
21281
+ return path;
21282
+ return entry.isDirectory() ? findByExtension(path, extension) : undefined;
21283
+ }));
21284
+ return matches.find((match) => match !== undefined);
21285
+ };
21286
+ var readAbsoluteIosRelease = async (projectRoot, requested) => {
21287
+ const root = resolve7(projectRoot);
21288
+ const selected = resolve7(root, requested);
21289
+ if (!inside2(root, selected))
21290
+ throw new TypeError("mobile test ios --release must remain inside the project.");
21291
+ const metadataPath = basename2(selected) === "release.json" ? selected : join11(selected, "release.json");
21292
+ const releaseRoot = dirname9(metadataPath);
21293
+ const metadata = requireAbsoluteIosReleaseMetadata(JSON.parse(await readFile12(metadataPath, "utf8")));
21294
+ const artifactPath = resolve7(releaseRoot, metadata.artifact);
21295
+ if (!inside2(releaseRoot, artifactPath))
21296
+ throw new TypeError("iOS release artifact path is invalid.");
21297
+ const [artifactStat, artifactSha256] = await Promise.all([
21298
+ stat4(artifactPath),
21299
+ sha2562(artifactPath)
21300
+ ]);
21301
+ if (artifactStat.size !== metadata.bytes || artifactSha256 !== metadata.sha256)
21302
+ throw new TypeError(`Immutable iOS release ${metadata.releaseId} is missing or modified.`);
21303
+ const registered = metadata.registeredDevice;
21304
+ let registeredArtifactPath;
21305
+ if (registered) {
21306
+ registeredArtifactPath = resolve7(releaseRoot, registered.artifact);
21307
+ if (!inside2(releaseRoot, registeredArtifactPath))
21308
+ throw new TypeError("iOS registered-device artifact path is invalid.");
21309
+ const [registeredStat, registeredSha256] = await Promise.all([
21310
+ stat4(registeredArtifactPath),
21311
+ sha2562(registeredArtifactPath)
21312
+ ]);
21313
+ if (registeredStat.size !== registered.bytes || registeredSha256 !== registered.sha256)
21314
+ throw new TypeError(`Registered-device export for ${metadata.releaseId} is missing or modified.`);
21315
+ }
21316
+ return {
21317
+ artifactPath,
21318
+ metadata,
21319
+ metadataPath,
21320
+ registeredArtifactPath,
21321
+ releaseRoot
21322
+ };
21323
+ };
21324
+ var releaseMarker = (release) => {
21325
+ const marker = READY_MARKERS[release.metadata.engine];
21326
+ return release.metadata.buildNumber === undefined ? marker : `${marker}; version=${release.metadata.marketingVersion}; build=${release.metadata.buildNumber}`;
21327
+ };
21328
+ var waitForSimulatorMarker = async (options) => {
21329
+ const startedAt = performance.now();
21330
+ const poll = async () => {
21331
+ if (performance.now() - startedAt >= READY_TIMEOUT_MS)
21332
+ throw new Error("The installed iOS Release app launched but its embedded content did not become ready.");
21333
+ const logs = await options.run([
21334
+ options.xcrun,
21335
+ "simctl",
21336
+ "spawn",
21337
+ options.udid,
21338
+ "log",
21339
+ "show",
21340
+ "--style",
21341
+ "compact",
21342
+ "--start",
21343
+ options.startedAt.toISOString(),
21344
+ "--predicate",
21345
+ `eventMessage CONTAINS "${releaseMarker(options.release)}"`
21346
+ ]);
21347
+ if (logs.exitCode === 0 && logs.stdout.includes(releaseMarker(options.release)))
21348
+ return;
21349
+ await Bun.sleep(READY_POLL_MS);
21350
+ await poll();
21351
+ };
21352
+ await poll();
21353
+ };
21354
+ var launchSimulator = async (options) => {
21355
+ const startedAt = performance.now();
21356
+ const logStartedAt = new Date;
21357
+ await requireSuccess2(options.run, [
21358
+ options.xcrun,
21359
+ "simctl",
21360
+ "launch",
21361
+ "--terminate-running-process",
21362
+ options.udid,
21363
+ options.release.metadata.appId
21364
+ ], "iOS Simulator Release launch");
21365
+ await waitForSimulatorMarker({ ...options, startedAt: logStartedAt });
21366
+ return Math.round(performance.now() - startedAt);
21367
+ };
21368
+ var runAbsoluteIosSimulatorReleaseAcceptance = async (options) => {
21369
+ const run = options.run ?? defaultRun2;
21370
+ const xcodebuild = options.xcodebuild ?? "/usr/bin/xcodebuild";
21371
+ const xcrun = options.xcrun ?? "/usr/bin/xcrun";
21372
+ const startedAt = performance.now();
21373
+ const iosRoot = join11(options.config.nativeProjectDirectory, "ios");
21374
+ const xcode = await resolveAbsoluteIosXcodeProject(iosRoot);
21375
+ const derivedData = join11(options.artifactDirectory, "DerivedData");
21376
+ await mkdir9(options.artifactDirectory, { recursive: true });
21377
+ await requireSuccess2(run, [
21378
+ xcodebuild,
21379
+ "-workspace",
21380
+ xcode.workspacePath,
21381
+ "-scheme",
21382
+ xcode.scheme,
21383
+ "-configuration",
21384
+ "Release",
21385
+ "-destination",
21386
+ `platform=iOS Simulator,id=${options.udid}`,
21387
+ "-derivedDataPath",
21388
+ derivedData,
21389
+ `MARKETING_VERSION=${options.release.metadata.marketingVersion}`,
21390
+ ...options.release.metadata.buildNumber === undefined ? [] : [
21391
+ `CURRENT_PROJECT_VERSION=${options.release.metadata.buildNumber}`
21392
+ ],
21393
+ "build"
21394
+ ], "iOS Simulator Release build", { cwd: iosRoot });
21395
+ const app = await findByExtension(join11(derivedData, "Build", "Products"), ".app");
21396
+ if (!app)
21397
+ throw new Error("Xcode did not produce a Simulator Release app.");
21398
+ const installStartedAt = performance.now();
21399
+ await requireSuccess2(run, [xcrun, "simctl", "install", options.udid, app], "iOS Simulator Release installation");
21400
+ const installMs = Math.round(performance.now() - installStartedAt);
21401
+ const installed = await inspectAbsoluteIosInstalledApp(xcrun, options.udid, options.release.metadata.appId, (command) => run(command));
21402
+ if (installed.version !== options.release.metadata.marketingVersion)
21403
+ throw new Error(`Installed iOS version ${installed.version ?? "unknown"} does not match release ${options.release.metadata.marketingVersion}.`);
21404
+ if (options.release.metadata.buildNumber !== undefined && installed.buildNumber !== String(options.release.metadata.buildNumber))
21405
+ throw new Error(`Installed iOS build ${installed.buildNumber ?? "unknown"} does not match release ${options.release.metadata.buildNumber}.`);
21406
+ const launchMs = await launchSimulator({
21407
+ release: options.release,
21408
+ run,
21409
+ udid: options.udid,
21410
+ xcrun
21411
+ });
21412
+ await requireSuccess2(run, [
21413
+ xcrun,
21414
+ "simctl",
21415
+ "terminate",
21416
+ options.udid,
21417
+ options.release.metadata.appId
21418
+ ], "iOS Simulator Release termination");
21419
+ const relaunchMs = await launchSimulator({
21420
+ release: options.release,
21421
+ run,
21422
+ udid: options.udid,
21423
+ xcrun
21424
+ });
21425
+ return {
21426
+ artifactBytes: options.release.metadata.bytes,
21427
+ artifactExactness: "source-equivalent",
21428
+ artifactSha256: options.release.metadata.sha256,
21429
+ ...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
21430
+ distribution: "simulator-release",
21431
+ durationMs: Math.round(performance.now() - startedAt),
21432
+ embeddedLocal: true,
21433
+ engine: options.release.metadata.engine,
21434
+ installMs,
21435
+ launchMs,
21436
+ marketingVersion: options.release.metadata.marketingVersion,
21437
+ networkUnavailable: "not-proven",
21438
+ relaunchMs,
21439
+ releaseId: options.release.metadata.releaseId,
21440
+ signed: options.release.metadata.signed,
21441
+ status: "pass",
21442
+ target: "simulator"
21443
+ };
21444
+ };
21445
+ var readUntilMarker = async (reader, marker, output = "") => {
21446
+ const { done, value } = await reader.read();
21447
+ if (done)
21448
+ return { found: false, output };
21449
+ const next = `${output}${new TextDecoder().decode(value)}`;
21450
+ if (next.includes(marker))
21451
+ return { found: true, output: next };
21452
+ return readUntilMarker(reader, marker, next);
21453
+ };
21454
+ var observeDeviceLaunch = async (options) => {
21455
+ const startedAt = performance.now();
21456
+ const process2 = Bun.spawn([
21457
+ options.xcrun,
21458
+ "devicectl",
21459
+ "device",
21460
+ "process",
21461
+ "launch",
21462
+ "--console",
21463
+ "--terminate-existing",
21464
+ "--device",
21465
+ options.device,
21466
+ options.appId
21467
+ ], { stderr: "pipe", stdin: "ignore", stdout: "pipe" });
21468
+ const stdoutReader = process2.stdout.getReader();
21469
+ const stderrReader = process2.stderr.getReader();
21470
+ const requireMarker = async (reader) => {
21471
+ const result = await readUntilMarker(reader, options.marker);
21472
+ if (!result.found)
21473
+ throw new Error(`Console stream closed before readiness.${result.output.trim() ? ` Output: ${result.output.trim()}` : ""}`);
21474
+ return result;
21475
+ };
21476
+ const observed = Promise.any([
21477
+ requireMarker(stdoutReader),
21478
+ requireMarker(stderrReader)
21479
+ ]);
21480
+ try {
21481
+ const result = await Promise.race([
21482
+ observed.then((value) => ({ type: "output", value })).catch(() => ({ type: "closed" })),
21483
+ Bun.sleep(options.timeoutMs ?? READY_TIMEOUT_MS).then(() => ({
21484
+ type: "timeout"
21485
+ }))
21486
+ ]);
21487
+ if (result.type === "output" && result.value.found)
21488
+ return Math.round(performance.now() - startedAt);
21489
+ if (result.type === "closed")
21490
+ throw new Error("The physical iOS app exited before embedded content became ready.");
21491
+ throw new Error("The physical iOS app did not report embedded readiness before the timeout.");
21492
+ } finally {
21493
+ process2.kill();
21494
+ await Promise.all([
21495
+ stdoutReader.cancel().catch(() => {
21496
+ return;
21497
+ }),
21498
+ stderrReader.cancel().catch(() => {
21499
+ return;
21500
+ })
21501
+ ]);
21502
+ stdoutReader.releaseLock();
21503
+ stderrReader.releaseLock();
21504
+ }
21505
+ };
21506
+ var installRegisteredDeviceRelease = async (options) => {
21507
+ if (!options.release.registeredArtifactPath)
21508
+ throw new TypeError("This release has no same-archive registered-device IPA. Rebuild with `absolute mobile build ios --registered-device-artifact`.");
21509
+ const staging = await mkdtemp3(join11(dirname9(options.release.releaseRoot), ".ios-device-"));
21510
+ try {
21511
+ await requireSuccess2(options.run, [
21512
+ "/usr/bin/ditto",
21513
+ "-x",
21514
+ "-k",
21515
+ options.release.registeredArtifactPath,
21516
+ staging
21517
+ ], "iOS registered-device IPA extraction");
21518
+ const app = await findByExtension(staging, ".app");
21519
+ if (!app)
21520
+ throw new Error("The registered-device IPA contains no installable app.");
21521
+ const startedAt = performance.now();
21522
+ await requireSuccess2(options.run, [
21523
+ options.xcrun,
21524
+ "devicectl",
21525
+ "device",
21526
+ "install",
21527
+ "app",
21528
+ "--device",
21529
+ options.device,
21530
+ app
21531
+ ], "iOS registered-device Release installation");
21532
+ return Math.round(performance.now() - startedAt);
21533
+ } finally {
21534
+ await rm6(staging, { force: true, recursive: true });
21535
+ }
21536
+ };
21537
+ var inspectTestFlightInstallation = async (options) => {
21538
+ const apps = await requireSuccess2(options.run, [
21539
+ options.xcrun,
21540
+ "devicectl",
21541
+ "device",
21542
+ "info",
21543
+ "apps",
21544
+ "--device",
21545
+ options.device
21546
+ ], "TestFlight installation inspection");
21547
+ if (!apps.stdout.includes(options.release.metadata.appId))
21548
+ throw new Error("The selected device does not have the requested TestFlight app installed.");
21549
+ return 0;
21550
+ };
21551
+ var runAbsoluteIosDeviceReleaseAcceptance = async (options) => {
21552
+ if (!options.networkUnavailableConfirmed)
21553
+ throw new TypeError("Physical iOS offline acceptance requires explicit confirmation that Airplane Mode is enabled and Wi-Fi is disabled in Settings.");
21554
+ const run = options.run ?? defaultRun2;
21555
+ const xcrun = options.xcrun ?? "/usr/bin/xcrun";
21556
+ const startedAt = performance.now();
21557
+ await requireSuccess2(run, [
21558
+ xcrun,
21559
+ "devicectl",
21560
+ "device",
21561
+ "info",
21562
+ "details",
21563
+ "--device",
21564
+ options.device
21565
+ ], "iOS physical-device availability inspection");
21566
+ const installation = {
21567
+ device: options.device,
21568
+ release: options.release,
21569
+ run,
21570
+ xcrun
21571
+ };
21572
+ const installMs = options.distribution === "registered-device" ? await installRegisteredDeviceRelease(installation) : await inspectTestFlightInstallation(installation);
21573
+ const observe = options.observeLaunch ?? ((launch2) => observeDeviceLaunch({ ...launch2, xcrun }));
21574
+ const marker = releaseMarker(options.release);
21575
+ const launchMs = await observe({
21576
+ appId: options.release.metadata.appId,
21577
+ device: options.device,
21578
+ marker
21579
+ });
21580
+ const relaunchMs = await observe({
21581
+ appId: options.release.metadata.appId,
21582
+ device: options.device,
21583
+ marker
21584
+ });
21585
+ return {
21586
+ artifactBytes: options.release.metadata.bytes,
21587
+ artifactExactness: options.distribution === "testflight" ? "store-delivered" : "archive-equivalent",
21588
+ artifactSha256: options.release.metadata.sha256,
21589
+ ...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
21590
+ distribution: options.distribution === "testflight" ? "apple-processed" : "registered-device",
21591
+ durationMs: Math.round(performance.now() - startedAt),
21592
+ embeddedLocal: true,
21593
+ engine: options.release.metadata.engine,
21594
+ installMs,
21595
+ launchMs,
21596
+ marketingVersion: options.release.metadata.marketingVersion,
21597
+ networkUnavailable: "user-confirmed",
21598
+ relaunchMs,
21599
+ releaseId: options.release.metadata.releaseId,
21600
+ signed: options.release.metadata.signed,
21601
+ status: "pass",
21602
+ target: "device"
21603
+ };
21604
+ };
21605
+
21086
21606
  // src/mobile/iosDeviceAcceptance.ts
21087
21607
  var absoluteIosDeviceAcceptanceCommands = (options) => {
21088
21608
  const xcrun = options.xcrun ?? "/usr/bin/xcrun";
@@ -21101,20 +21621,20 @@ var absoluteIosDeviceAcceptanceCommands = (options) => {
21101
21621
  ]
21102
21622
  };
21103
21623
  };
21104
- var requireSuccess2 = (result, message) => {
21624
+ var requireSuccess3 = (result, message) => {
21105
21625
  if (result.exitCode !== 0)
21106
21626
  throw new Error(message);
21107
21627
  return result;
21108
21628
  };
21109
21629
  var testAbsoluteIosPhysicalDevice = async (options) => {
21110
21630
  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.");
21631
+ requireSuccess3(await options.capture(commands.details), "The selected physical iOS device is unavailable. Pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode.");
21632
+ const apps = requireSuccess3(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
21113
21633
  if (!apps.stdout.includes(options.appId))
21114
21634
  throw new Error("The AbsoluteJS app is not installed on the selected physical iOS device. Start bun dev with the same --ios-device value first.");
21115
21635
  const now = options.now ?? performance.now.bind(performance);
21116
21636
  const startedAt = now();
21117
- requireSuccess2(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
21637
+ requireSuccess3(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
21118
21638
  await options.waitForHmr();
21119
21639
  return {
21120
21640
  hmrConnected: true,
@@ -21124,7 +21644,7 @@ var testAbsoluteIosPhysicalDevice = async (options) => {
21124
21644
  };
21125
21645
 
21126
21646
  // src/mobile/iosConformance.ts
21127
- import { readFile as readFile12, stat as stat4 } from "fs/promises";
21647
+ import { readFile as readFile13, stat as stat5 } from "fs/promises";
21128
21648
  var HMR_LINE = new RegExp(String.raw`\[hmr:ios\]\s+([^\n]*?)\s+(applied in|falling back to reload after|failed after)\s+(\d+)ms(?:; server\s+(\d+)ms, client\s+(\d+)ms)?`, "u");
21129
21649
  var parseAbsoluteIosHmrLog = (line) => {
21130
21650
  const match = HMR_LINE.exec(line);
@@ -21156,13 +21676,13 @@ var waitForAbsoluteIosHmrLog = async (options) => {
21156
21676
  const sleep = options.sleep ?? Bun.sleep;
21157
21677
  const timeoutMs = options.timeoutMs ?? 30000;
21158
21678
  const deadline = Date.now() + timeoutMs;
21159
- let offset = options.startOffset ?? await stat4(options.logPath).then(({ size }) => size).catch(() => 0);
21679
+ let offset = options.startOffset ?? await stat5(options.logPath).then(({ size }) => size).catch(() => 0);
21160
21680
  let buffered = "";
21161
21681
  const poll = async () => {
21162
21682
  if (Date.now() > deadline)
21163
21683
  throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
21164
21684
  options.signal?.throwIfAborted();
21165
- const contents = await readFile12(options.logPath).catch(() => Buffer.alloc(0));
21685
+ const contents = await readFile13(options.logPath).catch(() => Buffer.alloc(0));
21166
21686
  if (contents.byteLength < offset) {
21167
21687
  offset = 0;
21168
21688
  buffered = "";
@@ -21183,8 +21703,8 @@ var waitForAbsoluteIosHmrLog = async (options) => {
21183
21703
  };
21184
21704
 
21185
21705
  // 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";
21706
+ import { mkdir as mkdir10, readFile as readFile14, writeFile as writeFile11 } from "fs/promises";
21707
+ import { join as join12 } from "path";
21188
21708
  var secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
21189
21709
  var bearerPattern = /bearer\s+[^\s,;]+/giu;
21190
21710
  var coordinatePattern = /\b(latitude|longitude|lat|lng)(\s*[=:]\s*)-?\d+(?:\.\d+)?/giu;
@@ -21211,8 +21731,10 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
21211
21731
  let developmentDetails = run.status === "pass" ? `The app launched and connected to native HMR in ${run.durationMs}ms.${routeDetails}` : `The automated native run failed after ${run.durationMs}ms: ${run.error ?? "No error detail was available."}`;
21212
21732
  if (run.release)
21213
21733
  developmentDetails = run.status === "pass" ? `The ${run.release.engine} release was installed from its generated APK set, launched offline in ${run.release.launchMs}ms, and relaunched in ${run.release.relaunchMs}ms.` : `Installed ${run.release.engine} release acceptance failed after ${run.durationMs}ms.`;
21734
+ if (run.iosRelease)
21735
+ developmentDetails = run.status === "pass" ? `The ${run.iosRelease.engine} iOS release rendered embedded local content in ${run.iosRelease.launchMs}ms and relaunched in ${run.iosRelease.relaunchMs}ms.` : `Installed ${run.iosRelease.engine} iOS release acceptance failed after ${run.durationMs}ms.`;
21214
21736
  let hmrDetails = "Correlated edit timing was not requested. Rerun with --wait-for-hmr.";
21215
- if (run.release)
21737
+ if (run.release || run.iosRelease)
21216
21738
  hmrDetails = "HMR is intentionally not part of installed production-release acceptance.";
21217
21739
  if (run.hmr)
21218
21740
  hmrDetails = `Observed native HMR ${run.hmr.outcome} in ${run.hmr.durationMs}ms${run.hmr.serverMs === undefined ? "" : ` (server ${run.hmr.serverMs}ms, client ${run.hmr.clientMs}ms)`}.`;
@@ -21251,6 +21773,18 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
21251
21773
  result: run.release.embeddedOffline ? "PASS" : "FAIL"
21252
21774
  });
21253
21775
  }
21776
+ if (run.iosRelease) {
21777
+ const release = run.iosRelease;
21778
+ checks.push({
21779
+ details: `Validated immutable release ${release.releaseId} (${release.artifactBytes} bytes, SHA-256 ${release.artifactSha256}); ${release.distribution} evidence is ${release.artifactExactness}, installed in ${release.installMs}ms.`,
21780
+ id: "AUTO-IOS-RELEASE-01",
21781
+ result: run.status === "pass" ? "PASS" : "FAIL"
21782
+ }, {
21783
+ details: release.networkUnavailable === "user-confirmed" ? "The tester explicitly confirmed Airplane Mode was enabled and Wi-Fi was disabled before both physical-device launches." : "Simulator Release proved embedded local rendering, but does not claim physical-device network-unavailable behavior.",
21784
+ id: "AUTO-IOS-RELEASE-OFFLINE-01",
21785
+ result: release.networkUnavailable === "user-confirmed" ? "PASS" : "SKIPPED"
21786
+ });
21787
+ }
21254
21788
  if (run.upgrade) {
21255
21789
  const { upgrade } = run;
21256
21790
  checks.push({
@@ -21313,7 +21847,7 @@ var createAbsoluteNativeTestReport = (options) => ({
21313
21847
  run: options.run
21314
21848
  });
21315
21849
  var readPackageVersionForNativeReport = async (packageJsonPath) => {
21316
- const manifest = JSON.parse(await readFile13(packageJsonPath, "utf8"));
21850
+ const manifest = JSON.parse(await readFile14(packageJsonPath, "utf8"));
21317
21851
  if (typeof manifest !== "object" || manifest === null)
21318
21852
  return "unknown";
21319
21853
  const version = Reflect.get(manifest, "version");
@@ -21351,9 +21885,9 @@ ${table(report.manualChecks)}
21351
21885
  `;
21352
21886
  };
21353
21887
  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");
21888
+ await mkdir10(directory, { recursive: true });
21889
+ const jsonPath = join12(directory, "report.json");
21890
+ const markdownPath = join12(directory, "report.md");
21357
21891
  await Promise.all([
21358
21892
  writeFile11(jsonPath, `${JSON.stringify(report, null, 2)}
21359
21893
  `),
@@ -21468,7 +22002,7 @@ var createAbsoluteIosPartnerReport = (options) => {
21468
22002
  absolutejsVersion: options.absolutejsVersion,
21469
22003
  bunVersion: options.bunVersion,
21470
22004
  macosVersion: options.macosVersion,
21471
- provider: "capacitor",
22005
+ provider: options.provider ?? "capacitor",
21472
22006
  xcodeVersion: options.xcodeVersion
21473
22007
  },
21474
22008
  run: {
@@ -21609,8 +22143,8 @@ var createAbsoluteAndroidTestReport = (options) => {
21609
22143
  };
21610
22144
 
21611
22145
  // 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";
22146
+ import { access as access8 } from "fs/promises";
22147
+ import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve8, sep as sep5 } from "path";
21614
22148
  import { pathToFileURL as pathToFileURL2 } from "url";
21615
22149
  var prepareAbsoluteIosRelease = async (publisher, options) => {
21616
22150
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -21636,17 +22170,17 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
21636
22170
  var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21637
22171
  var isPublisher = (value) => isRecord6(value) && typeof value.publish === "function";
21638
22172
  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)) {
22173
+ const root = resolve8(projectRoot);
22174
+ const path = resolve8(root, requested);
22175
+ const projectRelative = relative7(root, path);
22176
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute5(projectRelative)) {
21643
22177
  throw new TypeError("mobile publish --registry must remain inside the project.");
21644
22178
  }
21645
22179
  return path;
21646
22180
  };
21647
22181
  var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
21648
22182
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
21649
- await access7(modulePath).catch(() => {
22183
+ await access8(modulePath).catch(() => {
21650
22184
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
21651
22185
  });
21652
22186
  const loaded = await import(pathToFileURL2(modulePath).href);
@@ -21708,8 +22242,8 @@ var publishAbsoluteIosRelease = async (options) => {
21708
22242
  };
21709
22243
 
21710
22244
  // 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";
22245
+ import { access as access9, readFile as readFile15 } from "fs/promises";
22246
+ import { join as join13, relative as relative8, resolve as resolve9 } from "path";
21713
22247
  var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
21714
22248
  var MOBILE_PACKAGE_NAMES = new Set([
21715
22249
  "@absolutejs/absolute",
@@ -21724,19 +22258,19 @@ var MOBILE_PACKAGE_NAMES = new Set([
21724
22258
  ]);
21725
22259
  var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21726
22260
  var portablePath2 = (projectRoot, path) => {
21727
- const value = relative7(resolve8(projectRoot), resolve8(path)).replaceAll("\\", "/");
22261
+ const value = relative8(resolve9(projectRoot), resolve9(path)).replaceAll("\\", "/");
21728
22262
  return value || ".";
21729
22263
  };
21730
22264
  var pathExists4 = async (path) => {
21731
22265
  try {
21732
- await access8(path);
22266
+ await access9(path);
21733
22267
  return true;
21734
22268
  } catch {
21735
22269
  return false;
21736
22270
  }
21737
22271
  };
21738
22272
  var readObject2 = async (path) => {
21739
- const value = JSON.parse(await readFile14(path, "utf8"));
22273
+ const value = JSON.parse(await readFile15(path, "utf8"));
21740
22274
  if (!isObject2(value))
21741
22275
  throw new TypeError("JSON root must be an object.");
21742
22276
  return value;
@@ -21748,13 +22282,13 @@ var addPackageDeclarations = (declarations, value) => {
21748
22282
  declarations.set(name, declared);
21749
22283
  };
21750
22284
  var packageInspections = async (projectRoot, additionalNames) => {
21751
- const project = await readObject2(join12(projectRoot, "package.json"));
22285
+ const project = await readObject2(join13(projectRoot, "package.json"));
21752
22286
  const declarations = new Map;
21753
22287
  for (const field2 of ["dependencies", "devDependencies"])
21754
22288
  addPackageDeclarations(declarations, project[field2]);
21755
22289
  const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
21756
22290
  return Promise.all(names.map(async (name) => {
21757
- const installedManifest = await readObject2(join12(projectRoot, "node_modules", name, "package.json")).catch(() => {
22291
+ const installedManifest = await readObject2(join13(projectRoot, "node_modules", name, "package.json")).catch(() => {
21758
22292
  return;
21759
22293
  });
21760
22294
  const installed = installedManifest?.version;
@@ -21813,7 +22347,7 @@ var inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) =>
21813
22347
  },
21814
22348
  format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
21815
22349
  nativeProjects: await Promise.all(config.platforms.map(async (platform) => {
21816
- const path = join12(config.nativeProjectDirectory, platform);
22350
+ const path = join13(config.nativeProjectDirectory, platform);
21817
22351
  return {
21818
22352
  initialized: await pathExists4(path),
21819
22353
  path: portablePath2(projectRoot, path),
@@ -21870,8 +22404,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
21870
22404
 
21871
22405
  // src/mobile/ciWorkflow.ts
21872
22406
  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";
22407
+ import { access as access10, mkdir as mkdir11, readFile as readFile16, writeFile as writeFile12 } from "fs/promises";
22408
+ import { dirname as dirname10, extname as extname2, relative as relative9, resolve as resolve10, sep as sep6 } from "path";
21875
22409
  var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
21876
22410
  var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
21877
22411
  var CI_ENV_INDENTATION = 6;
@@ -21890,9 +22424,9 @@ var RESERVED_SECRET_NAMES = new Set([
21890
22424
  "APP_STORE_CONNECT_KEY_ID",
21891
22425
  "APP_STORE_CONNECT_PRIVATE_KEY_BASE64"
21892
22426
  ]);
21893
- var exists3 = async (path) => {
22427
+ var exists4 = async (path) => {
21894
22428
  try {
21895
- await access9(path);
22429
+ await access10(path);
21896
22430
  return true;
21897
22431
  } catch {
21898
22432
  return false;
@@ -21900,10 +22434,10 @@ var exists3 = async (path) => {
21900
22434
  };
21901
22435
  var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
21902
22436
  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 === "") {
22437
+ const root = resolve10(projectRoot);
22438
+ const path = resolve10(root, value);
22439
+ const portable = relative9(root, path).replaceAll("\\", "/");
22440
+ if (portable === ".." || portable.startsWith(`..${sep6}`) || portable.startsWith("../") || portable === "") {
21907
22441
  throw new TypeError(`${field2} must remain inside the project root.`);
21908
22442
  }
21909
22443
  if (/\r|\n/u.test(portable) || portable.startsWith("-"))
@@ -21913,11 +22447,11 @@ var projectPath2 = (projectRoot, value, field2, options = {}) => {
21913
22447
  return portable;
21914
22448
  };
21915
22449
  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") {
22450
+ const root = resolve10(projectRoot);
22451
+ const workflows = resolve10(root, ".github/workflows");
22452
+ const path = resolve10(root, value ?? ".github/workflows/absolute-mobile.yml");
22453
+ const portable = relative9(workflows, path);
22454
+ if (portable === ".." || portable.startsWith(`..${sep6}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
21921
22455
  throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
21922
22456
  }
21923
22457
  return path;
@@ -22346,11 +22880,11 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
22346
22880
  var writeAbsoluteMobileGithubWorkflow = async (options) => {
22347
22881
  const path = workflowOutputPath(options.projectRoot, options.outputPath);
22348
22882
  const generated = createAbsoluteMobileGithubWorkflow(options);
22349
- const previous = await exists3(path) ? await readFile15(path, "utf8") : undefined;
22883
+ const previous = await exists4(path) ? await readFile16(path, "utf8") : undefined;
22350
22884
  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.`);
22885
+ throw new TypeError(`${relative9(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
22352
22886
  if (previous !== generated.workflow) {
22353
- await mkdir10(dirname9(path), { recursive: true });
22887
+ await mkdir11(dirname10(path), { recursive: true });
22354
22888
  await writeFile12(path, generated.workflow);
22355
22889
  }
22356
22890
  return {
@@ -22364,46 +22898,46 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
22364
22898
  };
22365
22899
 
22366
22900
  // src/mobile/updateSigning.ts
22367
- import { createHash as createHash4, sign, verify } from "crypto";
22901
+ import { createHash as createHash5, sign, verify } from "crypto";
22368
22902
  import {
22369
22903
  cp,
22370
- mkdir as mkdir11,
22371
- mkdtemp as mkdtemp3,
22372
- readdir as readdir5,
22373
- readFile as readFile16,
22904
+ mkdir as mkdir12,
22905
+ mkdtemp as mkdtemp4,
22906
+ readdir as readdir6,
22907
+ readFile as readFile17,
22374
22908
  rename as rename10,
22375
- rm as rm6,
22376
- stat as stat5,
22909
+ rm as rm7,
22910
+ stat as stat6,
22377
22911
  writeFile as writeFile13
22378
22912
  } from "fs/promises";
22379
- import { dirname as dirname10, join as join13, relative as relative9, resolve as resolve10 } from "path";
22913
+ import { dirname as dirname11, join as join14, relative as relative10, resolve as resolve11 } from "path";
22380
22914
  var UPDATE_MANIFEST_FILE = "update.json";
22381
22915
  var UPDATE_FILES_DIRECTORY = "files";
22382
- var sha2562 = (value) => createHash4("sha256").update(value).digest("hex");
22916
+ var sha2563 = (value) => createHash5("sha256").update(value).digest("hex");
22383
22917
  var listFiles = async (root, directory = root) => {
22384
- const entries = await readdir5(directory, { withFileTypes: true });
22918
+ const entries = await readdir6(directory, { withFileTypes: true });
22385
22919
  const paths = await Promise.all(entries.map(async (entry) => {
22386
- const path = join13(directory, entry.name);
22920
+ const path = join14(directory, entry.name);
22387
22921
  if (entry.isDirectory())
22388
22922
  return listFiles(root, path);
22389
22923
  if (!entry.isFile())
22390
22924
  throw new TypeError("Mobile updates cannot contain links or special files.");
22391
- return [relative9(root, path).replaceAll("\\", "/")];
22925
+ return [relative10(root, path).replaceAll("\\", "/")];
22392
22926
  }));
22393
22927
  return paths.flat().sort((left, right) => left.localeCompare(right));
22394
22928
  };
22395
22929
  var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
22396
- const source = join13(root, path);
22930
+ const source = join14(root, path);
22397
22931
  const [metadata, contents] = await Promise.all([
22398
- stat5(source),
22399
- readFile16(source)
22932
+ stat6(source),
22933
+ readFile17(source)
22400
22934
  ]);
22401
- return { bytes: metadata.size, path, sha256: sha2562(contents) };
22935
+ return { bytes: metadata.size, path, sha256: sha2563(contents) };
22402
22936
  }));
22403
- var releaseIdFor = (value) => `amu_${sha2562(canonicalizeAbsoluteMobileUpdate(value))}`;
22937
+ var releaseIdFor = (value) => `amu_${sha2563(canonicalizeAbsoluteMobileUpdate(value))}`;
22404
22938
  var buildAbsoluteMobileUpdate = async (options) => {
22405
- const bundleDirectory = resolve10(options.bundleDirectory);
22406
- const outputRoot = resolve10(options.outputDirectory);
22939
+ const bundleDirectory = resolve11(options.bundleDirectory);
22940
+ const outputRoot = resolve11(options.outputDirectory);
22407
22941
  if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
22408
22942
  throw new TypeError("Mobile update output must be outside the embedded bundle.");
22409
22943
  const paths = await listFiles(bundleDirectory);
@@ -22431,30 +22965,30 @@ var buildAbsoluteMobileUpdate = async (options) => {
22431
22965
  value: signature.toString("base64")
22432
22966
  }
22433
22967
  });
22434
- await mkdir11(outputRoot, { recursive: true });
22435
- const outputDirectory = join13(outputRoot, manifest.releaseId);
22436
- const staging = await mkdtemp3(join13(outputRoot, ".stage-"));
22968
+ await mkdir12(outputRoot, { recursive: true });
22969
+ const outputDirectory = join14(outputRoot, manifest.releaseId);
22970
+ const staging = await mkdtemp4(join14(outputRoot, ".stage-"));
22437
22971
  try {
22438
- await cp(bundleDirectory, join13(staging, UPDATE_FILES_DIRECTORY), {
22972
+ await cp(bundleDirectory, join14(staging, UPDATE_FILES_DIRECTORY), {
22439
22973
  force: true,
22440
22974
  recursive: true
22441
22975
  });
22442
- await writeFile13(join13(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
22976
+ await writeFile13(join14(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
22443
22977
  `);
22444
22978
  await rename10(staging, outputDirectory);
22445
22979
  } catch (error) {
22446
- await rm6(staging, { force: true, recursive: true });
22980
+ await rm7(staging, { force: true, recursive: true });
22447
22981
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
22448
22982
  throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
22449
22983
  throw error;
22450
22984
  }
22451
22985
  return {
22452
22986
  manifest,
22453
- manifestPath: join13(outputDirectory, UPDATE_MANIFEST_FILE),
22987
+ manifestPath: join14(outputDirectory, UPDATE_MANIFEST_FILE),
22454
22988
  outputDirectory
22455
22989
  };
22456
22990
  };
22457
- var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile16(join13(resolve10(directory), UPDATE_MANIFEST_FILE), "utf8")));
22991
+ var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile17(join14(resolve11(directory), UPDATE_MANIFEST_FILE), "utf8")));
22458
22992
  var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
22459
22993
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
22460
22994
  const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
@@ -22464,8 +22998,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
22464
22998
  };
22465
22999
 
22466
23000
  // 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";
23001
+ import { access as access11, mkdir as mkdir13, readFile as readFile18, writeFile as writeFile14 } from "fs/promises";
23002
+ import { dirname as dirname12, extname as extname3, join as join15 } from "path";
22469
23003
  var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
22470
23004
  var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
22471
23005
  var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -22531,7 +23065,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
22531
23065
  };
22532
23066
  };
22533
23067
  var finalizeAbsoluteExpoUpdateExport = async (options) => {
22534
- const metadata = JSON.parse(await readFile17(join14(options.exportDirectory, "metadata.json"), "utf8"));
23068
+ const metadata = JSON.parse(await readFile18(join15(options.exportDirectory, "metadata.json"), "utf8"));
22535
23069
  if (!object2(metadata) || !object2(metadata.fileMetadata))
22536
23070
  throw new TypeError("Expo export metadata.json is invalid.");
22537
23071
  const descriptor = parseAbsoluteExpoUpdateDescriptor({
@@ -22542,11 +23076,11 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
22542
23076
  runtimeVersion: options.runtimeVersion
22543
23077
  });
22544
23078
  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(() => {
23079
+ await Promise.all(referenced.map(({ path }) => access11(join15(options.exportDirectory, path)).catch(() => {
22546
23080
  throw new TypeError(`Expo export metadata references missing asset ${path}.`);
22547
23081
  })));
22548
- const destination = join14(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
22549
- await mkdir12(dirname11(destination), { recursive: true });
23082
+ const destination = join15(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
23083
+ await mkdir13(dirname12(destination), { recursive: true });
22550
23084
  await writeFile14(destination, `${JSON.stringify(descriptor, null, "\t")}
22551
23085
  `);
22552
23086
  return { descriptor, path: destination };
@@ -22554,21 +23088,21 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
22554
23088
 
22555
23089
  // src/mobile/expoCodeSigning.ts
22556
23090
  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);
23091
+ import { access as access12, mkdir as mkdir14, writeFile as writeFile15 } from "fs/promises";
23092
+ import { dirname as dirname13, isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep7 } from "path";
23093
+ var exists5 = async (path) => access12(path).then(() => true).catch(() => false);
23094
+ var inside3 = (root, path) => {
23095
+ const location = relative11(root, path);
23096
+ return location === "" || location !== ".." && !location.startsWith(`..${sep7}`) && !isAbsolute6(location);
22563
23097
  };
22564
23098
  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))
23099
+ const root = resolve12(options.projectRoot);
23100
+ const certificatePath = resolve12(root, options.certificatePath);
23101
+ const privateKeyPath = resolve12(root, options.privateKeyPath);
23102
+ const publicKeyPath = resolve12(root, options.publicKeyPath ?? resolve12(dirname13(privateKeyPath), "public-key.pem"));
23103
+ if (!inside3(root, certificatePath))
22570
23104
  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))
23105
+ if (inside3(root, privateKeyPath) || inside3(root, publicKeyPath))
22572
23106
  throw new TypeError("Expo code-signing private/public key output must remain outside the project and source control.");
22573
23107
  const commonName = options.commonName.trim();
22574
23108
  if (!commonName || commonName.length > 64)
@@ -22577,7 +23111,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22577
23111
  if (!Number.isSafeInteger(validityYears) || validityYears < 1 || validityYears > 20)
22578
23112
  throw new TypeError("Expo code-signing validity must be an integer from 1 through 20 years.");
22579
23113
  const collisions = (await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map(async (path) => ({
22580
- exists: await exists4(path),
23114
+ exists: await exists5(path),
22581
23115
  path
22582
23116
  })))).filter((entry) => entry.exists);
22583
23117
  if (collisions.length > 0)
@@ -22595,7 +23129,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22595
23129
  import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
22596
23130
  const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
22597
23131
  const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
22598
- await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir13(dirname12(path), { recursive: true })));
23132
+ await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir14(dirname13(path), { recursive: true })));
22599
23133
  await Promise.all([
22600
23134
  writeFile15(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
22601
23135
  writeFile15(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
@@ -22610,8 +23144,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
22610
23144
  };
22611
23145
 
22612
23146
  // 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";
23147
+ import { access as access13 } from "fs/promises";
23148
+ import { isAbsolute as isAbsolute7, relative as relative12, resolve as resolve13, sep as sep8 } from "path";
22615
23149
  import { pathToFileURL as pathToFileURL3 } from "url";
22616
23150
  var lifecycleMethod = (publisher, name) => {
22617
23151
  const method = publisher[name];
@@ -22720,16 +23254,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
22720
23254
  var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22721
23255
  var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
22722
23256
  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))
23257
+ const root = resolve13(projectRoot);
23258
+ const path = resolve13(root, requested);
23259
+ const projectRelative = relative12(root, path);
23260
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep8}`) || isAbsolute7(projectRelative))
22727
23261
  throw new TypeError(`${label} must remain inside the project.`);
22728
23262
  return path;
22729
23263
  };
22730
23264
  var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
22731
23265
  const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
22732
- await access12(modulePath).catch(() => {
23266
+ await access13(modulePath).catch(() => {
22733
23267
  throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
22734
23268
  });
22735
23269
  const loaded = await import(pathToFileURL3(modulePath).href);
@@ -22827,7 +23361,7 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
22827
23361
  ];
22828
23362
  var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
22829
23363
  var directProjectPackages = async (projectRoot) => {
22830
- const manifest = JSON.parse(await readFile18(join15(projectRoot, "package.json"), "utf8"));
23364
+ const manifest = JSON.parse(await readFile19(join16(projectRoot, "package.json"), "utf8"));
22831
23365
  if (!isRecord7(manifest))
22832
23366
  throw new TypeError("Application package.json must contain an object.");
22833
23367
  const names = new Set;
@@ -22841,7 +23375,7 @@ var directProjectPackages = async (projectRoot) => {
22841
23375
  };
22842
23376
  var resolvedPackageVersion = async (projectRoot, packageName) => {
22843
23377
  try {
22844
- const manifest = JSON.parse(await readFile18(join15(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
23378
+ const manifest = JSON.parse(await readFile19(join16(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
22845
23379
  return isRecord7(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
22846
23380
  } catch {
22847
23381
  return;
@@ -22905,9 +23439,9 @@ var runCapacitor = async (projectRoot, args) => {
22905
23439
  };
22906
23440
  var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
22907
23441
  var expoExecutable = async (project) => {
22908
- const executable = join15(project, "node_modules", ".bin", "expo");
23442
+ const executable = join16(project, "node_modules", ".bin", "expo");
22909
23443
  try {
22910
- await access13(executable);
23444
+ await access14(executable);
22911
23445
  return executable;
22912
23446
  } catch {
22913
23447
  throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
@@ -22939,14 +23473,14 @@ var runExpo = async (project, args, options = {}) => {
22939
23473
  };
22940
23474
  var ensureExpoPackages = async (project, args) => {
22941
23475
  try {
22942
- const manifest = JSON.parse(await readFile18(join15(project, "package.json"), "utf8"));
23476
+ const manifest = JSON.parse(await readFile19(join16(project, "package.json"), "utf8"));
22943
23477
  const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
22944
23478
  if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
22945
23479
  throw new TypeError("Generated Expo dependencies are invalid.");
22946
23480
  await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
22947
23481
  if (typeof expected !== "string")
22948
23482
  throw new TypeError("Generated Expo dependency version is invalid.");
22949
- const installed = JSON.parse(await readFile18(join15(project, "node_modules", name, "package.json"), "utf8"));
23483
+ const installed = JSON.parse(await readFile19(join16(project, "node_modules", name, "package.json"), "utf8"));
22950
23484
  const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
22951
23485
  if (typeof actual !== "string")
22952
23486
  throw new TypeError("Installed Expo dependency version is invalid.");
@@ -23144,7 +23678,7 @@ var sync = async (args) => {
23144
23678
  };
23145
23679
  var associations = async (args) => {
23146
23680
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
23147
- const outputDirectory = resolve13(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
23681
+ const outputDirectory = resolve14(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
23148
23682
  if (args.includes("--verify")) {
23149
23683
  const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
23150
23684
  console.log(`Verified ${result2.results.length} hosted association files`);
@@ -23196,7 +23730,7 @@ var generateGithubCi = async (args) => {
23196
23730
  const publicResult = {
23197
23731
  changed: result.changed,
23198
23732
  format: result.format,
23199
- path: relative12(projectRoot, result.path).replaceAll("\\", "/"),
23733
+ path: relative13(projectRoot, result.path).replaceAll("\\", "/"),
23200
23734
  platforms: result.platforms,
23201
23735
  publishing: result.publishing,
23202
23736
  requiredSecrets: result.requiredSecrets
@@ -23307,9 +23841,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
23307
23841
  temporaryDirectory: undefined
23308
23842
  };
23309
23843
  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-"));
23844
+ const temporaryParent = join16(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
23845
+ await mkdir15(temporaryParent, { recursive: true });
23846
+ const temporaryDirectory = await mkdtemp5(join16(temporaryParent, ".stage-"));
23313
23847
  try {
23314
23848
  await runExpo(mobile.nativeProjectDirectory, [
23315
23849
  "export",
@@ -23321,7 +23855,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
23321
23855
  temporaryDirectory,
23322
23856
  "--clear"
23323
23857
  ], { production: true });
23324
- const generatedApp = JSON.parse(await readFile18(join15(mobile.nativeProjectDirectory, "app.json"), "utf8"));
23858
+ const generatedApp = JSON.parse(await readFile19(join16(mobile.nativeProjectDirectory, "app.json"), "utf8"));
23325
23859
  const expoConfig = isRecord7(generatedApp) ? generatedApp.expo : undefined;
23326
23860
  if (!isRecord7(expoConfig))
23327
23861
  throw new TypeError("Generated Expo app configuration is invalid.");
@@ -23339,7 +23873,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
23339
23873
  temporaryDirectory
23340
23874
  };
23341
23875
  } catch (error) {
23342
- await rm7(temporaryDirectory, { force: true, recursive: true });
23876
+ await rm8(temporaryDirectory, { force: true, recursive: true });
23343
23877
  throw error;
23344
23878
  }
23345
23879
  };
@@ -23364,11 +23898,11 @@ var buildMobileUpdate = async (args) => {
23364
23898
  let expoExportDirectory;
23365
23899
  try {
23366
23900
  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"));
23901
+ const embedded = JSON.parse(await readFile19(join16(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
23368
23902
  const runtimeFingerprint = isRecord7(embedded) ? embedded.nativeRuntime : undefined;
23369
23903
  if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
23370
23904
  throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
23371
- const privateKey = await readFile18(resolve13(projectRoot, signingKeyPath));
23905
+ const privateKey = await readFile19(resolve14(projectRoot, signingKeyPath));
23372
23906
  const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
23373
23907
  const derivedPublicKey = createPublicKey2(privateKey).export({
23374
23908
  format: "der",
@@ -23389,7 +23923,7 @@ var buildMobileUpdate = async (args) => {
23389
23923
  channel: mobile.updates.channel,
23390
23924
  classification,
23391
23925
  keyId,
23392
- outputDirectory: valueAfter(args, "--outdir") ?? join15(projectRoot, ".absolutejs", "mobile", "updates"),
23926
+ outputDirectory: valueAfter(args, "--outdir") ?? join16(projectRoot, ".absolutejs", "mobile", "updates"),
23393
23927
  privateKey,
23394
23928
  runtimeFingerprint
23395
23929
  });
@@ -23401,7 +23935,7 @@ var buildMobileUpdate = async (args) => {
23401
23935
  return result;
23402
23936
  } finally {
23403
23937
  if (expoExportDirectory)
23404
- await rm7(expoExportDirectory, { force: true, recursive: true });
23938
+ await rm8(expoExportDirectory, { force: true, recursive: true });
23405
23939
  sendTelemetryEvent("mobile:update-build", {
23406
23940
  classification: classification ?? "invalid",
23407
23941
  durationMs: Math.round(performance.now() - startedAt),
@@ -23431,7 +23965,7 @@ var generateExpoUpdateSigning = async (args) => {
23431
23965
  ...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
23432
23966
  ...validityYears === undefined ? {} : { validityYears }
23433
23967
  });
23434
- const certificatePath = relative12(projectRoot, result.certificatePath).replaceAll("\\", "/");
23968
+ const certificatePath = relative13(projectRoot, result.certificatePath).replaceAll("\\", "/");
23435
23969
  sendTelemetryEvent("mobile:update-code-signing-generated", {
23436
23970
  engine: "expo",
23437
23971
  validityYears: validityYears ?? 10
@@ -23493,7 +24027,7 @@ var provisionMobileUpdate = async (args) => {
23493
24027
  publicKeys: mobile.updates.publicKeys,
23494
24028
  storage: requestedStorage
23495
24029
  });
23496
- console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative12(projectRoot, path).replaceAll("\\", "/")}.`);
24030
+ console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative13(projectRoot, path).replaceAll("\\", "/")}.`);
23497
24031
  if (requestedStorage === "local")
23498
24032
  console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
23499
24033
  else
@@ -23905,6 +24439,7 @@ var prepareCapacitorIosReleaseProject = async (mobile, projectRoot) => {
23905
24439
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["ios"]);
23906
24440
  await applyAbsoluteNativeUpdates(mobile, ["ios"]);
23907
24441
  await applyAbsoluteNativeObservability(mobile, ["ios"]);
24442
+ await applyAbsoluteNativeReleaseReadiness(mobile, ["ios"]);
23908
24443
  };
23909
24444
  var prepareIosReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoIosReleaseProject(mobile, projectRoot, args) : prepareCapacitorIosReleaseProject(mobile, projectRoot);
23910
24445
  var prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
@@ -23969,7 +24504,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
23969
24504
  const durationMs = Math.round(performance.now() - startedAt);
23970
24505
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
23971
24506
  console.log(`Artifact: ${release.artifactPath}`);
23972
- console.log(`Metadata: ${join15(release.releaseRoot, "release.json")}`);
24507
+ console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
23973
24508
  return release;
23974
24509
  } finally {
23975
24510
  sendTelemetryEvent("mobile:android-release-build", {
@@ -24057,6 +24592,7 @@ var buildLocalIosRelease = async (options) => {
24057
24592
  developmentTeam: process.env.ABSOLUTE_IOS_DEVELOPMENT_TEAM,
24058
24593
  ...options.mobile.engine === "expo" ? { env: expoProductionEnvironment() } : {},
24059
24594
  outputDirectory: valueAfter(options.args, "--outdir"),
24595
+ registeredDeviceArtifact: options.args.includes("--registered-device-artifact"),
24060
24596
  ...options.prepareBuildNumber === undefined ? {} : { prepareBuildNumber: options.prepareBuildNumber },
24061
24597
  projectRoot: options.projectRoot
24062
24598
  });
@@ -24087,6 +24623,8 @@ var buildIos = async (args, prepareBuildNumber) => {
24087
24623
  const remoteProfile = requestedRemote !== undefined || process.platform !== "darwin" ? await getAbsoluteRemoteMacProfile(requestedRemote, remoteProfilePath()) : undefined;
24088
24624
  if (process.platform !== "darwin" && !remoteProfile)
24089
24625
  throw new TypeError("iOS release builds require macOS or a paired Remote Mac. Run `absolute mobile pair mac <name> <user@host>`.");
24626
+ if (remoteProfile && args.includes("--registered-device-artifact"))
24627
+ throw new TypeError("--registered-device-artifact currently requires a local Mac; paired Remote Mac transfer support is not yet available.");
24090
24628
  let success = false;
24091
24629
  const cancellation = remoteProfile ? new AbortController : undefined;
24092
24630
  let stopListeningForCancellation = () => {
@@ -24125,7 +24663,9 @@ var buildIos = async (args, prepareBuildNumber) => {
24125
24663
  const durationMs = Math.round(performance.now() - startedAt);
24126
24664
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
24127
24665
  console.log(`Artifact: ${release.artifactPath}`);
24128
- console.log(`Metadata: ${join15(release.releaseRoot, "release.json")}`);
24666
+ if (release.registeredArtifactPath)
24667
+ console.log(`Registered-device artifact: ${release.registeredArtifactPath}`);
24668
+ console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
24129
24669
  return release;
24130
24670
  } finally {
24131
24671
  stopListeningForCancellation();
@@ -24237,7 +24777,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
24237
24777
  checks.push({
24238
24778
  id: "sync.storage-schema",
24239
24779
  label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
24240
- path: join15(projectRoot, "package.json"),
24780
+ path: join16(projectRoot, "package.json"),
24241
24781
  platform: "host",
24242
24782
  status: "pass"
24243
24783
  });
@@ -24245,7 +24785,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
24245
24785
  checks.push({
24246
24786
  id: "sync.storage-schema",
24247
24787
  label: "Offline schema metadata is invalid",
24248
- path: join15(projectRoot, "package.json"),
24788
+ path: join16(projectRoot, "package.json"),
24249
24789
  platform: "host",
24250
24790
  remediation: error instanceof Error ? error.message : String(error),
24251
24791
  status: "fail"
@@ -24312,7 +24852,7 @@ AbsoluteJS can configure ${installPlatform} emulation:`);
24312
24852
  Emulator setup verification:`);
24313
24853
  printDoctorChecks(result.checks.filter((check) => check.platform === "host" || check.platform === installPlatform));
24314
24854
  };
24315
- var captureCommand3 = (command) => {
24855
+ var captureCommand4 = (command) => {
24316
24856
  const result = Bun.spawnSync(command, {
24317
24857
  stderr: "pipe",
24318
24858
  stdin: "ignore",
@@ -24333,7 +24873,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
24333
24873
  }
24334
24874
  return { https: args.includes("--https"), port };
24335
24875
  }
24336
- const instances = listLiveInstances().filter((instance2) => resolve13(instance2.cwd) === resolve13(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24876
+ const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24337
24877
  if (instances.length !== 1) {
24338
24878
  throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for Android to report ready, then run `absolute mobile test android`." : "Multiple dev servers are running for this project. Select one with mobile test android --port <port>.");
24339
24879
  }
@@ -24362,7 +24902,7 @@ var requireAndroidAdb = async () => {
24362
24902
  return adb2;
24363
24903
  };
24364
24904
  var selectAndroidSerial = (adb2, explicitSerial) => {
24365
- const devices = captureCommand3([adb2, "devices"]);
24905
+ const devices = captureCommand4([adb2, "devices"]);
24366
24906
  if (devices.exitCode !== 0) {
24367
24907
  throw new Error(`Could not list Android targets: ${devices.stderr.trim() || devices.stdout.trim()}`);
24368
24908
  }
@@ -24382,7 +24922,7 @@ var selectAndroidSerial = (adb2, explicitSerial) => {
24382
24922
  var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial, json) => {
24383
24923
  if (explicitSerial)
24384
24924
  return selectAndroidSerial(adb2, explicitSerial);
24385
- const current = captureCommand3([adb2, "devices"]);
24925
+ const current = captureCommand4([adb2, "devices"]);
24386
24926
  const ready = current.exitCode === 0 ? parseAdbDevices(current.stdout).find((serial) => serial.startsWith("emulator-")) : undefined;
24387
24927
  if (ready)
24388
24928
  return ready;
@@ -24401,11 +24941,11 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
24401
24941
  launched.unref();
24402
24942
  const startedAt = performance.now();
24403
24943
  const bootedSerial = () => {
24404
- const devices = captureCommand3([adb2, "devices"]);
24944
+ const devices = captureCommand4([adb2, "devices"]);
24405
24945
  const serial = devices.exitCode === 0 ? parseAdbDevices(devices.stdout).find((candidate) => candidate.startsWith("emulator-")) : undefined;
24406
24946
  if (!serial)
24407
24947
  return;
24408
- const booted = captureCommand3([
24948
+ const booted = captureCommand4([
24409
24949
  adb2,
24410
24950
  "-s",
24411
24951
  serial,
@@ -24427,8 +24967,8 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
24427
24967
  return waitForBoot();
24428
24968
  };
24429
24969
  var safeArtifactRoot = (projectRoot, value) => {
24430
- const root = resolve13(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
24431
- if (root !== projectRoot && !root.startsWith(`${resolve13(projectRoot)}/`)) {
24970
+ const root = resolve14(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
24971
+ if (root !== projectRoot && !root.startsWith(`${resolve14(projectRoot)}/`)) {
24432
24972
  throw new TypeError("mobile test --artifacts must remain inside the project.");
24433
24973
  }
24434
24974
  return root;
@@ -24456,11 +24996,11 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
24456
24996
  });
24457
24997
  };
24458
24998
  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(() => {
24999
+ await mkdir15(options.artifactRoot, { recursive: true });
25000
+ const screenshot = options.session ? await options.session.screenshot(join16(options.artifactRoot, "android-failure.png")).catch(() => {
24461
25001
  return;
24462
25002
  }) : undefined;
24463
- const diagnosticPath = join15(options.artifactRoot, "android-failure.json");
25003
+ const diagnosticPath = join16(options.artifactRoot, "android-failure.json");
24464
25004
  await writeFile16(diagnosticPath, `${JSON.stringify({
24465
25005
  diagnostics: options.session?.diagnostics ?? [],
24466
25006
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -24536,7 +25076,7 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
24536
25076
  release,
24537
25077
  serial
24538
25078
  });
24539
- const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join15(reportRoot, "android-release.png")) : undefined;
25079
+ const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join16(reportRoot, "android-release.png")) : undefined;
24540
25080
  sendTelemetryEvent("mobile:android-release-conformance", {
24541
25081
  durationMs: result.durationMs,
24542
25082
  engine: result.engine,
@@ -24583,8 +25123,8 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
24583
25123
  platform: "android",
24584
25124
  success: false
24585
25125
  });
24586
- await mkdir14(artifactRoot, { recursive: true });
24587
- const diagnosticPath = join15(artifactRoot, "android-release-failure.json");
25126
+ await mkdir15(artifactRoot, { recursive: true });
25127
+ const diagnosticPath = join16(artifactRoot, "android-release-failure.json");
24588
25128
  await writeFile16(diagnosticPath, `${JSON.stringify({
24589
25129
  error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
24590
25130
  platform: "android",
@@ -24679,7 +25219,7 @@ var testAndroid = async (args) => {
24679
25219
  console.log(JSON.stringify(report, null, 2));
24680
25220
  else
24681
25221
  printAndroidTestReport(report);
24682
- const screenshot = reportRoot ? await session.screenshot(join15(artifactRoot, "android-emulator.png")) : undefined;
25222
+ const screenshot = reportRoot ? await session.screenshot(join16(artifactRoot, "android-emulator.png")) : undefined;
24683
25223
  await writeRequestedAndroidReport({
24684
25224
  adb: adb2,
24685
25225
  args,
@@ -24748,14 +25288,14 @@ var requireIosTestContext = (args, projectRoot) => {
24748
25288
  const port = Number(explicit);
24749
25289
  if (!Number.isInteger(port) || port < 1 || port > 65535)
24750
25290
  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);
25291
+ const instance2 = listLiveInstances().find((candidate) => resolve14(candidate.cwd) === resolve14(projectRoot) && candidate.source === "dev" && candidate.port === port);
24752
25292
  return {
24753
25293
  https: instance2?.https ?? args.includes("--https"),
24754
25294
  instance: instance2,
24755
25295
  port
24756
25296
  };
24757
25297
  }
24758
- const instances = listLiveInstances().filter((instance2) => resolve13(instance2.cwd) === resolve13(projectRoot) && instance2.source === "dev" && instance2.port !== null);
25298
+ const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
24759
25299
  if (instances.length !== 1)
24760
25300
  throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for iOS to report ready, then run `absolute mobile test ios`." : "Multiple dev servers are running for this project. Select one with mobile test ios --port <port>.");
24761
25301
  const [instance] = instances;
@@ -24845,11 +25385,11 @@ var physicalIosCapture = async (options) => {
24845
25385
  remote: false,
24846
25386
  xcodeVersion: requireCapturedCommand(["/usr/bin/xcodebuild", "-version"], "Xcode version inspection").stdout.trim(),
24847
25387
  xcrun: "/usr/bin/xcrun",
24848
- capture: async (command) => captureCommand3(command)
25388
+ capture: async (command) => captureCommand4(command)
24849
25389
  };
24850
25390
  };
24851
25391
  var selectIosSimulator = (xcrun, explicitUdid) => {
24852
- const result = captureCommand3([
25392
+ const result = captureCommand4([
24853
25393
  xcrun,
24854
25394
  "simctl",
24855
25395
  "list",
@@ -24871,16 +25411,37 @@ var selectIosSimulator = (xcrun, explicitUdid) => {
24871
25411
  throw new TypeError("No ready AbsoluteJS iOS simulator was found. Start `bun dev` and wait for the iOS target to become ready.");
24872
25412
  return selected;
24873
25413
  };
25414
+ var selectOrStartIosReleaseSimulator = async (xcrun, explicitUdid) => {
25415
+ const result = captureCommand4([
25416
+ xcrun,
25417
+ "simctl",
25418
+ "list",
25419
+ "devices",
25420
+ "available",
25421
+ "-j"
25422
+ ]);
25423
+ if (result.exitCode !== 0)
25424
+ throw new Error(`Could not list iOS simulators: ${result.stderr.trim() || result.stdout.trim()}`);
25425
+ const devices = parseIosSimulators(result.stdout).filter((device) => device.isAvailable);
25426
+ const selected = explicitUdid ? devices.find((device) => device.udid === explicitUdid) : devices.find((device) => device.name === ABSOLUTE_IOS_SIMULATOR_NAME);
25427
+ if (!selected)
25428
+ throw new TypeError(explicitUdid ? `iOS simulator ${explicitUdid} is unavailable.` : "No managed AbsoluteJS iOS simulator was found. Run `absolute mobile doctor ios --fix`.");
25429
+ if (selected.state !== "Booted") {
25430
+ requireCapturedCommand([xcrun, "simctl", "boot", selected.udid], "iOS Simulator boot");
25431
+ requireCapturedCommand([xcrun, "simctl", "bootstatus", selected.udid, "-b"], "iOS Simulator readiness");
25432
+ }
25433
+ return { ...selected, state: "Booted" };
25434
+ };
24874
25435
  var requireCapturedCommand = (command, label) => {
24875
- const result = captureCommand3(command);
25436
+ const result = captureCommand4(command);
24876
25437
  if (result.exitCode !== 0)
24877
25438
  throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
24878
25439
  return result;
24879
25440
  };
24880
25441
  var writeIosFailureArtifacts = async (options) => {
24881
- await mkdir14(options.artifactRoot, { recursive: true });
24882
- const screenshot = join15(options.artifactRoot, "ios-failure.png");
24883
- const screenshotResult = captureCommand3([
25442
+ await mkdir15(options.artifactRoot, { recursive: true });
25443
+ const screenshot = join16(options.artifactRoot, "ios-failure.png");
25444
+ const screenshotResult = captureCommand4([
24884
25445
  options.xcrun,
24885
25446
  "simctl",
24886
25447
  "io",
@@ -24888,7 +25449,7 @@ var writeIosFailureArtifacts = async (options) => {
24888
25449
  "screenshot",
24889
25450
  screenshot
24890
25451
  ]);
24891
- const diagnosticPath = join15(options.artifactRoot, "ios-failure.json");
25452
+ const diagnosticPath = join16(options.artifactRoot, "ios-failure.json");
24892
25453
  await writeFile16(diagnosticPath, `${JSON.stringify({
24893
25454
  appId: options.appId,
24894
25455
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -24916,8 +25477,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
24916
25477
  var absolutejsVersionForReport = async () => {
24917
25478
  let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
24918
25479
  const versions = await Promise.all([
24919
- resolve13(import.meta.dir, "..", "..", "package.json"),
24920
- resolve13(import.meta.dir, "..", "..", "..", "package.json")
25480
+ resolve14(import.meta.dir, "..", "..", "package.json"),
25481
+ resolve14(import.meta.dir, "..", "..", "..", "package.json")
24921
25482
  ].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
24922
25483
  for (const version of versions) {
24923
25484
  if (version === "unknown")
@@ -24964,7 +25525,11 @@ var writeRequestedIosReport = async (options) => {
24964
25525
  const metadata = options.metadata ?? (options.xcrun ? await iosReportMetadata(options.xcrun) : undefined);
24965
25526
  if (!metadata)
24966
25527
  throw new Error("iOS report metadata could not be inspected.");
24967
- const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({ ...metadata, run: options.run }));
25528
+ const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({
25529
+ ...metadata,
25530
+ ...options.provider ? { provider: options.provider } : {},
25531
+ run: options.run
25532
+ }));
24968
25533
  const print = options.args.includes("--json") ? console.error : console.log;
24969
25534
  print(`iOS partner report: ${paths.markdownPath}`);
24970
25535
  print(`Return this report directory: ${paths.directory}`);
@@ -25081,8 +25646,167 @@ var testPhysicalIos = async (options) => {
25081
25646
  throw error;
25082
25647
  }
25083
25648
  };
25649
+ var printIosReleaseAcceptance = (result, json) => {
25650
+ if (json) {
25651
+ console.log(JSON.stringify(result, null, 2));
25652
+ return;
25653
+ }
25654
+ console.log(`\u2713 ${result.artifactExactness} ${result.engine} iOS release evidence passed via ${result.distribution}.`);
25655
+ console.log(`\u2713 Embedded local content launched in ${getDurationString(result.launchMs)} and relaunched in ${getDurationString(result.relaunchMs)}.`);
25656
+ console.log(result.networkUnavailable === "user-confirmed" ? "\u2713 Physical-device network-unavailable state was explicitly confirmed." : "\u2139 Simulator evidence does not claim physical-device offline behavior.");
25657
+ };
25658
+ var iosReleaseRun = (result, appId, targetId) => ({
25659
+ appId,
25660
+ durationMs: result.durationMs,
25661
+ hmrConnected: false,
25662
+ iosRelease: {
25663
+ artifactBytes: result.artifactBytes,
25664
+ artifactExactness: result.artifactExactness,
25665
+ artifactSha256: result.artifactSha256,
25666
+ distribution: result.distribution,
25667
+ embeddedLocal: result.embeddedLocal,
25668
+ engine: result.engine,
25669
+ installMs: result.installMs,
25670
+ launchMs: result.launchMs,
25671
+ networkUnavailable: result.networkUnavailable,
25672
+ relaunchMs: result.relaunchMs,
25673
+ releaseId: result.releaseId,
25674
+ signed: result.signed
25675
+ },
25676
+ status: "pass",
25677
+ targetId,
25678
+ targetKind: result.target
25679
+ });
25680
+ var runIosPhysicalReleaseTarget = async (options) => {
25681
+ const confirmed = options.args.includes("--yes") || await confirmInstall("On the selected iPhone, enable Airplane Mode and then disable Wi-Fi in Settings. Confirm both are still disabled and continue with two release launches?");
25682
+ if (!confirmed)
25683
+ throw new TypeError("Physical iOS offline acceptance was cancelled because network-unavailable state was not confirmed.");
25684
+ const targetId = normalizeAbsoluteIosDeviceIdentifier(options.device);
25685
+ const result = await runAbsoluteIosDeviceReleaseAcceptance({
25686
+ device: targetId,
25687
+ distribution: options.args.includes("--testflight") ? "testflight" : "registered-device",
25688
+ networkUnavailableConfirmed: true,
25689
+ release: options.release,
25690
+ xcrun: options.xcrun
25691
+ });
25692
+ return { result, targetId };
25693
+ };
25694
+ var runIosSimulatorReleaseTarget = async (options) => {
25695
+ const simulator = await selectOrStartIosReleaseSimulator(options.xcrun, valueAfter(options.args, "--udid") ?? valueAfter(options.args, "--serial"));
25696
+ const xcodebuild = requireCapturedCommand([options.xcrun, "--find", "xcodebuild"], "Xcode build-tool inspection").stdout.trim();
25697
+ const result = await runAbsoluteIosSimulatorReleaseAcceptance({
25698
+ artifactDirectory: options.artifactRoot,
25699
+ config: options.mobile,
25700
+ release: options.release,
25701
+ udid: simulator.udid,
25702
+ xcodebuild,
25703
+ xcrun: options.xcrun
25704
+ });
25705
+ return { result, targetId: simulator.udid };
25706
+ };
25707
+ var testIosRelease = async (args, mobile, projectRoot) => {
25708
+ const requested = valueAfter(args, "--release");
25709
+ if (!requested || requested.startsWith("--"))
25710
+ throw new TypeError("mobile test ios --release requires a release directory or release.json path.");
25711
+ if (!mobile.platforms.includes("ios"))
25712
+ throw new TypeError("mobile test ios requires ios in mobile.platforms.");
25713
+ if (args.includes("--remote"))
25714
+ throw new TypeError("Installed iOS release acceptance currently runs on a local Mac. Remote Mac acceptance will be added to the paired-host protocol.");
25715
+ const release = await readAbsoluteIosRelease(projectRoot, requested);
25716
+ if (release.metadata.appId !== mobile.appId)
25717
+ throw new TypeError(`iOS release app ID ${release.metadata.appId} does not match mobile.appId ${mobile.appId}.`);
25718
+ if (release.metadata.engine !== mobile.engine)
25719
+ throw new TypeError(`iOS release engine ${release.metadata.engine} does not match configured engine ${mobile.engine}.`);
25720
+ if (mobile.engine === "expo" && mobile.expoNativeRoutes[mobile.entry])
25721
+ throw new TypeError("Embedded-local Expo release acceptance requires mobile.entry to be a web route; native Expo routes may depend on trusted-server page data.");
25722
+ const requestedDevice = valueAfter(args, "--device");
25723
+ if (args.includes("--device") && !requestedDevice)
25724
+ throw new TypeError("mobile test ios --release --device requires a device identifier or name.");
25725
+ if (args.includes("--testflight") && !requestedDevice)
25726
+ throw new TypeError("mobile test ios --release --testflight requires --device.");
25727
+ if (requestedDevice && (valueAfter(args, "--udid") || valueAfter(args, "--serial")))
25728
+ throw new TypeError("mobile test ios --release --device cannot be combined with a simulator selector.");
25729
+ const xcrun = await requireIosXcrun();
25730
+ const reportRoot = nativeReportRoot(args, projectRoot, "ios");
25731
+ const artifactRoot = reportRoot ?? safeArtifactRoot(projectRoot, valueAfter(args, "--artifacts") ?? `.absolutejs/mobile/test-artifacts/${release.metadata.releaseId}`);
25732
+ const startedAt = performance.now();
25733
+ let targetId = requestedDevice ?? "ios-simulator";
25734
+ try {
25735
+ const acceptance = requestedDevice ? await runIosPhysicalReleaseTarget({
25736
+ args,
25737
+ device: requestedDevice,
25738
+ release,
25739
+ xcrun
25740
+ }) : await runIosSimulatorReleaseTarget({
25741
+ args,
25742
+ artifactRoot,
25743
+ mobile,
25744
+ release,
25745
+ xcrun
25746
+ });
25747
+ const { result, targetId: acceptedTargetId } = acceptance;
25748
+ targetId = acceptedTargetId;
25749
+ sendTelemetryEvent("mobile:ios-release-conformance", {
25750
+ artifactExactness: result.artifactExactness,
25751
+ distribution: result.distribution,
25752
+ durationMs: result.durationMs,
25753
+ engine: result.engine,
25754
+ installMs: result.installMs,
25755
+ launchMs: result.launchMs,
25756
+ platform: "ios",
25757
+ relaunchMs: result.relaunchMs,
25758
+ success: true
25759
+ });
25760
+ await writeRequestedIosReport({
25761
+ args,
25762
+ projectRoot,
25763
+ provider: mobile.engine,
25764
+ run: iosReleaseRun(result, mobile.appId, targetId),
25765
+ xcrun
25766
+ });
25767
+ printIosReleaseAcceptance(result, args.includes("--json"));
25768
+ return result;
25769
+ } catch (error) {
25770
+ const durationMs = Math.round(performance.now() - startedAt);
25771
+ sendTelemetryEvent("mobile:ios-release-conformance", {
25772
+ durationMs,
25773
+ engine: mobile.engine,
25774
+ platform: "ios",
25775
+ success: false
25776
+ });
25777
+ await mkdir15(artifactRoot, { recursive: true });
25778
+ const diagnosticPath = join16(artifactRoot, "ios-release-failure.json");
25779
+ await writeFile16(diagnosticPath, `${JSON.stringify({
25780
+ error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
25781
+ platform: "ios",
25782
+ provider: mobile.engine,
25783
+ releaseId: release.metadata.releaseId,
25784
+ status: "fail"
25785
+ }, null, 2)}
25786
+ `);
25787
+ if (reportRoot)
25788
+ await writeRequestedIosReport({
25789
+ args,
25790
+ projectRoot,
25791
+ provider: mobile.engine,
25792
+ run: {
25793
+ appId: mobile.appId,
25794
+ durationMs,
25795
+ error: "Installed iOS release acceptance failed; inspect the local diagnostic artifact before sharing evidence.",
25796
+ hmrConnected: false,
25797
+ status: "fail",
25798
+ targetId,
25799
+ targetKind: requestedDevice ? "device" : "simulator"
25800
+ },
25801
+ xcrun
25802
+ });
25803
+ throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}`, { cause: error });
25804
+ }
25805
+ };
25084
25806
  var testIos = async (args) => {
25085
25807
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
25808
+ if (args.includes("--release"))
25809
+ return testIosRelease(args, mobile, projectRoot);
25086
25810
  requireCapacitorEngine(mobile, "mobile test ios");
25087
25811
  const { https, instance, port } = requireIosTestContext(args, projectRoot);
25088
25812
  if (!mobile.platforms.includes("ios"))
@@ -25142,8 +25866,8 @@ var testIos = async (args) => {
25142
25866
  mobile.appId
25143
25867
  ], "iOS app launch");
25144
25868
  await waitForIosHmrClient({ https, port, timeoutMs });
25145
- await mkdir14(artifactRoot, { recursive: true });
25146
- const screenshot = join15(artifactRoot, "ios-simulator.png");
25869
+ await mkdir15(artifactRoot, { recursive: true });
25870
+ const screenshot = join16(artifactRoot, "ios-simulator.png");
25147
25871
  requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
25148
25872
  const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
25149
25873
  const report = {
@@ -25329,7 +26053,7 @@ var runMobile = async (args) => {
25329
26053
  await publishIos(args.slice(2));
25330
26054
  return;
25331
26055
  }
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]");
26056
+ throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--registered-device-artifact] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path] | test ios [--release release-dir [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
25333
26057
  };
25334
26058
  export {
25335
26059
  runMobile