stim 1.25.0 → 1.27.0

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 (182) hide show
  1. package/README.md +1 -1
  2. package/dist/{acquire-artifact-zA4S7RHE.mjs → acquire-artifact-BKN2Yuhg.mjs} +2 -2
  3. package/dist/{act-BbHS3oV8.mjs → act-ROviTF_t.mjs} +415 -118
  4. package/dist/{activity-BpjwrQXU.mjs → activity-Cd-ysFes.mjs} +6 -7
  5. package/dist/{agent-device-usage-output-Bnkerfod.mjs → agent-device-usage-output-C5nCKWp9.mjs} +5 -5
  6. package/dist/{android-CNZgRC-4.mjs → android--HBgpvpW.mjs} +5 -5
  7. package/dist/{android-BGwuK3Mc.mjs → android-Cd6w7_qw.mjs} +134 -57
  8. package/dist/{android-saBeB4lr.mjs → android-Deb9X3_Y.mjs} +37 -15
  9. package/dist/{android-Cjcj8MCw.mjs → android-DriNoAVy.mjs} +99 -47
  10. package/dist/{android-cas-BVqh2Zep.mjs → android-cas-D5e6ecUg.mjs} +4 -4
  11. package/dist/android-cas-compiler.mjs +1 -1
  12. package/dist/{android-project-CTYaETc0.d.mts → android-project-DUGMbq28.d.mts} +55 -1
  13. package/dist/api.d.mts +20 -7
  14. package/dist/api.mjs +42 -32
  15. package/dist/apk-signed-tail-BHQHx3Rm.mjs +142 -0
  16. package/dist/{artifact-Co8zixVx.mjs → artifact-0AGJYB9O.mjs} +28 -28
  17. package/dist/{artifact-DN8JNwqS.mjs → artifact-faemIZv5.mjs} +40 -30
  18. package/dist/{artifact-platform-CMRc0M_r.mjs → artifact-platform-BO2Uvk_A.mjs} +1 -1
  19. package/dist/{attempt-DW-mQqM7.mjs → attempt-ClyJlZPT.mjs} +4 -2
  20. package/dist/auto-placement-z-H5pJza.mjs +350 -0
  21. package/dist/{browser-web-CCgZsHmI.mjs → browser-web-slD8AvLp.mjs} +16 -17
  22. package/dist/{build-cache-CKCzxzxA.mjs → build-cache-gBJ-WKzA.mjs} +141 -37
  23. package/dist/{build-CAiw8Ezm.mjs → build-hS0QyFlQ.mjs} +38 -31
  24. package/dist/{build-plan-zCuHGlA8.mjs → build-plan-CBqYbzYM.mjs} +7 -6
  25. package/dist/{cache-manifest-BPXRONJz.mjs → cache-manifest-DEjqGGat.mjs} +1 -1
  26. package/dist/cache-manifest.mjs +1 -1
  27. package/dist/{budget-HyShrIpL.mjs → ccache-BWTze1Ls.mjs} +634 -1514
  28. package/dist/{cdp-BG3FJCCx.mjs → cdp-C3svvXiE.mjs} +1 -1
  29. package/dist/{chrome-BvV8ssI3.mjs → chrome-DyDWurWG.mjs} +1 -1
  30. package/dist/{cli-C4w607Pz.mjs → cli-ftXDLto1.mjs} +24 -23
  31. package/dist/cli.mjs +1 -1
  32. package/dist/{client-BQsmyp_-.mjs → client-xsdxrhWA.mjs} +77 -116
  33. package/dist/collector-run.d.mts +10 -9
  34. package/dist/collector-run.mjs +42 -27
  35. package/dist/{command-output-BBvS1H6v.mjs → command-output-Dj-WT4BP.mjs} +1 -1
  36. package/dist/{config-BZhXY15H.mjs → config-Cn94Jc0g.mjs} +343 -18
  37. package/dist/{launch-DscF1QfM.mjs → dependencies-BGokZ-Ng.mjs} +330 -279
  38. package/dist/{dependency-state-BwNCYG5Q.mjs → dependency-state-C3OtgG94.mjs} +14 -7
  39. package/dist/{detached-entry-KmieOKgN.mjs → detached-entry-BPlSaCwC.mjs} +1 -1
  40. package/dist/{dev-client-CHoXXZeg.mjs → dev-client-BMca82xo.mjs} +2 -2
  41. package/dist/{device-F5-4z9ef.mjs → device-BJm0irrq.mjs} +16 -16
  42. package/dist/{device-capacity-ZYaRALv5.mjs → device-capacity-6LP7QsaA.mjs} +103 -25
  43. package/dist/device-host-worker.mjs +19 -21
  44. package/dist/{device-ios-DkHBrmvH.mjs → device-ios-534c14fz.mjs} +288 -28
  45. package/dist/{device-lease-run-ogEeJ62K.mjs → device-lease-run-DwsGfjUN.mjs} +5 -5
  46. package/dist/{device-pool-BIg8MU8l.mjs → device-pool-BnQqvs6o.mjs} +3 -3
  47. package/dist/{device-B9FqTBYW.mjs → device-tM41-TUq.mjs} +25 -24
  48. package/dist/{doctor-A_Nr_fDe.mjs → doctor-Dbx1ZpD2.mjs} +1 -1
  49. package/dist/{doctor-k3P3PizH.mjs → doctor-DyqE9n1J.mjs} +28 -16
  50. package/dist/{doctor-Bm8TVpGn.mjs → doctor-F-3VUUyN.mjs} +128 -35
  51. package/dist/{doctor-fingerprint-zlujA1Gs.mjs → doctor-fingerprint-o7ZI9YLn.mjs} +9 -3
  52. package/dist/doctor-fingerprint.mjs +1 -1
  53. package/dist/{doctor-watchman-BovI1y3-.mjs → doctor-watchman-CxDbyrrt.mjs} +5 -5
  54. package/dist/{eas-build-CyDKNTBP.mjs → eas-build-CjNSWmjo.mjs} +4 -4
  55. package/dist/eas-fallback-9fqb46kW.mjs +87 -0
  56. package/dist/{eas-session-ledger-BMCA1gdO.mjs → eas-session-ledger-DrP4OuuM.mjs} +6 -6
  57. package/dist/{error-diagnostics-B1Mj_-7h.mjs → error-diagnostics-snej9k4j.mjs} +51 -25
  58. package/dist/{exec-VFRpu1iA.mjs → exec-aULwtNly.mjs} +57 -6
  59. package/dist/{gc-eaz403W6.mjs → gc-BRwAY3qT.mjs} +2 -2
  60. package/dist/{gc-B9jcnmIk.mjs → gc-CGZ1Yg88.mjs} +218 -72
  61. package/dist/{guide-DR0K0xW9.mjs → guide-_rfEh_zM.mjs} +839 -222
  62. package/dist/{hosted-android-Bvb2-ZxD.mjs → hosted-android-BMKzUNnY.mjs} +4 -4
  63. package/dist/{hosted-client-rsRqysJc.mjs → hosted-client-D7Pk_Oo1.mjs} +13 -9
  64. package/dist/{hosted-logs-CWKGYWNX.mjs → hosted-logs-B1gqQuva.mjs} +18 -12
  65. package/dist/hosted-logs-sync-Dg3qrZmV.mjs +195 -0
  66. package/dist/{hosted-macos-d0pv9Tth.mjs → hosted-macos-B9w4H8hc.mjs} +6 -6
  67. package/dist/{hosted-native-BxTizGU2.mjs → hosted-native-CdgaiuyP.mjs} +17 -17
  68. package/dist/{idle-shutdown-BIyfl0Zj.mjs → idle-shutdown-Cbvo5KEh.mjs} +21 -9
  69. package/dist/{ios-BRVM8b0P.mjs → ios-B5HlVMhQ.mjs} +138 -55
  70. package/dist/{ios-Dvt97HgQ.mjs → ios-Bk4mWbtS.mjs} +8 -8
  71. package/dist/{ios-ENC4sRPA.mjs → ios-CsRVik64.mjs} +7 -1
  72. package/dist/{ios-BQzvJlzw.mjs → ios-E26qSpkb.mjs} +94 -11
  73. package/dist/{ios-device-CKSvk0bu.mjs → ios-device-C_QkIf1Z.mjs} +3 -3
  74. package/dist/{ios-device-B6HK951C.mjs → ios-device-CiBA-er9.mjs} +1 -1
  75. package/dist/{ios-project-DCUDo06I.mjs → ios-project-D8Jcy_br.mjs} +22 -9
  76. package/dist/{ios-signing-4c_qXvQm.mjs → ios-signing-BJzvDPfU.mjs} +2 -2
  77. package/dist/{ios-state-CT9nV5SP.mjs → ios-state-BZ03NFgF.mjs} +1 -1
  78. package/dist/{js-swap-Z9HbC4cj.mjs → js-swap-Dg2a945i.mjs} +8 -7
  79. package/dist/{launch-D0lPvFFu.mjs → launch-CRyBeNsA.mjs} +53 -44
  80. package/dist/lightened-BCsdAGj_.mjs +171 -0
  81. package/dist/{logs-BsIRre8x.mjs → logs-CC_-ZzEE.mjs} +10 -174
  82. package/dist/{logs-Crs9y34Z.mjs → logs-aJQb-RDg.mjs} +112 -38
  83. package/dist/{macos-CcUlJPMu.mjs → macos-BGmTpnv9.mjs} +1 -1
  84. package/dist/{macos-D477nfzH.mjs → macos-DlCBhMw5.mjs} +73 -39
  85. package/dist/macos-run.mjs +1 -1
  86. package/dist/maintenance-run.mjs +30 -13
  87. package/dist/{metro-1pJgCX4U.mjs → metro-CvyD-xwe.mjs} +4 -4
  88. package/dist/{metro-gateway-Bp9gJQAN.mjs → metro-gateway-BnHdqMO_.mjs} +3 -4
  89. package/dist/{named-ports-BVbn8SvC.mjs → named-ports-BCyMWA6b.mjs} +3 -3
  90. package/dist/{native-android-BvMizJn1.mjs → native-android-DlpxCyOP.mjs} +17 -18
  91. package/dist/{native-gradle-artifact-inputs-BJscK4vN.mjs → native-gradle-artifact-inputs-C13qT7R_.mjs} +5 -5
  92. package/dist/{native-runtime-B9w_Qyz3.mjs → native-runtime-DqaIBO5w.mjs} +9 -9
  93. package/dist/{native-source-D-4Dh4JU.mjs → native-source-D1AzWiuP.mjs} +1 -1
  94. package/dist/{native-xcode-inputs-Cv4l-obn.mjs → native-xcode-inputs-B1W8SrxB.mjs} +3 -3
  95. package/dist/{native-xcode-ios-DGrg90hE.mjs → native-xcode-ios-QPP0Rm68.mjs} +17 -18
  96. package/dist/{ndjson-CgE_o3Dl.mjs → ndjson-Dy2W4vRa.mjs} +2 -2
  97. package/dist/offload-worker.d.mts +3 -1
  98. package/dist/offload-worker.mjs +34 -25
  99. package/dist/{operation-exec-CUZifp6Z.mjs → operation-exec-Dp4FuIGR.mjs} +1 -1
  100. package/dist/{ownership-jxxz-19m.mjs → ownership-Bx-0cku-.mjs} +3 -3
  101. package/dist/{page-CzEA_-5_.mjs → page-CQJlcj3x.mjs} +2 -2
  102. package/dist/{phone-reverses-CYb7RRAx.mjs → phone-reverses-C9yAaKEv.mjs} +18 -4
  103. package/dist/{placement-log-DjOXJRIH.mjs → placement-log-BXUbiZM6.mjs} +3 -3
  104. package/dist/{plan-placement-BqKgKHCi.mjs → plan-placement-UxnQjv5Z.mjs} +36 -10
  105. package/dist/{ports-DtS1Amb0.mjs → ports-uUrghqWN.mjs} +3 -3
  106. package/dist/prebuild-BDpToDgR.mjs +1246 -0
  107. package/dist/{project-BiC68mIp.mjs → project-DYj-HVx6.mjs} +17 -18
  108. package/dist/{pull-request-CbNlkFok.mjs → pull-request-BzLmzAIQ.mjs} +2 -2
  109. package/dist/pull-requests.mjs +1 -1
  110. package/dist/{react-native-android-DRYqoJc4.mjs → react-native-android-DDBccgJw.mjs} +56 -36
  111. package/dist/{react-native-build-CwZB-agu.mjs → react-native-build-DaK2IFyP.mjs} +9 -9
  112. package/dist/{react-native-doctor-BCrytK7h.mjs → react-native-doctor-B-Mw6DcR.mjs} +50 -9
  113. package/dist/{react-native-ios-Cr1buNFo.mjs → react-native-ios-DjYwUF9U.mjs} +45 -25
  114. package/dist/{recordings-BaoQ0t-J.mjs → recordings-C20iUZfp.mjs} +1 -1
  115. package/dist/{registration-CN6nlhcD.mjs → registration-BwCe64PH.mjs} +1 -1
  116. package/dist/{reload-Beftw2X3.mjs → reload-DJBeF_rC.mjs} +1 -1
  117. package/dist/{reload-FbSgMF8x.mjs → reload-fUdkH2_p.mjs} +16 -16
  118. package/dist/{remote-BuZj6AyU.mjs → remote-y6n99WMW.mjs} +22 -9
  119. package/dist/{result-CR2vk2cj.mjs → result-CTqBw2Te.mjs} +32 -21
  120. package/dist/{run-BtzE09Dl.mjs → run-DaCeNKRm.mjs} +3 -3
  121. package/dist/{selection-D5cxEl3t.mjs → selection-Bs8phwhN.mjs} +183 -62
  122. package/dist/{server-bare-CH9X0RLn.mjs → server-bare-BOh-iZEj.mjs} +2 -2
  123. package/dist/{server-command-CaBbDNU2.mjs → server-command-8CuA51Cv.mjs} +6 -6
  124. package/dist/{server-expo-Ck8EIidM.mjs → server-expo-ABQs2XFA.mjs} +4 -4
  125. package/dist/server-expo-Ba_Y21vB.mjs +2 -0
  126. package/dist/{settings-B5tCoq9N.mjs → settings-BDVI8Ivc.mjs} +331 -62
  127. package/dist/{settings-DzH9mGQZ.mjs → settings-CMHhQNwI.mjs} +24 -3
  128. package/dist/settings-payload-cm60Zhib.mjs +257 -0
  129. package/dist/settings-write-BdNRLlBn.mjs +185 -0
  130. package/dist/settings.schema.json +84 -39
  131. package/dist/{simslim-BjxszUAb.mjs → simslim-DCQwzoPy.mjs} +4 -3
  132. package/dist/{slot-launch-QeuJMlnJ.mjs → slot-launch-0l7RqSpr.mjs} +6 -6
  133. package/dist/{stage-D8xlRU2D.mjs → stage-F4cAfJGi.mjs} +2 -2
  134. package/dist/{start-C1JLjkNw.mjs → start-DcqnF9p8.mjs} +1 -1
  135. package/dist/{start-YMpNwLhh.mjs → start-DrPwER_2.mjs} +27 -23
  136. package/dist/{state-DMRNP7-4.mjs → state-BX1y5syV.mjs} +4 -5
  137. package/dist/{state-DPaM3u5K.mjs → state-CZxLyetD.mjs} +2 -3
  138. package/dist/{stats-CP7STzZw.mjs → stats-GxuCbles.mjs} +5 -5
  139. package/dist/{stats-report-n2U5plhk.mjs → stats-report-B86Cv7Uc.mjs} +1 -1
  140. package/dist/{status-Fect2w9R.mjs → status-7ULDEJ1e.mjs} +1 -1
  141. package/dist/{status-CRLiJ-O_.mjs → status-BfnK9pUd.mjs} +25 -26
  142. package/dist/{status-cNw6i0_t.mjs → status-Bj7ZYvvc.mjs} +1 -1
  143. package/dist/{status-watch-Bc2J4TZn.mjs → status-watch-BfabQn03.mjs} +5 -5
  144. package/dist/status-worker.mjs +1 -1
  145. package/dist/{stim-desktop-DS0mzR8U.mjs → stim-desktop-CbWo21kb.mjs} +2 -2
  146. package/dist/{stop-COP6PQqw.mjs → stop-CH79S3IZ.mjs} +5 -6
  147. package/dist/{stop-Db9Kh-UA.mjs → stop-D-EAr25B.mjs} +25 -22
  148. package/dist/{stop-Br7LKxBE.mjs → stop-x1Yu3He4.mjs} +2 -2
  149. package/dist/supervisor-run.d.mts +1 -0
  150. package/dist/supervisor-run.mjs +17 -18
  151. package/dist/{support-qKcbgmI5.mjs → support-Be0UeVZU.mjs} +10 -10
  152. package/dist/{support-Cxj9CmII.mjs → support-CNg_YsOk.mjs} +6 -6
  153. package/dist/{swiftpm-artifact-inputs-iY9fsmOc.mjs → swiftpm-artifact-inputs-uPqYvGBv.mjs} +4 -4
  154. package/dist/{swiftpm-macos-Bp_sn9P4.mjs → swiftpm-macos-Ch4YjY_A.mjs} +11 -12
  155. package/dist/toolchain-CbCG6C5Z.mjs +2413 -0
  156. package/dist/toolchain-JYAOquPg.mjs +2 -0
  157. package/dist/{trigger-CBMe2YLH.mjs → trigger-DMou-X7j.mjs} +3 -3
  158. package/dist/trigger-qp2ji5wE.mjs +2 -0
  159. package/dist/{warm-progress-BV3ZPmcY.mjs → warm-progress-C0RsbngN.mjs} +731 -728
  160. package/dist/{web-B4Yjyt1G.mjs → web-Cq_AQ4DK.mjs} +9 -10
  161. package/dist/web-Je6abcpz.mjs +2 -0
  162. package/dist/web-run.mjs +9 -9
  163. package/dist/windows-report.mjs +1 -1
  164. package/dist/{workspace-state-CPSp3mav.mjs → workspace-state-BjqdjnJR.mjs} +46 -8
  165. package/dist/{workspaces-C9w4aeXF.mjs → workspaces-DFbj07jp.mjs} +1801 -76
  166. package/dist/{worktree-CclFIe1Z.mjs → worktree-COAJ3-OD.mjs} +934 -511
  167. package/dist/{worktree-Ct9Gk3KL.mjs → worktree-JGUd_53L.mjs} +1 -1
  168. package/package.json +8 -8
  169. package/dist/ccache-h1FKhZeg.mjs +0 -143
  170. package/dist/created-devices-D2Aca-xu.mjs +0 -52
  171. package/dist/eas-fallback-gaZi-dJL.mjs +0 -381
  172. package/dist/hosted-ios-RtVcJH3u.mjs +0 -15
  173. package/dist/native-run-D5OJsGwA.mjs +0 -276
  174. package/dist/paths-DQtTgsBf.mjs +0 -42
  175. package/dist/prebuild-JUaqOLpc.mjs +0 -501
  176. package/dist/server-expo-C16Zf2k7.mjs +0 -2
  177. package/dist/settings-write-n0zwfohx.mjs +0 -360
  178. package/dist/toolchain-DB1aeqxO.mjs +0 -2
  179. package/dist/toolchain-VzLJzZ7O.mjs +0 -908
  180. package/dist/trigger-BLsHgAGR.mjs +0 -2
  181. package/dist/web-DS4UDWvf.mjs +0 -2
  182. package/dist/xcode-CiIIDnuD.mjs +0 -910
@@ -0,0 +1,350 @@
1
+ import { _ as loadConfig$1, p as getProject } from "./config-Cn94Jc0g.mjs";
2
+ import { m as peekBudget } from "./ccache-BWTze1Ls.mjs";
3
+ import { z as readHostMemoryPressureAsync } from "./ios-E26qSpkb.mjs";
4
+ import { _ as hostingMachines, o as namedBuildMachine, u as resolveBuildMachine } from "./selection-Bs8phwhN.mjs";
5
+ import { c as call, l as connectHost, p as requireHostedAppMode } from "./hosted-client-D7Pk_Oo1.mjs";
6
+ import { c as writeHostedNativeAsync, r as readHostedNative } from "./ios-state-BZ03NFgF.mjs";
7
+ import { i as prepareHostedNative } from "./hosted-native-CdgaiuyP.mjs";
8
+ import { l as peekDeviceSlots, t as DEFAULT_DEVICE_SLOT_WAIT_MS } from "./device-capacity-6LP7QsaA.mjs";
9
+ import { ambientEnv } from "@stim-cli/core";
10
+ import { automaticMachineEnabled, isJsonObject, machineCapacity, mayRunHere, parseHostedChoice, parseHostedNativeOffer, prefersRemote } from "@stim-cli/core/state";
11
+ import { setTimeout } from "node:timers/promises";
12
+ //#region src/device-host/placement.ts
13
+ function local$1(code, reason, skipped = [], atCapacity = false) {
14
+ return {
15
+ kind: "local",
16
+ code,
17
+ reason,
18
+ skipped,
19
+ ...atCapacity ? { atCapacity: true } : {}
20
+ };
21
+ }
22
+ function decideDevicePlacement(inputs) {
23
+ if ("sticky" in inputs) {
24
+ if ("machine" in inputs.sticky) {
25
+ const reason = `recorded session on ${inputs.sticky.machine}`;
26
+ return {
27
+ kind: "host",
28
+ code: "sticky",
29
+ reason,
30
+ machines: [{
31
+ machine: inputs.sticky.machine,
32
+ reason
33
+ }],
34
+ skipped: []
35
+ };
36
+ }
37
+ return {
38
+ kind: "local",
39
+ code: "sticky",
40
+ reason: "this workspace's device runs here",
41
+ skipped: []
42
+ };
43
+ }
44
+ const { platform, placement = "auto", here, offers, buildMachine, noWait, eas } = inputs;
45
+ const remoteFirst = prefersRemote(placement);
46
+ const { count, max, queued } = here.devices;
47
+ const room = max === 0 || count !== null && count < max;
48
+ const localEnabled = here.localEnabled !== false && mayRunHere(placement);
49
+ const cannotTake = !localEnabled || !room || queued > 0;
50
+ const atCapacity = count !== null && (!room || queued > 0);
51
+ const usage = max > 0 ? `${count} of ${max} devices in use` : `${count} devices in use, no device cap`;
52
+ const localReason = !mayRunHere(placement) ? "remote-only never runs here" : !localEnabled ? "local is disabled in remote.devicePoolDisabled" : count === null ? "cannot tell how many devices run here; local boot admission decides" : cannotTake ? `${usage}${queued ? `, ${queued} runs queued ahead` : ""}` : here.budgetRefusal ?? (here.memoryPressure !== "normal" ? `host memory pressure ${here.memoryPressure ?? "unknown"} here` : `load ${here.loadPerCore.toFixed(1)}/core here, ${usage}`);
53
+ let hostsFull = false;
54
+ const nowhere = (code, reason, skipped = [], waiting = "") => {
55
+ if (!mayRunHere(placement)) return {
56
+ kind: "refused",
57
+ code: "pool-unavailable",
58
+ reason,
59
+ skipped,
60
+ ...hostsFull ? { hostsFull: true } : {}
61
+ };
62
+ if (!localEnabled && !atCapacity) return {
63
+ kind: "refused",
64
+ code: "pool-unavailable",
65
+ reason,
66
+ skipped
67
+ };
68
+ if (!cannotTake || count === null) return local$1(code, reason + waiting, skipped);
69
+ if (eas?.usable) return {
70
+ kind: "eas",
71
+ code: "eas-fallback",
72
+ reason: `${reason}; EAS Simulator (remote.easFallback)`,
73
+ skipped
74
+ };
75
+ const why = eas ? [...skipped, {
76
+ machine: "eas",
77
+ code: eas.code,
78
+ reason: eas.reason
79
+ }] : skipped;
80
+ return localEnabled ? local$1(code, reason + waiting, why, true) : {
81
+ kind: "refused",
82
+ code: "pool-unavailable",
83
+ reason,
84
+ skipped: why,
85
+ atCapacity: true
86
+ };
87
+ };
88
+ if (!offers.length) return nowhere("no-remote-mac", "no remote machines configured; " + localReason);
89
+ if (localEnabled && count === null && !remoteFirst) return local$1("device-count-unknown", localReason);
90
+ if (!remoteFirst && !cannotTake && here.memoryPressure === "normal" && !here.budgetRefusal && here.loadPerCore < here.maxLoadPerCore) return local$1("this-mac-free", localReason);
91
+ const ceiling = (offer) => offer.resources.maxLoadPerCore ?? here.maxLoadPerCore;
92
+ const skipped = [];
93
+ const admitted = [];
94
+ offers.forEach((probe, index) => {
95
+ let reason = null;
96
+ let code = "unreachable";
97
+ if ("failure" in probe) reason = probe.failure;
98
+ else {
99
+ const { offer } = probe;
100
+ if (offer.declined !== null) [code, reason] = ["declined", `declined: ${offer.declined}`];
101
+ else if (offer.platform !== platform || !offer.choice) [code, reason] = ["no-matching-device", `no matching ${platform} device choice`];
102
+ else if (offer.resources.memoryPressure !== "normal") [code, reason] = ["memory", "host memory pressure unknown or elevated"];
103
+ else if (offer.capacity.available === 0) [code, reason] = ["no-capacity", "no hosted device capacity available"];
104
+ else if (offer.resources.loadPerCore !== null && offer.resources.loadPerCore >= ceiling(offer)) [code, reason] = ["load", `saturated: load ${offer.resources.loadPerCore.toFixed(1)}/core there, at or above ${ceiling(offer)}/core`];
105
+ else if (placement === "auto" && !cannotTake && offer.resources.loadPerCore === null) [code, reason] = ["load", "host load unknown while this machine has room"];
106
+ else admitted.push({
107
+ machine: probe.machine,
108
+ offer,
109
+ index
110
+ });
111
+ }
112
+ if (reason) skipped.push({
113
+ machine: probe.machine,
114
+ code,
115
+ reason
116
+ });
117
+ if (reason && ("offer" in probe ? [
118
+ "no-capacity",
119
+ "load",
120
+ "memory"
121
+ ].includes(code) || code === "declined" && (probe.offer.capacity.available === 0 || probe.offer.resources.memoryPressure !== "normal") : false)) hostsFull = true;
122
+ });
123
+ admitted.sort((a, b) => Number(b.machine === buildMachine) - Number(a.machine === buildMachine) || a.index - b.index);
124
+ if (!admitted.length) return nowhere("no-host-admits", `${localReason}; no host admits`, skipped, localEnabled && cannotTake && !noWait ? "; waiting locally if needed" : "");
125
+ const machines = admitted.map(({ machine, offer }) => ({
126
+ machine,
127
+ reason: `${remoteFirst && !cannotTake ? `${placement} tries remote machines first` : localReason}; ${machine}${machine === buildMachine ? " is the explicit remote machine," : ""} load ${offer.resources.loadPerCore === null ? "unknown" : offer.resources.loadPerCore.toFixed(1) + "/core"}`
128
+ }));
129
+ return {
130
+ kind: "host",
131
+ code: "placed",
132
+ machines,
133
+ reason: machines[0].reason,
134
+ skipped
135
+ };
136
+ }
137
+ //#endregion
138
+ //#region src/device-host/auto-placement.ts
139
+ const HOST_WAIT_POLL_MS = 5e3;
140
+ function refusalText(decision, waitedMs) {
141
+ return [
142
+ decision.reason,
143
+ ...decision.skipped.map((each) => `${each.machine}: ${each.reason}`),
144
+ ...waitedMs === null ? [] : [`waited ${Math.round(waitedMs / 1e3)}s for a remote machine with room`]
145
+ ].join("; ");
146
+ }
147
+ async function probeHost(machine, platform, selectors, appMode, signal) {
148
+ let host;
149
+ const deadline = Date.now() + 3e3;
150
+ const cancel = () => host?.connection.close();
151
+ try {
152
+ host = await connectHost(machine, 3e3, true, signal);
153
+ signal?.addEventListener("abort", cancel, { once: true });
154
+ signal?.throwIfAborted();
155
+ if (platform !== "macos") requireHostedAppMode(host, platform, appMode);
156
+ const offer = await call(host, "device-host.offer", {
157
+ platform,
158
+ ...selectors
159
+ }, Math.max(1, deadline - Date.now()));
160
+ signal?.throwIfAborted();
161
+ const resources = isJsonObject(offer.resources) ? offer.resources : {};
162
+ const parsed = parseHostedNativeOffer({
163
+ ...offer,
164
+ resources: {
165
+ ...resources,
166
+ loadPerCore: resources.loadPerCore ?? 0
167
+ }
168
+ });
169
+ let choice = platform === "ios" ? offer.platform === "ios" ? parseHostedChoice(offer.choice) : null : parsed?.platform === platform ? parsed.choice : null;
170
+ if (choice && platform !== "macos" && ("runtime" in choice ? selectors.deviceType && selectors.deviceType !== choice.deviceType || selectors.runtime && selectors.runtime.replace(/^iOS /, "") !== choice.runtime.replace(/^iOS /, "") : "systemImage" in choice && (selectors.systemImage && selectors.systemImage !== choice.systemImage || selectors.deviceProfile && selectors.deviceProfile !== choice.deviceProfile))) choice = null;
171
+ if (!isJsonObject(offer.capacity) || offer.declined !== null && typeof offer.declined !== "string") throw new Error("unreadable device offer");
172
+ return {
173
+ probe: {
174
+ machine,
175
+ offer: {
176
+ platform,
177
+ choice,
178
+ declined: offer.declined,
179
+ capacity: { available: typeof offer.capacity.available === "number" ? offer.capacity.available : null },
180
+ resources: {
181
+ ...typeof resources.maxLoadPerCore === "number" && Number.isFinite(resources.maxLoadPerCore) && resources.maxLoadPerCore > 0 ? { maxLoadPerCore: resources.maxLoadPerCore } : {},
182
+ loadPerCore: typeof resources.loadPerCore === "number" && Number.isFinite(resources.loadPerCore) && resources.loadPerCore >= 0 ? resources.loadPerCore : null,
183
+ memoryFreeBytes: typeof resources.memoryFreeBytes === "number" ? resources.memoryFreeBytes : 0,
184
+ memoryPressure: typeof resources.memoryPressure === "string" ? resources.memoryPressure : null
185
+ }
186
+ }
187
+ },
188
+ ...choice && ("runtime" in choice || "systemImage" in choice) ? { target: {
189
+ host,
190
+ choice,
191
+ session: null
192
+ } } : {}
193
+ };
194
+ } catch (error) {
195
+ signal?.throwIfAborted();
196
+ return { probe: {
197
+ machine,
198
+ failure: error instanceof Error ? error.message : String(error)
199
+ } };
200
+ } finally {
201
+ signal?.removeEventListener("abort", cancel);
202
+ host?.connection.close();
203
+ }
204
+ }
205
+ function hosted(target, machine, code, reason, skipped = []) {
206
+ if (target) target.selection = {
207
+ selected: "auto",
208
+ reason
209
+ };
210
+ return {
211
+ target,
212
+ placement: {
213
+ decision: "hosted",
214
+ machine,
215
+ reason
216
+ },
217
+ code,
218
+ skipped
219
+ };
220
+ }
221
+ function local(code, reason, skipped = []) {
222
+ return {
223
+ target: null,
224
+ placement: {
225
+ decision: "local",
226
+ reason
227
+ },
228
+ code,
229
+ skipped
230
+ };
231
+ }
232
+ function viaEas(code, reason, skipped) {
233
+ return {
234
+ target: null,
235
+ placement: {
236
+ decision: "eas",
237
+ machine: "eas",
238
+ reason
239
+ },
240
+ code,
241
+ skipped
242
+ };
243
+ }
244
+ async function automaticDevicePlacement({ root, slot, platform, placement = "auto", selectors, appMode, buildMachine, noWait, waitMs = DEFAULT_DEVICE_SLOT_WAIT_MS, onWait = () => {}, eas, signal, localSims }, { machines = () => hostingMachines(void 0, signal), read = readHostedNative, write = writeHostedNativeAsync, resume = prepareHostedNative, peek = () => peekDeviceSlots({
245
+ platform: platform === "macos" ? "ios" : platform,
246
+ project: getProject(root),
247
+ slot,
248
+ ...localSims ? { sources: { sims: localSims } } : {}
249
+ }), capacity = machineCapacity, memory = () => readHostMemoryPressureAsync(void 0, void 0, signal), probe = probeHost, budget = (projectRoot) => peekBudget(projectRoot, {}, ambientEnv(), signal), now = Date.now, sleep = (ms) => setTimeout(ms, void 0, { signal }) } = {}) {
250
+ const recorded = platform === "macos" ? void 0 : read(root, platform, slot)[slot];
251
+ if (recorded && platform !== "macos") {
252
+ const target = await resume(recorded.machine, selectors, recorded, platform, true, appMode);
253
+ if (target) return {
254
+ target,
255
+ placement: {
256
+ decision: "hosted",
257
+ machine: recorded.machine,
258
+ reason: recorded.reason ?? decideDevicePlacement({ sticky: { machine: recorded.machine } }).reason
259
+ },
260
+ code: "sticky",
261
+ skipped: [],
262
+ sticky: true
263
+ };
264
+ await write(root, slot, null, platform);
265
+ }
266
+ const devices = platform === "macos" ? {
267
+ count: 0,
268
+ max: 0,
269
+ queued: 0,
270
+ localLive: false
271
+ } : peek();
272
+ if (devices.localLive && placement === "remote-only") throw Object.assign(/* @__PURE__ */ new Error(`This workspace's ${platform} device for slot ${slot} runs on this machine, and remote-only never runs here.`), {
273
+ code: "STIM_HOSTING_REFUSED",
274
+ remedy: "Run stim stop, then retry."
275
+ });
276
+ if (devices.localLive) return {
277
+ ...local("sticky", decideDevicePlacement({ sticky: { local: true } }).reason),
278
+ sticky: true
279
+ };
280
+ const configured = await machines();
281
+ signal?.throwIfAborted();
282
+ if (configured === null) throw Object.assign(/* @__PURE__ */ new Error("remote.machines is invalid. Run stim guide settings and correct it."), { code: "STIM_HOSTING_REFUSED" });
283
+ const config = loadConfig$1();
284
+ const entries = configured.filter((machine) => automaticMachineEnabled("device", machine, config));
285
+ const preference = resolveBuildMachine(buildMachine, ambientEnv().STIM_REMOTE_BUILD, config?.remote?.build);
286
+ buildMachine = namedBuildMachine(preference) ? preference : void 0;
287
+ const load = capacity();
288
+ const here = {
289
+ localEnabled: automaticMachineEnabled("device", "local", config),
290
+ loadPerCore: load.loadPerCore,
291
+ maxLoadPerCore: load.maxLoadPerCore,
292
+ memoryPressure: await memory(),
293
+ devices,
294
+ budgetRefusal: await budget(root)
295
+ };
296
+ signal?.throwIfAborted();
297
+ const inputs = (offers) => ({
298
+ platform,
299
+ placement,
300
+ here,
301
+ offers,
302
+ buildMachine,
303
+ noWait
304
+ });
305
+ const notProbed = entries.map((machine) => ({
306
+ machine,
307
+ failure: "not probed"
308
+ }));
309
+ const waitStarted = now();
310
+ let announced = false;
311
+ let probes = [];
312
+ let offers = notProbed;
313
+ let decision;
314
+ for (;;) {
315
+ const early = decideDevicePlacement(inputs(notProbed));
316
+ probes = early.kind === "local" && early.skipped.length === 0 ? [] : await Promise.all(entries.map((machine) => probe(machine, platform, selectors, appMode, signal)));
317
+ offers = probes.length ? probes.map((each) => each.probe) : notProbed;
318
+ decision = decideDevicePlacement(inputs(offers));
319
+ if (decision.kind !== "refused" || !decision.hostsFull) break;
320
+ const waited = now() - waitStarted;
321
+ const immediate = noWait || waitMs <= 0;
322
+ if (immediate || waited >= waitMs) throw Object.assign(new Error(refusalText(decision, immediate ? null : waited)), {
323
+ code: "STIM_AT_CAPACITY",
324
+ remedy: `Wait for a remote machine in remote.machines to have room, ${platform === "macos" ? "retry later" : "retry with a longer --wait <seconds>"}, or select a placement that may run here.`
325
+ });
326
+ if (!announced) {
327
+ announced = true;
328
+ onWait(`remote-only waits up to ${Math.round(waitMs / 1e3)}s for a remote machine with room (${decision.skipped.map((each) => `${each.machine}: ${each.reason}`).join("; ")})`);
329
+ }
330
+ await sleep(Math.min(HOST_WAIT_POLL_MS, waitMs - waited));
331
+ signal?.throwIfAborted();
332
+ }
333
+ if ((decision.kind === "local" || decision.kind === "refused") && decision.atCapacity && eas && placement !== "remote-only") decision = decideDevicePlacement({
334
+ ...inputs(offers),
335
+ eas: await eas()
336
+ });
337
+ if (decision.kind === "refused") throw Object.assign(new Error(refusalText(decision, null)), {
338
+ code: "STIM_HOSTING_REFUSED",
339
+ remedy: placement === "remote-only" ? entries.length === 0 ? "Add an approved Mac to remote.machines (stim guide settings), or select a placement that may run here." : "Wait for a remote machine in remote.machines to have room, check stim doctor for the ones that are unreachable or unapproved, or select a placement that may run here." : "Enable an available member in remote.devicePoolDisabled or select a machine explicitly."
340
+ });
341
+ if (decision.kind === "eas") return viaEas(decision.code, decision.reason, decision.skipped);
342
+ if (decision.kind === "local") return local(decision.code, decision.reason, decision.skipped);
343
+ const machine = decision.machines[0].machine;
344
+ return hosted(probes.find((each) => each.probe.machine === machine)?.target ?? null, machine, decision.code, decision.reason, decision.skipped);
345
+ }
346
+ function devicePlacementLine(placement, skipped) {
347
+ return `${placement.machine ?? "local"}, ${placement.reason}${placement.decision !== "hosted" && skipped.length ? "; " + skipped.map((each) => `${each.machine}: ${each.reason}`).join("; ") : ""}`;
348
+ }
349
+ //#endregion
350
+ export { devicePlacementLine as n, probeHost as r, automaticDevicePlacement as t };
@@ -1,21 +1,20 @@
1
- import { D as ownership_claim_exports, S as process_identity_exports, c as throwIfCancelled, l as WorkspaceProcessCleanupError, s as cancelledError, t as getExecutor, u as withWorkspaceProcessLock } from "./exec-VFRpu1iA.mjs";
2
- import { d as workspaceDir, m as workspaceLogsDir$1 } from "./paths-DQtTgsBf.mjs";
3
- import { p as getProject } from "./config-BZhXY15H.mjs";
4
- import { l as phaseLine } from "./command-output-BBvS1H6v.mjs";
5
- import { l as withWorkspaceStateLockAsync, r as readWorkspaceState$1 } from "./workspace-state-CPSp3mav.mjs";
6
- import { M as metroPortSetting, a as SETTING_SHAPE_REMEDY, ot as webSettings } from "./settings-B5tCoq9N.mjs";
7
- import { b as isPackageResolvable, v as detectIsExpo } from "./project-BiC68mIp.mjs";
8
- import { d as resolveProjectMetro } from "./metro-1pJgCX4U.mjs";
9
- import { s as readNdjsonGenerations$1 } from "./ndjson-CgE_o3Dl.mjs";
10
- import { f as webLogFile, m as webSupervisorLogFile, o as readWebRecord, s as updateWebRecord } from "./state-DMRNP7-4.mjs";
11
- import { rn as readMetroRecords } from "./budget-HyShrIpL.mjs";
12
- import { D as teardownBrowserHeld, g as resolveSupervisorTarget } from "./workspaces-C9w4aeXF.mjs";
13
- import { i as reserveBrowserPort, n as getNamedPort } from "./named-ports-BVbn8SvC.mjs";
14
- import { n as sendToOwnedPage, t as liveWebRecord } from "./page-CzEA_-5_.mjs";
15
- import { o as resolveWorkspaceMetroPort } from "./start-YMpNwLhh.mjs";
1
+ import { D as ownership_claim_exports, S as process_identity_exports, c as throwIfCancelled, l as WorkspaceProcessCleanupError, s as cancelledError, t as getExecutor, u as withWorkspaceProcessLock } from "./exec-aULwtNly.mjs";
2
+ import { E as workspaceDir, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1 } from "./workspace-state-BjqdjnJR.mjs";
3
+ import { p as getProject } from "./config-Cn94Jc0g.mjs";
4
+ import { l as phaseLine } from "./command-output-Dj-WT4BP.mjs";
5
+ import { N as metroPortSetting, a as SETTING_SHAPE_REMEDY, ct as webSettings } from "./settings-BDVI8Ivc.mjs";
6
+ import { b as isPackageResolvable, v as detectIsExpo } from "./project-DYj-HVx6.mjs";
7
+ import { d as resolveProjectMetro } from "./metro-CvyD-xwe.mjs";
8
+ import { c as readNdjsonGenerations$1 } from "./ndjson-Dy2W4vRa.mjs";
9
+ import { Wt as readMetroRecords } from "./ccache-BWTze1Ls.mjs";
10
+ import { f as webLogFile, m as webSupervisorLogFile, o as readWebRecord, s as updateWebRecord } from "./state-BX1y5syV.mjs";
11
+ import { b as resolveSupervisorTarget, lt as teardownBrowserHeld } from "./workspaces-DFbj07jp.mjs";
12
+ import { i as reserveBrowserPort, n as getNamedPort } from "./named-ports-BCyMWA6b.mjs";
13
+ import { n as sendToOwnedPage, t as liveWebRecord } from "./page-CQJlcj3x.mjs";
14
+ import { o as resolveWorkspaceMetroPort } from "./start-DrPwER_2.mjs";
16
15
  import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
17
- import { n as windowsLauncherArgs } from "./detached-entry-KmieOKgN.mjs";
18
- import { a as findChrome, t as CHROME_INSTALL_REMEDY } from "./chrome-BvV8ssI3.mjs";
16
+ import { n as windowsLauncherArgs } from "./detached-entry-BPlSaCwC.mjs";
17
+ import { a as findChrome, t as CHROME_INSTALL_REMEDY } from "./chrome-DyDWurWG.mjs";
19
18
  import { randomUUID } from "node:crypto";
20
19
  import { mkdirSync, openSync, statSync, watch } from "node:fs";
21
20
  import { ambientEnv } from "@stim-cli/core";
@@ -1,15 +1,17 @@
1
- import { c as throwIfCancelled, t as getExecutor, x as withDrainingCancellation } from "./exec-VFRpu1iA.mjs";
2
- import { i as sharedBuildCache } from "./paths-DQtTgsBf.mjs";
3
- import { a as killOnAbort, c as gitQuiet, o as git, r as childEnvironment, s as gitAsync } from "./operation-exec-CUZifp6Z.mjs";
4
- import { m as defaultAndroidLayout } from "./settings-B5tCoq9N.mjs";
5
- import { r as register } from "./cache-manifest-BPXRONJz.mjs";
1
+ import { c as throwIfCancelled, t as getExecutor, x as withDrainingCancellation } from "./exec-aULwtNly.mjs";
2
+ import { y as sharedBuildCache } from "./workspace-state-BjqdjnJR.mjs";
3
+ import { a as killOnAbort, c as gitQuiet, o as git, r as childEnvironment, s as gitAsync } from "./operation-exec-Dp4FuIGR.mjs";
4
+ import { m as defaultAndroidLayout } from "./settings-BDVI8Ivc.mjs";
5
+ import { r as register } from "./cache-manifest-DEjqGGat.mjs";
6
+ import { r as writeSignedTail, t as computeSignedTail } from "./apk-signed-tail-BHQHx3Rm.mjs";
6
7
  import { createHash } from "node:crypto";
7
8
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
8
9
  import { join } from "node:path";
9
10
  import { artifactIn, buildCacheKey, resolveArtifact, storeArtifactAsync } from "@stim-cli/core";
10
- import { isAbsolute as isAbsolute$1, join as join$1, posix as posix$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
11
+ import { trace } from "@stim-cli/core/state";
12
+ import { basename as basename$1, isAbsolute as isAbsolute$1, join as join$1, posix as posix$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
11
13
  import { existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1, statSync as statSync$1 } from "fs";
12
- import { readdir, writeFile } from "node:fs/promises";
14
+ import { readdir, rename, rm, writeFile } from "node:fs/promises";
13
15
  import { isDeepStrictEqual } from "util";
14
16
  import * as expoFingerprint from "@expo/fingerprint";
15
17
  import { loadConfigAsync } from "@expo/fingerprint/build/Config.js";
@@ -293,6 +295,58 @@ function androidProjectFingerprintOptions(projectRoot, layout, exists = existsSy
293
295
  ].map((dir) => `${moduleRel}/${dir}/**/*`)
294
296
  };
295
297
  }
298
+ const LOCKFILES = [
299
+ "pnpm-lock.yaml",
300
+ "yarn.lock",
301
+ "package-lock.json",
302
+ "bun.lock",
303
+ "bun.lockb"
304
+ ];
305
+ /**
306
+ * The patches of a monorepo's install root, which @expo/fingerprint does not see: its patch sourcer reads only the
307
+ * app's own `patches/`, and its default preset hashes an installed package by name and version, so a patch applied
308
+ * from the workspace root would leave the key unchanged.
309
+ */
310
+ function workspacePatchSources(projectRoot) {
311
+ let dir = resolve$1(projectRoot);
312
+ for (;;) {
313
+ if (LOCKFILES.some((name) => existsSync$1(join$1(dir, name)))) break;
314
+ const parent = resolve$1(dir, "..");
315
+ if (parent === dir || existsSync$1(join$1(dir, ".git"))) return [];
316
+ dir = parent;
317
+ }
318
+ const sources = ["patches", ".yarn/patches"].filter((name) => (name === "patches" ? dir !== resolve$1(projectRoot) : true) && existsSync$1(join$1(dir, name))).map((name) => ({
319
+ type: "dir",
320
+ filePath: relative$1(projectRoot, join$1(dir, name)).split(sep$1).join("/"),
321
+ reasons: ["workspacePatches"]
322
+ }));
323
+ const patched = pnpmPatchedDependencies(join$1(dir, "pnpm-lock.yaml"));
324
+ if (patched) sources.push({
325
+ type: "contents",
326
+ id: "pnpmPatchedDependencies",
327
+ contents: patched,
328
+ reasons: ["workspacePatches"]
329
+ });
330
+ return sources;
331
+ }
332
+ /** The lockfile's `patchedDependencies` block, which pnpm rewrites with each patch's hash wherever the patch lives. */
333
+ function pnpmPatchedDependencies(lockfile) {
334
+ let text;
335
+ try {
336
+ text = readFileSync$1(lockfile, "utf8");
337
+ } catch {
338
+ return null;
339
+ }
340
+ const lines = text.split(/\r?\n/);
341
+ const start = lines.indexOf("patchedDependencies:");
342
+ if (start === -1) return null;
343
+ const block = [];
344
+ for (const line of lines.slice(start + 1)) {
345
+ if (line && !/^\s/.test(line)) break;
346
+ if (line.trim()) block.push(line.trim());
347
+ }
348
+ return block.length ? block.join("\n") : null;
349
+ }
296
350
  /**
297
351
  * The bundle output and, with `ios.projectPath` '.', the top-level entries that git ignores here for an iOS
298
352
  * fingerprint, as the paths a build worker needs to ignore the same ones: it has no .git/info/exclude or global
@@ -319,7 +373,12 @@ async function fingerprintOptions(projectRoot, { platform, iosProjectPath = "ios
319
373
  const ignoredAtAppRoot = (names) => new Set([...ignoredHere(names)].filter((name) => !trackedPaths.includes(name)));
320
374
  const platforms = platform && FINGERPRINT_PLATFORMS.has(platform) ? { platforms: [platform] } : void 0;
321
375
  const ios = platform === "ios" ? iosProjectFingerprintOptions(projectRoot, iosProjectPath, { ignored: (_, names) => ignoredAtAppRoot(names) }) : androidLayout?.custom ? androidProjectFingerprintOptions(projectRoot, androidLayout) : {};
322
- if (ios.extraSources) ios.extraSources = [...(await loadConfigAsync(projectRoot, true))?.extraSources ?? [], ...ios.extraSources];
376
+ const patches = workspacePatchSources(projectRoot);
377
+ if (ios.extraSources || patches.length) ios.extraSources = [
378
+ ...(await loadConfigAsync(projectRoot, true))?.extraSources ?? [],
379
+ ...ios.extraSources ?? [],
380
+ ...patches
381
+ ];
323
382
  const bundleOutput = platform === "android" ? [] : [...ignoredHere(Object.keys(BUNDLE_OUTPUT_IGNORES), true)].map((path) => BUNDLE_OUTPUT_IGNORES[path]);
324
383
  return {
325
384
  ...platforms,
@@ -333,26 +392,27 @@ async function fingerprintOptions(projectRoot, { platform, iosProjectPath = "ios
333
392
  };
334
393
  }
335
394
  async function fingerprintProject(projectRoot, { createFingerprint = expoFingerprint.createFingerprintAsync, debug = false, ...target } = {}) {
336
- const options = await fingerprintOptions(projectRoot, target);
337
- const result = await createFingerprint(projectRoot, debug ? {
338
- ...options,
339
- debug
340
- } : options);
341
- const hash = result?.hash ?? null;
342
- if (!hash) return null;
343
- return {
344
- hash,
345
- sources: Array.isArray(result?.sources) ? result.sources : []
346
- };
395
+ return trace.spanAsync("fingerprint", async () => {
396
+ const options = await fingerprintOptions(projectRoot, target);
397
+ const result = await createFingerprint(projectRoot, debug ? {
398
+ ...options,
399
+ debug
400
+ } : options);
401
+ const hash = result?.hash ?? null;
402
+ if (!hash) return null;
403
+ return {
404
+ hash,
405
+ sources: Array.isArray(result?.sources) ? result.sources : []
406
+ };
407
+ }, { platform: target.platform });
347
408
  }
348
- /** The env files @expo/env loads for the config, in the mode @expo/fingerprint's config loader sets. */
409
+ /** The env files @expo/env loads for the config: @expo/fingerprint 0.21 evaluates it in development mode. */
349
410
  function expoEnvFiles(env = {}) {
350
411
  if (/^(1|true)$/i.test(env.EXPO_NO_DOTENV ?? "")) return [];
351
- const mode = env.NODE_ENV || "development";
352
412
  return [
353
- `.env.${mode}.local`,
354
- ...mode === "test" ? [] : [".env.local"],
355
- `.env.${mode}`,
413
+ ".env.development.local",
414
+ ".env.local",
415
+ ".env.development",
356
416
  ".env"
357
417
  ];
358
418
  }
@@ -367,8 +427,8 @@ async function gitignoredFingerprintInputs(projectRoot, sources, target, { env,
367
427
  if (sources.some((source) => source.type === "contents" && source.id === "expoConfig")) {
368
428
  for (const name of expoEnvFiles(env)) if (lstatSync$1(join$1(projectRoot, name), { throwIfNoEntry: false })?.isFile()) found.add(name);
369
429
  }
370
- const top = realpathSync$1(await gitAsync(projectRoot, ["rev-parse", "--show-toplevel"], { signal }));
371
- const base = realpathSync$1(projectRoot);
430
+ const top = realpathSync$1.native(await gitAsync(projectRoot, ["rev-parse", "--show-toplevel"], { signal }));
431
+ const base = realpathSync$1.native(projectRoot);
372
432
  const pathspecs = sources.flatMap((source) => {
373
433
  if (source.type === "contents" || inNodeModules(source.filePath)) return [];
374
434
  const inRepo = relative$1(top, resolve$1(base, source.filePath));
@@ -457,6 +517,17 @@ async function storeBuild(platform, key, buildPath, rootOrOptions = {}) {
457
517
  }),
458
518
  overwrite: Boolean(options.overwrite),
459
519
  writeMetadata: async (staging) => {
520
+ if (platform === "android" && buildPath.endsWith(".apk")) try {
521
+ const tail = await computeSignedTail(join$1(staging, basename$1(buildPath)), (file, args) => getExecutor().runFileAsync(file, args, {
522
+ timeoutMs: 12e4,
523
+ ...env ? childEnvironment(env) : {},
524
+ onSpawn: killOnAbort(signal),
525
+ declareFor: options.context
526
+ }));
527
+ if (tail) await writeSignedTail(staging, tail);
528
+ } catch {
529
+ throwIfCancelled(signal, "build cache copy");
530
+ }
460
531
  if (Array.isArray(options.sources)) try {
461
532
  await writeFile(join$1(staging, SOURCES_FILE), JSON.stringify(options.sources.map(withoutContents)));
462
533
  } catch {}
@@ -476,6 +547,25 @@ async function storeBuild(platform, key, buildPath, rootOrOptions = {}) {
476
547
  throw error;
477
548
  }
478
549
  }
550
+ /** Removes a stored build by renaming its entry aside first, so no run sees a half-deleted entry. */
551
+ async function evictBuild(platform, key, root = sharedBuildCache()) {
552
+ const dir = entryDir(platform, key, root);
553
+ const evicted = `${dir}.evicted-${process.pid}`;
554
+ try {
555
+ await rm(evicted, {
556
+ recursive: true,
557
+ force: true
558
+ });
559
+ await rename(dir, evicted);
560
+ } catch {
561
+ return false;
562
+ }
563
+ await rm(evicted, {
564
+ recursive: true,
565
+ force: true
566
+ }).catch(() => {});
567
+ return true;
568
+ }
479
569
  const PROVIDER_DOWNLOAD_DIR = "cache-provider";
480
570
  function providerDownloadPath(workspacePath) {
481
571
  return join$1(workspacePath, PROVIDER_DOWNLOAD_DIR);
@@ -588,6 +678,28 @@ function diffItemChange(item) {
588
678
  reasons: sourceReasons(source)
589
679
  };
590
680
  }
681
+ /** The installed package a source path lies in: the name after its last `node_modules/`. */
682
+ function packageIn(path) {
683
+ return [...path.matchAll(/(?:^|\/)node_modules\/((?:@[^/]+\/)?[^/]+)/g)].at(-1)?.[1] ?? null;
684
+ }
685
+ /** A package that moved, such as a version bump of a pnpm store directory, is one change rather than a removal and an addition. */
686
+ function mergeMovedPackages(changes) {
687
+ const added = /* @__PURE__ */ new Map();
688
+ for (const change of changes) {
689
+ const pkg = change.change === "added" ? packageIn(change.name) : null;
690
+ if (pkg && !added.has(pkg)) added.set(pkg, change);
691
+ }
692
+ const moved = /* @__PURE__ */ new Set();
693
+ const kept = changes.filter((change) => {
694
+ const pkg = change.change === "removed" ? packageIn(change.name) : null;
695
+ const counterpart = pkg ? added.get(pkg) : void 0;
696
+ if (!counterpart || moved.has(counterpart)) return true;
697
+ moved.add(counterpart);
698
+ return false;
699
+ });
700
+ for (const change of moved) change.change = "changed";
701
+ return kept;
702
+ }
591
703
  function compareSourceLists(previous, current) {
592
704
  const previousByName = /* @__PURE__ */ new Map();
593
705
  for (const source of previous) {
@@ -621,7 +733,7 @@ function compareSourceLists(previous, current) {
621
733
  change: "removed",
622
734
  reasons: before.reasons
623
735
  });
624
- return changes;
736
+ return mergeMovedPackages(changes);
625
737
  }
626
738
  function diffFingerprintSources({ previous, previousHash = null, current, differ = null }) {
627
739
  if (typeof differ === "function") try {
@@ -633,16 +745,8 @@ function diffFingerprintSources({ previous, previousHash = null, current, differ
633
745
  hash: current.hash
634
746
  });
635
747
  if (Array.isArray(items)) {
636
- const changes = [];
637
748
  const seen = /* @__PURE__ */ new Set();
638
- for (const item of items) {
639
- const change = diffItemChange(item);
640
- if (change !== null && !seen.has(change.name)) {
641
- seen.add(change.name);
642
- changes.push(change);
643
- }
644
- }
645
- return changes;
749
+ return mergeMovedPackages(items.map(diffItemChange).filter((change) => change !== null)).filter((change) => !seen.has(change.name) && Boolean(seen.add(change.name)));
646
750
  }
647
751
  } catch {}
648
752
  return compareSourceLists(previous, current.sources);
@@ -750,4 +854,4 @@ function fingerprintDiffRecord({ changed, previousHash, hash }) {
750
854
  };
751
855
  }
752
856
  //#endregion
753
- export { readAssetManifest as A, storedSources as C, assetDiffReason as D, untrackedNativeFiles as E, captureAssetManifest as O, storedAssetManifest as S, untrackedMissLine as T, providerUploadOutcome as _, configInputsChanged as a, storeBuild as b, expoConfigContents as c, fingerprintProject as d, gitignoredFingerprintInputs as f, providerDownloadPath as g, prepareProviderDownloadDir as h, compareSourceLists as i, compareAssetManifests as k, filesystemBuildCapability as l, inputsChangedDuringBuild as m, buildCacheKey as n, diffFingerprintSources as o, ignoredFingerprintPaths as p, changedDuringBuildLine as r, entryDir as s, artifactIn as t, fingerprintDiffRecord as u, refingerprintAfterMutation as v, trackedFingerprintPaths as w, storedAppId as x, resolveBuild as y };
857
+ export { untrackedNativeFiles as A, resolveBuild as C, storedSources as D, storedAssetManifest as E, captureAssetManifest as M, compareAssetManifests as N, trackedFingerprintPaths as O, readAssetManifest as P, refingerprintAfterMutation as S, storedAppId as T, inputsChangedDuringBuild as _, configInputsChanged as a, providerDownloadPath as b, evictBuild as c, filesystemBuildCapability as d, fingerprintDiffRecord as f, ignoredFingerprintPaths as g, gitignoredFingerprintInputs as h, compareSourceLists as i, assetDiffReason as j, untrackedMissLine as k, expoConfigContents as l, fingerprintProject as m, buildCacheKey as n, diffFingerprintSources as o, fingerprintOptions as p, changedDuringBuildLine as r, entryDir as s, artifactIn as t, expoEnvFiles as u, packageIn as v, storeBuild as w, providerUploadOutcome as x, prepareProviderDownloadDir as y };