@absolutejs/absolute 0.20.0-beta.51 → 0.20.0-beta.53

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-K4oNvN/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-Ul3n0I/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-K4oNvN/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-Ul3n0I/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-K4oNvN/src/core/streamingSlotRegistry.ts
51
+ // .angular-partial-tmp-Ul3n0I/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,
@@ -18403,6 +18575,10 @@ console.log(\`
18403
18575
  const resolvedOutdir = resolve36(outdir ?? "dist");
18404
18576
  await withBuildDirectoryLock(resolvedOutdir, () => compileUnlocked(serverEntry, resolvedOutdir, outfile, configPath2));
18405
18577
  }, compileUnlocked = async (serverEntry, resolvedOutdir, outfile, configPath2) => {
18578
+ rmSync7(join47(resolvedOutdir, "node_modules"), {
18579
+ force: true,
18580
+ recursive: true
18581
+ });
18406
18582
  const configuredPrerenderPort = env5.COMPILE_PORT === undefined ? Number(env5.PORT) : Number(env5.COMPILE_PORT);
18407
18583
  const prerenderPort = configuredPrerenderPort > 0 ? configuredPrerenderPort : await findFreePort();
18408
18584
  killStaleProcesses(prerenderPort);
@@ -22247,6 +22423,32 @@ var NOT_FOUND4 = -1, isRecord17 = (value) => typeof value === "object" && value
22247
22423
  });
22248
22424
  console.log(`Paired remote Mac ${profile.name} (${profile.xcodeVersion}) and selected it as the default iOS development host.`);
22249
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
+ }
22250
22452
  const result = await listAbsoluteRemoteMacProfiles(remoteProfilePath());
22251
22453
  if (args.includes("--json")) {
22252
22454
  console.log(JSON.stringify(result, null, 2));
@@ -22744,6 +22946,18 @@ Mobile release security and compliance checks failed.`);
22744
22946
  ...options.prepareBuildNumber === undefined ? {} : { prepareBuildNumber: options.prepareBuildNumber },
22745
22947
  projectRoot: options.projectRoot
22746
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
+ };
22747
22961
  }, buildIos = async (args, prepareBuildNumber) => {
22748
22962
  const configPath2 = valueAfter(args, "--config");
22749
22963
  const { mobile, projectRoot } = await loadMobile(configPath2);
@@ -22758,10 +22972,15 @@ Mobile release security and compliance checks failed.`);
22758
22972
  if (process.platform !== "darwin" && !remoteProfile)
22759
22973
  throw new TypeError("iOS release builds require macOS or a paired Remote Mac. Run `absolute mobile pair mac <name> <user@host>`.");
22760
22974
  let success = false;
22975
+ const cancellation = remoteProfile ? new AbortController : undefined;
22976
+ let stopListeningForCancellation = () => {
22977
+ return;
22978
+ };
22761
22979
  try {
22762
22980
  if (!remoteProfile && mobile.engine === "capacitor")
22763
22981
  await repairAbsoluteIosDevSession(projectRoot);
22764
22982
  await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath2, { prepareOnly: true });
22983
+ stopListeningForCancellation = listenForRemoteReleaseCancellation(cancellation);
22765
22984
  const release = remoteProfile ? await buildAbsoluteRemoteIosRelease({
22766
22985
  allowUnsigned: args.includes("--unsigned"),
22767
22986
  developmentTeam: process.env.ABSOLUTE_IOS_DEVELOPMENT_TEAM,
@@ -22778,7 +22997,8 @@ Mobile release security and compliance checks failed.`);
22778
22997
  });
22779
22998
  },
22780
22999
  ...prepareBuildNumber === undefined ? {} : { prepareBuildNumber },
22781
- project: createAbsoluteRemoteIosDevProject(mobile, projectRoot, remoteProfile)
23000
+ project: createAbsoluteRemoteIosDevProject(mobile, projectRoot, remoteProfile),
23001
+ signal: cancellation?.signal
22782
23002
  }) : await buildLocalIosRelease({
22783
23003
  args,
22784
23004
  mobile,
@@ -22792,6 +23012,7 @@ Mobile release security and compliance checks failed.`);
22792
23012
  console.log(`Metadata: ${join56(release.releaseRoot, "release.json")}`);
22793
23013
  return release;
22794
23014
  } finally {
23015
+ stopListeningForCancellation();
22795
23016
  sendTelemetryEvent("mobile:ios-release-build", {
22796
23017
  durationMs: Math.round(performance.now() - startedAt),
22797
23018
  engine: mobile.engine,
@@ -23718,7 +23939,7 @@ Emulator setup verification:`);
23718
23939
  await publishIos(args.slice(2));
23719
23940
  return;
23720
23941
  }
23721
- 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]");
23722
23943
  };
23723
23944
  var init_mobile = __esm(() => {
23724
23945
  init_dependencies();