@absolutejs/absolute 0.20.0-beta.52 → 0.20.0-beta.54

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.
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-x73pOK/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-WVc43d/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-x73pOK/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-WVc43d/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -48,7 +48,7 @@ var warnMissingStreamingSlotCollector = (primitiveName) => {
48
48
  getWarningController()?.maybeWarn(primitiveName);
49
49
  };
50
50
 
51
- // .angular-partial-tmp-x73pOK/src/core/streamingSlotRegistry.ts
51
+ // .angular-partial-tmp-WVc43d/src/core/streamingSlotRegistry.ts
52
52
  var STREAMING_SLOT_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotAsyncLocalStorage");
53
53
  var isObjectRecord2 = (value) => Boolean(value) && typeof value === "object";
54
54
  var isAsyncLocalStorage = (value) => isObjectRecord2(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
package/dist/cli/index.js CHANGED
@@ -5957,7 +5957,7 @@ import {
5957
5957
  resolve as resolvePath,
5958
5958
  sep as sep5
5959
5959
  } from "path";
5960
- var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join15(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
5960
+ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, REMOTE_RELEASE_LEASE_HEARTBEAT_MS = 15000, REMOTE_RELEASE_LEASE_STALE_SECONDS = 120, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join15(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
5961
5961
  format: PROFILE_FORMAT,
5962
5962
  profiles: {}
5963
5963
  }), loadStore = async (path = defaultProfilePath()) => {
@@ -6142,7 +6142,111 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6142
6142
  remoteProjectRoot: posix.join(profile.workspaceRoot, "projects", projectIdentity(projectRoot, config.appId), "current"),
6143
6143
  xcodebuild: "remote:xcodebuild",
6144
6144
  xcrun: "remote:xcrun"
6145
- }), installAbsoluteRemoteMacAgent = async (project) => {
6145
+ }), releaseLeasePath = (project) => posix.join(posix.dirname(project.remoteProjectRoot), ".release-lease"), acquireAbsoluteRemoteMacReleaseLease = async (project, options = {}) => {
6146
+ options.signal?.throwIfAborted();
6147
+ const token = randomUUID4();
6148
+ const path = releaseLeasePath(project);
6149
+ const stalePath = `${path}.stale-${token}`;
6150
+ const staleSeconds = options.staleSeconds ?? REMOTE_RELEASE_LEASE_STALE_SECONDS;
6151
+ if (!Number.isSafeInteger(staleSeconds) || staleSeconds < 30)
6152
+ throw new TypeError("Remote Mac release lease expiry must be at least 30 seconds.");
6153
+ const owner = JSON.stringify({
6154
+ acquiredAt: new Date().toISOString(),
6155
+ appId: project.config.appId,
6156
+ host: process.env.HOSTNAME ?? "unknown",
6157
+ pid: process.pid,
6158
+ token
6159
+ });
6160
+ const script = [
6161
+ "set -eu",
6162
+ "umask 077",
6163
+ `mkdir -p ${shellQuote(posix.dirname(path))}`,
6164
+ `if mkdir ${shellQuote(path)} 2>/dev/null; then status=ACQUIRED; else now=$(date +%s); modified=$(stat -f %m ${shellQuote(path)} 2>/dev/null || printf '0'); age=$((now-modified)); if [ "$age" -le ${staleSeconds} ]; then printf 'BUSY\\n'; cat ${shellQuote(posix.join(path, "owner.json"))} 2>/dev/null || true; exit 73; fi; if mv ${shellQuote(path)} ${shellQuote(stalePath)} 2>/dev/null && mkdir ${shellQuote(path)} 2>/dev/null; then status=RECOVERED; rm -rf ${shellQuote(stalePath)}; else printf 'BUSY\\n'; exit 73; fi; fi`,
6165
+ `printf '%s\\n' ${shellQuote(token)} > ${shellQuote(posix.join(path, "token"))}`,
6166
+ `printf '%s\\n' ${shellQuote(owner)} > ${shellQuote(posix.join(path, "owner.json"))}`,
6167
+ `touch ${shellQuote(path)}`,
6168
+ `printf '%s\\n' "$status"`
6169
+ ].join("; ");
6170
+ const capture = options.transport?.capture ?? defaultTransport.capture;
6171
+ const result = await capture([
6172
+ ...absoluteRemoteMacSshBase(project.profile),
6173
+ "/bin/sh -lc",
6174
+ shellQuote(script)
6175
+ ]);
6176
+ if (result.exitCode !== 0) {
6177
+ if (result.exitCode === 73 || result.stdout.startsWith("BUSY"))
6178
+ throw new Error(`Remote Mac ${project.profile.name} is already building ${project.config.appId}. Wait for that release to finish; a crashed lease recovers automatically after ${staleSeconds} seconds.${result.stdout.split(`
6179
+ `).slice(1).join(" ").trim() ? ` Owner: ${result.stdout.split(`
6180
+ `).slice(1).join(" ").trim()}` : ""}`);
6181
+ requireRemoteSuccess(result, "Remote Mac release lease acquisition");
6182
+ }
6183
+ const recovered = result.stdout.trim().split(/\r?\n/u).at(-1) === "RECOVERED";
6184
+ const runOwned = async (action) => {
6185
+ const ownedScript = `test -f ${shellQuote(posix.join(path, "token"))} && ` + `test "$(cat ${shellQuote(posix.join(path, "token"))})" = ${shellQuote(token)} && ${action}`;
6186
+ const owned = await capture([
6187
+ ...absoluteRemoteMacSshBase(project.profile),
6188
+ "/bin/sh -lc",
6189
+ shellQuote(ownedScript)
6190
+ ]);
6191
+ requireRemoteSuccess(owned, "Remote Mac release lease ownership check");
6192
+ };
6193
+ return {
6194
+ path,
6195
+ recovered,
6196
+ token,
6197
+ heartbeat: () => runOwned(`touch ${shellQuote(path)}`),
6198
+ release: () => runOwned(`rm -rf ${shellQuote(path)}`)
6199
+ };
6200
+ }, inspectAbsoluteRemoteMacWorkspace = async (profile, transport) => {
6201
+ const root = profile.workspaceRoot;
6202
+ const script = [
6203
+ "set -eu",
6204
+ `mkdir -p ${shellQuote(root)}`,
6205
+ `bytes=$(du -sk ${shellQuote(root)} 2>/dev/null | awk '{print $1 * 1024}')`,
6206
+ `projects=$(find ${shellQuote(posix.join(root, "projects"))} -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')`,
6207
+ `agents=$(find ${shellQuote(posix.join(root, "agents"))} -mindepth 2 -maxdepth 2 -type d 2>/dev/null | wc -l | tr -d ' ')`,
6208
+ `leases=$(find ${shellQuote(posix.join(root, "projects"))} -mindepth 2 -maxdepth 2 -type d -name .release-lease 2>/dev/null | wc -l | tr -d ' ')`,
6209
+ `printf '%s\\n%s\\n%s\\n%s\\n' "${"$"}{bytes:-0}" "${"$"}{projects:-0}" "${"$"}{agents:-0}" "${"$"}{leases:-0}"`
6210
+ ].join("; ");
6211
+ const result = await (transport?.capture ?? defaultTransport.capture)([
6212
+ ...absoluteRemoteMacSshBase(profile),
6213
+ "/bin/sh -lc",
6214
+ shellQuote(script)
6215
+ ]);
6216
+ const [bytes, projectCount, agentCount, leaseCount] = requireRemoteSuccess(result, "Remote Mac workspace inspection").split(/\r?\n/u).map(Number);
6217
+ if ([bytes, projectCount, agentCount, leaseCount].some((value) => typeof value !== "number" || !Number.isSafeInteger(value) || value < 0))
6218
+ throw new TypeError("Remote Mac returned invalid workspace statistics.");
6219
+ return {
6220
+ agentCount: agentCount ?? 0,
6221
+ bytes: bytes ?? 0,
6222
+ leaseCount: leaseCount ?? 0,
6223
+ profile: profile.name,
6224
+ projectCount: projectCount ?? 0,
6225
+ workspaceRoot: root
6226
+ };
6227
+ }, cleanAbsoluteRemoteMacWorkspace = async (profile, transport) => {
6228
+ const root = profile.workspaceRoot;
6229
+ const projects = posix.join(root, "projects");
6230
+ const script = [
6231
+ "set -eu",
6232
+ `mkdir -p ${shellQuote(root)}`,
6233
+ `project_staging=$(find ${shellQuote(projects)} -mindepth 2 -maxdepth 2 -type d \\( -name '.incoming-*' -o -name '.previous' -o -name '.release-lease.stale-*' \\) -mtime +0 -prune -print 2>/dev/null | wc -l | tr -d ' ')`,
6234
+ `mobile_staging=$(find ${shellQuote(projects)} -type d \\( -path '*/current/.absolutejs/mobile/.ios-build-*' -o -path '*/current/.absolutejs/mobile/.ios-stage-*' -o -path '*/current/.absolutejs/mobile/*.incoming-*' \\) -mtime +0 -prune -print 2>/dev/null | wc -l | tr -d ' ')`,
6235
+ `count=$((project_staging+mobile_staging))`,
6236
+ `find ${shellQuote(projects)} -mindepth 2 -maxdepth 2 -type d \\( -name '.incoming-*' -o -name '.previous' -o -name '.release-lease.stale-*' \\) -mtime +0 -prune -exec rm -rf {} + 2>/dev/null || true`,
6237
+ `find ${shellQuote(projects)} -type d \\( -path '*/current/.absolutejs/mobile/.ios-build-*' -o -path '*/current/.absolutejs/mobile/.ios-stage-*' -o -path '*/current/.absolutejs/mobile/*.incoming-*' \\) -mtime +0 -prune -exec rm -rf {} + 2>/dev/null || true`,
6238
+ `printf '%s\\n' "$count"`
6239
+ ].join("; ");
6240
+ const result = await (transport?.capture ?? defaultTransport.capture)([
6241
+ ...absoluteRemoteMacSshBase(profile),
6242
+ "/bin/sh -lc",
6243
+ shellQuote(script)
6244
+ ]);
6245
+ const removed = Number(requireRemoteSuccess(result, "Remote Mac workspace cleanup"));
6246
+ if (!Number.isSafeInteger(removed) || removed < 0)
6247
+ throw new TypeError("Remote Mac returned an invalid cleanup result.");
6248
+ return { profile: profile.name, removed, workspaceRoot: root };
6249
+ }, installAbsoluteRemoteMacAgent = async (project) => {
6146
6250
  const artifact = await materializeAbsoluteRemoteMacAgent(project.projectRoot);
6147
6251
  const directory = posix.join(project.profile.workspaceRoot, "agents", `protocol-${ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION}`, artifact.sha256);
6148
6252
  const remotePath = posix.join(directory, "agent.js");
@@ -6291,13 +6395,16 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6291
6395
  "."
6292
6396
  ]
6293
6397
  };
6294
- }, syncAbsoluteRemoteMacProject = async (project) => {
6398
+ }, syncAbsoluteRemoteMacProject = async (project, options = {}) => {
6399
+ options.signal?.throwIfAborted();
6295
6400
  const commands = absoluteRemoteProjectSyncCommands(project);
6296
6401
  const archive = Bun.spawn(commands.tar, {
6402
+ signal: options.signal,
6297
6403
  stderr: "pipe",
6298
6404
  stdout: "pipe"
6299
6405
  });
6300
6406
  const upload = Bun.spawn(commands.remote, {
6407
+ signal: options.signal,
6301
6408
  stderr: "pipe",
6302
6409
  stdin: archive.stdout,
6303
6410
  stdout: "pipe"
@@ -6342,13 +6449,16 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6342
6449
  ],
6343
6450
  tar: ["tar", "-cf", "-", "-C", project.config.bundleDirectory, "."]
6344
6451
  };
6345
- }, syncAbsoluteRemoteMacReleaseInputs = async (project) => {
6452
+ }, syncAbsoluteRemoteMacReleaseInputs = async (project, options = {}) => {
6453
+ options.signal?.throwIfAborted();
6346
6454
  const commands = absoluteRemoteReleaseInputSyncCommands(project);
6347
6455
  const archive = Bun.spawn(commands.tar, {
6456
+ signal: options.signal,
6348
6457
  stderr: "pipe",
6349
6458
  stdout: "pipe"
6350
6459
  });
6351
6460
  const upload = Bun.spawn(commands.remote, {
6461
+ signal: options.signal,
6352
6462
  stderr: "pipe",
6353
6463
  stdin: archive.stdout,
6354
6464
  stdout: "pipe"
@@ -6440,114 +6550,176 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6440
6550
  }
6441
6551
  }, buildAbsoluteRemoteIosRelease = async (options) => {
6442
6552
  const startedAt = performance.now();
6443
- const syncStartedAt = performance.now();
6444
- await (options.syncProject ?? syncAbsoluteRemoteMacProject)(options.project);
6445
- await (options.syncReleaseInputs ?? syncAbsoluteRemoteMacReleaseInputs)(options.project);
6446
- const syncDuration = performance.now() - syncStartedAt;
6553
+ options.signal?.throwIfAborted();
6554
+ const operation2 = new AbortController;
6555
+ const abort = () => operation2.abort(options.signal?.reason ?? new Error("Remote iOS release cancelled."));
6556
+ options.signal?.addEventListener("abort", abort, { once: true });
6557
+ let lease;
6558
+ const leaseStartedAt = performance.now();
6559
+ try {
6560
+ lease = await (options.acquireLease ?? ((project, signal) => acquireAbsoluteRemoteMacReleaseLease(project, {
6561
+ signal,
6562
+ transport: options.transport
6563
+ })))(options.project, operation2.signal);
6564
+ } catch (error) {
6565
+ options.signal?.removeEventListener("abort", abort);
6566
+ throw error;
6567
+ }
6568
+ options.log?.(`${lease.recovered ? "Recovered stale" : "Acquired"} Remote Mac release lease for ${options.project.config.appId}.`);
6569
+ const leaseDuration = performance.now() - leaseStartedAt;
6447
6570
  options.onPhaseTiming?.({
6448
- durationMs: syncDuration,
6449
- phase: "remote-release-sync"
6571
+ durationMs: leaseDuration,
6572
+ phase: "remote-release-lease"
6450
6573
  });
6451
- const agentStartedAt = performance.now();
6452
- const agent = await (options.installAgent ?? installAbsoluteRemoteMacAgent)(options.project);
6453
- const agentDuration = performance.now() - agentStartedAt;
6454
- const encodedConfig = Buffer.from(JSON.stringify(portableMobileConfig(options.project))).toString("base64url");
6455
- const remoteCommand = [
6456
- `cd ${shellQuote(options.project.remoteProjectRoot)}`,
6457
- "&&",
6458
- "exec",
6459
- shellQuote(options.project.profile.bunPath),
6460
- shellQuote(agent.remotePath),
6461
- "--release-ios",
6462
- "--mobile-config",
6463
- shellQuote(encodedConfig),
6464
- ...options.allowUnsigned ? ["--unsigned"] : [],
6465
- ...options.prepareBuildNumber ? ["--request-build-number"] : [],
6466
- ...options.developmentTeam ? ["--development-team", shellQuote(options.developmentTeam)] : []
6467
- ].join(" ");
6468
- const process2 = (options.transport?.spawn ?? defaultTransport.spawn)([
6469
- ...absoluteRemoteMacSshBase(options.project.profile),
6470
- "/bin/sh -lc",
6471
- shellQuote(remoteCommand)
6472
- ], {});
6473
- let failure;
6474
- let metadata;
6475
- const responses = [];
6476
- const stdoutDone = consumeLines2(process2.stdout, (line) => {
6477
- if (!line.startsWith(ABSOLUTE_REMOTE_MAC_EVENT_PREFIX))
6574
+ let heartbeat;
6575
+ const heartbeatTimer = setInterval(() => {
6576
+ if (heartbeat)
6478
6577
  return;
6479
- try {
6480
- const event = JSON.parse(line.slice(ABSOLUTE_REMOTE_MAC_EVENT_PREFIX.length));
6481
- if (event.v !== ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION)
6482
- throw new Error("Remote Mac protocol version mismatch.");
6483
- if (event.type === "log")
6484
- options.log?.(event.message);
6485
- if (event.type === "timing") {
6486
- const durationMs = Reflect.get(event, "durationMs");
6487
- const phase = Reflect.get(event, "phase");
6488
- if (typeof durationMs === "number" && typeof phase === "string")
6489
- options.onPhaseTiming?.({ durationMs, phase });
6490
- }
6491
- if (event.type === "fatal")
6492
- failure = new Error(event.error);
6493
- if (event.type === "release")
6494
- metadata = requireAbsoluteIosReleaseMetadata(event.metadata);
6495
- if (event.type === "build-number-request") {
6496
- if (!options.prepareBuildNumber || !/^[a-f0-9]{64}$/u.test(event.buildIdentity) || !event.id)
6497
- throw new TypeError("Remote Mac requested an invalid iOS build number.");
6498
- responses.push(options.prepareBuildNumber(event.buildIdentity).then(async (buildNumber) => {
6499
- if (!Number.isSafeInteger(buildNumber) || buildNumber < 1)
6500
- throw new TypeError("iOS release publisher returned an invalid build number.");
6501
- process2.stdin.write(`${JSON.stringify({ buildNumber, command: "build-number", id: event.id, v: ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION })}
6578
+ heartbeat = lease.heartbeat().catch((error) => operation2.abort(error)).finally(() => {
6579
+ heartbeat = undefined;
6580
+ });
6581
+ }, REMOTE_RELEASE_LEASE_HEARTBEAT_MS);
6582
+ heartbeatTimer.unref();
6583
+ try {
6584
+ const syncStartedAt = performance.now();
6585
+ if (options.syncProject)
6586
+ await options.syncProject(options.project);
6587
+ else
6588
+ await syncAbsoluteRemoteMacProject(options.project, {
6589
+ signal: operation2.signal
6590
+ });
6591
+ operation2.signal.throwIfAborted();
6592
+ if (options.syncReleaseInputs)
6593
+ await options.syncReleaseInputs(options.project);
6594
+ else
6595
+ await syncAbsoluteRemoteMacReleaseInputs(options.project, {
6596
+ signal: operation2.signal
6597
+ });
6598
+ operation2.signal.throwIfAborted();
6599
+ const syncDuration = performance.now() - syncStartedAt;
6600
+ options.onPhaseTiming?.({
6601
+ durationMs: syncDuration,
6602
+ phase: "remote-release-sync"
6603
+ });
6604
+ const agentStartedAt = performance.now();
6605
+ const agent = await (options.installAgent ?? installAbsoluteRemoteMacAgent)(options.project);
6606
+ operation2.signal.throwIfAborted();
6607
+ const agentDuration = performance.now() - agentStartedAt;
6608
+ const encodedConfig = Buffer.from(JSON.stringify(portableMobileConfig(options.project))).toString("base64url");
6609
+ const remoteCommand = [
6610
+ `cd ${shellQuote(options.project.remoteProjectRoot)}`,
6611
+ "&&",
6612
+ "exec",
6613
+ shellQuote(options.project.profile.bunPath),
6614
+ shellQuote(agent.remotePath),
6615
+ "--release-ios",
6616
+ "--mobile-config",
6617
+ shellQuote(encodedConfig),
6618
+ ...options.allowUnsigned ? ["--unsigned"] : [],
6619
+ ...options.prepareBuildNumber ? ["--request-build-number"] : [],
6620
+ ...options.developmentTeam ? ["--development-team", shellQuote(options.developmentTeam)] : []
6621
+ ].join(" ");
6622
+ const process2 = (options.transport?.spawn ?? defaultTransport.spawn)([
6623
+ ...absoluteRemoteMacSshBase(options.project.profile),
6624
+ "/bin/sh -lc",
6625
+ shellQuote(remoteCommand)
6626
+ ], { signal: operation2.signal });
6627
+ const killOnAbort = () => process2.kill();
6628
+ operation2.signal.addEventListener("abort", killOnAbort, { once: true });
6629
+ let failure;
6630
+ let metadata;
6631
+ const responses = [];
6632
+ const stdoutDone = consumeLines2(process2.stdout, (line) => {
6633
+ if (!line.startsWith(ABSOLUTE_REMOTE_MAC_EVENT_PREFIX))
6634
+ return;
6635
+ try {
6636
+ const event = JSON.parse(line.slice(ABSOLUTE_REMOTE_MAC_EVENT_PREFIX.length));
6637
+ if (event.v !== ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION)
6638
+ throw new Error("Remote Mac protocol version mismatch.");
6639
+ if (event.type === "log")
6640
+ options.log?.(event.message);
6641
+ if (event.type === "timing") {
6642
+ const durationMs = Reflect.get(event, "durationMs");
6643
+ const phase = Reflect.get(event, "phase");
6644
+ if (typeof durationMs === "number" && typeof phase === "string")
6645
+ options.onPhaseTiming?.({ durationMs, phase });
6646
+ }
6647
+ if (event.type === "fatal")
6648
+ failure = new Error(event.error);
6649
+ if (event.type === "release")
6650
+ metadata = requireAbsoluteIosReleaseMetadata(event.metadata);
6651
+ if (event.type === "build-number-request") {
6652
+ if (!options.prepareBuildNumber || !/^[a-f0-9]{64}$/u.test(event.buildIdentity) || !event.id)
6653
+ throw new TypeError("Remote Mac requested an invalid iOS build number.");
6654
+ responses.push(options.prepareBuildNumber(event.buildIdentity).then(async (buildNumber) => {
6655
+ if (!Number.isSafeInteger(buildNumber) || buildNumber < 1)
6656
+ throw new TypeError("iOS release publisher returned an invalid build number.");
6657
+ process2.stdin.write(`${JSON.stringify({ buildNumber, command: "build-number", id: event.id, v: ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION })}
6502
6658
  `);
6503
- for (let attempt = 0;attempt < REMOTE_STDIN_FLUSH_ATTEMPTS; attempt++) {
6504
- try {
6505
- await process2.stdin.flush();
6506
- break;
6507
- } catch {
6508
- await Promise.resolve();
6659
+ for (let attempt = 0;attempt < REMOTE_STDIN_FLUSH_ATTEMPTS; attempt++) {
6660
+ try {
6661
+ await process2.stdin.flush();
6662
+ break;
6663
+ } catch {
6664
+ await Promise.resolve();
6665
+ }
6509
6666
  }
6510
- }
6511
- }).catch((error) => {
6512
- failure = error instanceof Error ? error : new Error("Failed to allocate an iOS build number.");
6513
- process2.kill();
6514
- return;
6515
- }));
6667
+ }).catch((error) => {
6668
+ failure = error instanceof Error ? error : new Error("Failed to allocate an iOS build number.");
6669
+ process2.kill();
6670
+ return;
6671
+ }));
6672
+ }
6673
+ } catch (error) {
6674
+ failure = error instanceof Error ? error : new Error("Remote Mac emitted an invalid release event.");
6675
+ process2.kill();
6676
+ }
6677
+ });
6678
+ const stderrDone = consumeLines2(process2.stderr, (line) => options.log?.(`[remote] ${line}`));
6679
+ const exitCode = await process2.exited;
6680
+ operation2.signal.removeEventListener("abort", killOnAbort);
6681
+ try {
6682
+ process2.stdin.end();
6683
+ } catch {}
6684
+ await Promise.all([stdoutDone, stderrDone, ...responses]);
6685
+ operation2.signal.throwIfAborted();
6686
+ if (failure)
6687
+ throw failure;
6688
+ if (exitCode !== 0)
6689
+ throw new Error(`Remote iOS release agent exited with status ${exitCode}.`);
6690
+ if (!metadata)
6691
+ throw new Error("Remote iOS release agent did not return an IPA.");
6692
+ const downloadStartedAt = performance.now();
6693
+ const release = await (options.retrieveRelease ?? retrieveAbsoluteRemoteIosRelease)(options.project, metadata, options.outputDirectory);
6694
+ const downloadDuration = performance.now() - downloadStartedAt;
6695
+ options.onPhaseTiming?.({
6696
+ durationMs: downloadDuration,
6697
+ phase: "remote-release-download"
6698
+ });
6699
+ options.log?.(`Remote Mac ${options.project.profile.name} built and verified iOS release ${metadata.releaseId} in ${(performance.now() - startedAt).toFixed(2)}ms (agent ${agent.uploaded ? "uploaded" : "cache hit"}).`);
6700
+ return {
6701
+ ...release,
6702
+ timings: {
6703
+ "remote-agent": agentDuration,
6704
+ "remote-release-download": downloadDuration,
6705
+ "remote-release-lease": leaseDuration,
6706
+ "remote-release-sync": syncDuration,
6707
+ total: performance.now() - startedAt
6516
6708
  }
6709
+ };
6710
+ } finally {
6711
+ clearInterval(heartbeatTimer);
6712
+ await heartbeat?.catch(() => {
6713
+ return;
6714
+ });
6715
+ options.signal?.removeEventListener("abort", abort);
6716
+ try {
6717
+ await lease.release();
6718
+ options.log?.("Released Remote Mac release lease.");
6517
6719
  } catch (error) {
6518
- failure = error instanceof Error ? error : new Error("Remote Mac emitted an invalid release event.");
6519
- process2.kill();
6520
- }
6521
- });
6522
- const stderrDone = consumeLines2(process2.stderr, (line) => options.log?.(`[remote] ${line}`));
6523
- const exitCode = await process2.exited;
6524
- try {
6525
- process2.stdin.end();
6526
- } catch {}
6527
- await Promise.all([stdoutDone, stderrDone, ...responses]);
6528
- if (failure)
6529
- throw failure;
6530
- if (exitCode !== 0)
6531
- throw new Error(`Remote iOS release agent exited with status ${exitCode}.`);
6532
- if (!metadata)
6533
- throw new Error("Remote iOS release agent did not return an IPA.");
6534
- const downloadStartedAt = performance.now();
6535
- const release = await (options.retrieveRelease ?? retrieveAbsoluteRemoteIosRelease)(options.project, metadata, options.outputDirectory);
6536
- const downloadDuration = performance.now() - downloadStartedAt;
6537
- options.onPhaseTiming?.({
6538
- durationMs: downloadDuration,
6539
- phase: "remote-release-download"
6540
- });
6541
- options.log?.(`Remote Mac ${options.project.profile.name} built and verified iOS release ${metadata.releaseId} in ${(performance.now() - startedAt).toFixed(2)}ms (agent ${agent.uploaded ? "uploaded" : "cache hit"}).`);
6542
- return {
6543
- ...release,
6544
- timings: {
6545
- "remote-agent": agentDuration,
6546
- "remote-release-download": downloadDuration,
6547
- "remote-release-sync": syncDuration,
6548
- total: performance.now() - startedAt
6720
+ options.log?.(`Remote Mac release lease cleanup failed and will recover after expiry: ${error instanceof Error ? error.message : String(error)}`);
6549
6721
  }
6550
- };
6722
+ }
6551
6723
  }, startAbsoluteRemoteExpoIosDevSession = async (options) => {
6552
6724
  const session = await startAbsoluteRemoteDevSession({
6553
6725
  certificateAuthorityPath: options.certificateAuthorityPath,
@@ -22251,6 +22423,32 @@ var NOT_FOUND4 = -1, isRecord17 = (value) => typeof value === "object" && value
22251
22423
  });
22252
22424
  console.log(`Paired remote Mac ${profile.name} (${profile.xcodeVersion}) and selected it as the default iOS development host.`);
22253
22425
  }, listRemoteMacs = async (args) => {
22426
+ if (args[0] === "inspect") {
22427
+ const requested = args[1]?.startsWith("-") ? undefined : args[1];
22428
+ const profile = await getAbsoluteRemoteMacProfile(requested, remoteProfilePath());
22429
+ if (!profile)
22430
+ throw new TypeError("No Remote Mac is selected. Pair one before inspecting its workspace.");
22431
+ const inspection = await inspectAbsoluteRemoteMacWorkspace(profile);
22432
+ console.log(args.includes("--json") ? JSON.stringify(inspection, null, 2) : [
22433
+ `Remote Mac: ${inspection.profile}`,
22434
+ `Workspace: ${inspection.workspaceRoot}`,
22435
+ `Cache: ${(inspection.bytes / 1048576).toFixed(1)} MiB across ${inspection.projectCount} project(s) and ${inspection.agentCount} agent artifact(s)`,
22436
+ `Active release leases: ${inspection.leaseCount}`
22437
+ ].join(`
22438
+ `));
22439
+ return;
22440
+ }
22441
+ if (args[0] === "clean") {
22442
+ if (!args.includes("--yes"))
22443
+ throw new TypeError("Remote Mac cleanup requires --yes. It removes only abandoned staging directories older than one day.");
22444
+ const requested = args[1]?.startsWith("-") ? undefined : args[1];
22445
+ const profile = await getAbsoluteRemoteMacProfile(requested, remoteProfilePath());
22446
+ if (!profile)
22447
+ throw new TypeError("No Remote Mac is selected. Pair one before cleaning its workspace.");
22448
+ const result2 = await cleanAbsoluteRemoteMacWorkspace(profile);
22449
+ console.log(`Removed ${result2.removed} abandoned Remote Mac staging director${result2.removed === 1 ? "y" : "ies"}; project caches, releases, and active leases were retained.`);
22450
+ return;
22451
+ }
22254
22452
  const result = await listAbsoluteRemoteMacProfiles(remoteProfilePath());
22255
22453
  if (args.includes("--json")) {
22256
22454
  console.log(JSON.stringify(result, null, 2));
@@ -22748,6 +22946,18 @@ Mobile release security and compliance checks failed.`);
22748
22946
  ...options.prepareBuildNumber === undefined ? {} : { prepareBuildNumber: options.prepareBuildNumber },
22749
22947
  projectRoot: options.projectRoot
22750
22948
  });
22949
+ }, listenForRemoteReleaseCancellation = (cancellation) => {
22950
+ if (!cancellation)
22951
+ return () => {
22952
+ return;
22953
+ };
22954
+ const cancel = () => cancellation.abort(new Error("Remote iOS release interrupted."));
22955
+ process.once("SIGINT", cancel);
22956
+ process.once("SIGTERM", cancel);
22957
+ return () => {
22958
+ process.removeListener("SIGINT", cancel);
22959
+ process.removeListener("SIGTERM", cancel);
22960
+ };
22751
22961
  }, buildIos = async (args, prepareBuildNumber) => {
22752
22962
  const configPath2 = valueAfter(args, "--config");
22753
22963
  const { mobile, projectRoot } = await loadMobile(configPath2);
@@ -22762,10 +22972,15 @@ Mobile release security and compliance checks failed.`);
22762
22972
  if (process.platform !== "darwin" && !remoteProfile)
22763
22973
  throw new TypeError("iOS release builds require macOS or a paired Remote Mac. Run `absolute mobile pair mac <name> <user@host>`.");
22764
22974
  let success = false;
22975
+ const cancellation = remoteProfile ? new AbortController : undefined;
22976
+ let stopListeningForCancellation = () => {
22977
+ return;
22978
+ };
22765
22979
  try {
22766
22980
  if (!remoteProfile && mobile.engine === "capacitor")
22767
22981
  await repairAbsoluteIosDevSession(projectRoot);
22768
22982
  await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath2, { prepareOnly: true });
22983
+ stopListeningForCancellation = listenForRemoteReleaseCancellation(cancellation);
22769
22984
  const release = remoteProfile ? await buildAbsoluteRemoteIosRelease({
22770
22985
  allowUnsigned: args.includes("--unsigned"),
22771
22986
  developmentTeam: process.env.ABSOLUTE_IOS_DEVELOPMENT_TEAM,
@@ -22782,7 +22997,8 @@ Mobile release security and compliance checks failed.`);
22782
22997
  });
22783
22998
  },
22784
22999
  ...prepareBuildNumber === undefined ? {} : { prepareBuildNumber },
22785
- project: createAbsoluteRemoteIosDevProject(mobile, projectRoot, remoteProfile)
23000
+ project: createAbsoluteRemoteIosDevProject(mobile, projectRoot, remoteProfile),
23001
+ signal: cancellation?.signal
22786
23002
  }) : await buildLocalIosRelease({
22787
23003
  args,
22788
23004
  mobile,
@@ -22796,6 +23012,7 @@ Mobile release security and compliance checks failed.`);
22796
23012
  console.log(`Metadata: ${join56(release.releaseRoot, "release.json")}`);
22797
23013
  return release;
22798
23014
  } finally {
23015
+ stopListeningForCancellation();
22799
23016
  sendTelemetryEvent("mobile:ios-release-build", {
22800
23017
  durationMs: Math.round(performance.now() - startedAt),
22801
23018
  engine: mobile.engine,
@@ -23722,7 +23939,7 @@ Emulator setup verification:`);
23722
23939
  await publishIos(args.slice(2));
23723
23940
  return;
23724
23941
  }
23725
- throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [--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] | 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 [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--device identifier [--remote name] | --udid id] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--artifacts dir] [--json]> [--config path]");
23942
+ 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] | 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 [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--device identifier [--remote name] | --udid id] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--artifacts dir] [--json]> [--config path]");
23726
23943
  };
23727
23944
  var init_mobile = __esm(() => {
23728
23945
  init_dependencies();