stim 1.20.2 → 1.22.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 (231) hide show
  1. package/README.md +141 -8
  2. package/dist/acquire-artifact-Co2k4f7O.mjs +171 -0
  3. package/dist/act-DsuqEkiM.mjs +2801 -0
  4. package/dist/activity-CMLmzp7o.mjs +1397 -0
  5. package/dist/{agent-device-usage-output-Dzgv6lgR.mjs → agent-device-usage-output-DUFaTMdd.mjs} +23 -67
  6. package/dist/{android-DCKZrNvV.mjs → android-ByRMUoiJ.mjs} +157 -93
  7. package/dist/{android-Mz_Inn3w.mjs → android-C7sVmyI9.mjs} +299 -116
  8. package/dist/{android-Dlzxshgp.mjs → android-DGkNqa_c.mjs} +5 -5
  9. package/dist/{android-HqP1_vB_.mjs → android-DR5lZ2Az.mjs} +2 -2
  10. package/dist/android-cas-compiler.mjs +2 -2
  11. package/dist/{android-cas-Bl-cLJBk.mjs → android-cas-ngferVq2.mjs} +7 -6
  12. package/dist/android-project-BCAAmMwz.mjs +12 -0
  13. package/dist/android-project-DVWyCbWU.d.mts +391 -0
  14. package/dist/api.d.mts +810 -64
  15. package/dist/api.mjs +782 -1
  16. package/dist/{artifact-HSwkIWEQ.mjs → artifact-8EY8SAPw.mjs} +240 -213
  17. package/dist/{artifact-D3czlUvT.mjs → artifact-DJPy518Y.mjs} +203 -164
  18. package/dist/artifact-platform-CXcmX_j3.mjs +27 -0
  19. package/dist/{browser-web-CA-F1GQo.mjs → browser-web-Bcv99PBM.mjs} +194 -64
  20. package/dist/budget-Cvwn54qV.mjs +9285 -0
  21. package/dist/{build-DYDbfkk5.mjs → build-CabC3G23.mjs} +153 -98
  22. package/dist/build-cache-Dv7RN200.mjs +602 -0
  23. package/dist/build-plan-DUC8Ka9y.mjs +248 -0
  24. package/dist/{cache-manifest-XH-fvDUj.mjs → cache-manifest-DSeQjt-Q.mjs} +2 -2
  25. package/dist/cache-manifest.mjs +1 -1
  26. package/dist/{ccache-zdKiYQ3E.mjs → ccache-Btwc36-I.mjs} +10 -9
  27. package/dist/{cdp-Dq_ypAlJ.mjs → cdp-3fwICjzi.mjs} +18 -5
  28. package/dist/{chrome-BnzZMqsR.mjs → chrome-Bbw689-6.mjs} +7 -4
  29. package/dist/{cli-CLP3diBm.mjs → cli-CHZa9Lvk.mjs} +20 -20
  30. package/dist/cli.mjs +1 -1
  31. package/dist/client-OyRoEuSr.mjs +1577 -0
  32. package/dist/collector-run.d.mts +4 -40
  33. package/dist/collector-run.mjs +9 -8
  34. package/dist/{command-output-BTy8hZ0F.mjs → command-output-gwpm28fu.mjs} +1 -1
  35. package/dist/config-DlrsduwG.mjs +955 -0
  36. package/dist/{created-devices-Dzd5C2LF.mjs → created-devices-x9rN6bhs.mjs} +14 -4
  37. package/dist/{dependency-state-DG0p31-y.mjs → dependency-state-1I9KZT1Y.mjs} +20 -5
  38. package/dist/{detached-entry-B9C_zeQC.mjs → detached-entry-BwNEtIrP.mjs} +1 -1
  39. package/dist/{dev-client-DAWVIGBu.mjs → dev-client-BSDR5h3F.mjs} +2 -2
  40. package/dist/{device-BECOiufl.mjs → device-B-L3Fbxy.mjs} +91 -34
  41. package/dist/device-DklWZLqb.mjs +1068 -0
  42. package/dist/{device-capacity-CBUAcRyQ.mjs → device-capacity-DK5_EwRS.mjs} +42 -13
  43. package/dist/device-host-worker.mjs +76 -79
  44. package/dist/{device-ios-COSi6oS1.mjs → device-ios-DoB6KFCO.mjs} +77 -51
  45. package/dist/{device-lease-run-Cw2M4z51.mjs → device-lease-run-xJPQ_QQG.mjs} +14 -26
  46. package/dist/{device-pool-Bf-pOhSw.mjs → device-pool-BMkdsefq.mjs} +5 -5
  47. package/dist/{doctor-kfMBnUw6.mjs → doctor-Bo7BXLux.mjs} +271 -320
  48. package/dist/{doctor-CB8oZKCk.mjs → doctor-D5_EzwfA.mjs} +186 -91
  49. package/dist/doctor-fingerprint-DZLoYFVb.mjs +16 -0
  50. package/dist/doctor-fingerprint.mjs +19 -0
  51. package/dist/doctor-thjWAV0z.mjs +2 -0
  52. package/dist/{doctor-watchman-BRyV2adA.mjs → doctor-watchman-N2wDotwQ.mjs} +40 -21
  53. package/dist/{eas-build-JyfqyHfC.mjs → eas-build-CwknaQYj.mjs} +53 -20
  54. package/dist/{build-plan-B7SZsc2N.mjs → eas-fallback-CSgAulr5.mjs} +41 -214
  55. package/dist/{watchman-CPQFZLXY.mjs → eas-session-ledger-DlY_serQ.mjs} +230 -11
  56. package/dist/{error-diagnostics-BR677IDM.mjs → error-diagnostics-DBWO2rxE.mjs} +172 -82
  57. package/dist/exec-DEoAhcze.mjs +858 -0
  58. package/dist/exec-DnYc4HGH.d.mts +78 -0
  59. package/dist/{gc-qWOMdnER.mjs → gc-CXxN-4SE.mjs} +1091 -1029
  60. package/dist/gc-DnNDQyaP.mjs +3 -0
  61. package/dist/{guide-G9cAbVAc.mjs → guide-Br0LEfKy.mjs} +1165 -297
  62. package/dist/{hosted-android-C3MHGld1.mjs → hosted-android-B-9f-jkd.mjs} +10 -8
  63. package/dist/{hosted-client-ZEKpS9vw.mjs → hosted-client-DZAPrcro.mjs} +34 -19
  64. package/dist/{hosted-ios-l-o3iM9J.mjs → hosted-ios-CsCS21hn.mjs} +1 -1
  65. package/dist/{hosted-logs-EzQM7-3Z.mjs → hosted-logs-FIRx9X2f.mjs} +77 -43
  66. package/dist/{hosted-macos-BACVAVqE.mjs → hosted-macos-_awYSpfv.mjs} +35 -22
  67. package/dist/{hosted-native-A4s1QPXe.mjs → hosted-native-BdzE668-.mjs} +49 -27
  68. package/dist/idle-C4cZ757K.d.mts +24 -0
  69. package/dist/{idle-shutdown-DtxhPTKe.mjs → idle-shutdown-DDg3SdvY.mjs} +11 -10
  70. package/dist/ios-CDvlmUs7.d.mts +24 -0
  71. package/dist/{ios-BwVCF7oA.mjs → ios-Ds1clbIt.mjs} +8 -8
  72. package/dist/{ios-device-D7pi2Sca.mjs → ios-device--h_-8ENa.mjs} +51 -26
  73. package/dist/{ios-device-B5veSDDQ.mjs → ios-device-BXZ0c7Kb.mjs} +1 -1
  74. package/dist/{ios-RkW4pnQf.mjs → ios-hcBpnEGk.mjs} +200 -93
  75. package/dist/{ios-Ce7bc7br.mjs → ios-pm4nL6By.mjs} +160 -108
  76. package/dist/{ios-project-K9hY_Jab.mjs → ios-project-CZhKODF3.mjs} +13 -13
  77. package/dist/ios-signing-BtAffm0A.mjs +489 -0
  78. package/dist/{ios-state-CBLe1xYw.mjs → ios-state-Dtw5K2aI.mjs} +1 -1
  79. package/dist/{ios-nOLJGVdy.mjs → ios-xtlRHaeB.mjs} +1 -1
  80. package/dist/{js-swap-Bb3nsS07.mjs → js-swap-Bwhh4S7V.mjs} +30 -25
  81. package/dist/{launch-C3qJ6Agb.mjs → launch-BjxemNpK.mjs} +85 -543
  82. package/dist/{launch-F4sOqAhB.mjs → launch-CCzXlc8r.mjs} +91 -90
  83. package/dist/logs-BfwwadsZ.mjs +479 -0
  84. package/dist/logs-jTS_lHSo.mjs +248 -0
  85. package/dist/{macos-D7uDvO-z.mjs → macos-DVlSSyUg.mjs} +1 -1
  86. package/dist/macos-NfV2D7dO.mjs +694 -0
  87. package/dist/macos-run.mjs +1 -1
  88. package/dist/maintenance-run.mjs +123 -248
  89. package/dist/{metro-DnCEjCxs.mjs → metro-DiXtA3Ty.mjs} +107 -30
  90. package/dist/metro-gateway-CKZqKqx7.mjs +209 -0
  91. package/dist/{named-ports-4WopRPg-.mjs → named-ports-DkEzOfzO.mjs} +42 -90
  92. package/dist/native-android-CqgnOgsT.mjs +645 -0
  93. package/dist/native-gradle-artifact-inputs-DHFnmrVf.mjs +270 -0
  94. package/dist/native-gradle-inputs-Dnh84Zbd.mjs +211 -0
  95. package/dist/native-inputs-TRTTTRxt.mjs +91 -0
  96. package/dist/native-run-NwUuxbkg.mjs +276 -0
  97. package/dist/{native-runtime-k8dTIrbK.mjs → native-runtime-B7KIafpM.mjs} +11 -9
  98. package/dist/native-source-Y0yLhFyQ.mjs +133 -0
  99. package/dist/{native-xcode-inputs-CykopJdM.mjs → native-xcode-inputs-D_VOfFL7.mjs} +26 -18
  100. package/dist/{native-xcode-ios-Bbf30Gm0.mjs → native-xcode-ios-BYDOHWLN.mjs} +104 -60
  101. package/dist/{ndjson-GOnbyvso.mjs → ndjson-CgE_o3Dl.mjs} +3 -3
  102. package/dist/offload-worker.d.mts +59 -41
  103. package/dist/offload-worker.mjs +478 -115
  104. package/dist/operation-exec-BW0em61G.mjs +171 -0
  105. package/dist/{ownership-DCtw_2IA.mjs → ownership-C5fo5Edb.mjs} +3 -3
  106. package/dist/{page-DRHy329N.mjs → page-DBL2PiUU.mjs} +12 -5
  107. package/dist/paths-BsJMoPP2.mjs +12 -0
  108. package/dist/paths-DQtTgsBf.mjs +42 -0
  109. package/dist/{placement-log-MOfPVgaM.mjs → placement-log-BnGgJRRZ.mjs} +5 -5
  110. package/dist/{plan-placement-V0TdzYXp.mjs → plan-placement-DIsMtN-d.mjs} +15 -11
  111. package/dist/{ports-DdSUicVo.mjs → ports-Cy1RkmuQ.mjs} +3 -3
  112. package/dist/prebuild-D4JoEAEs.mjs +484 -0
  113. package/dist/{project-_phNIJfs.mjs → project-wM_XFTvE.mjs} +65 -37
  114. package/dist/{pull-request-CQ8sPbZa.mjs → pull-request-W1IuLXDt.mjs} +102 -20
  115. package/dist/pull-request-eODyIqZF.d.mts +124 -0
  116. package/dist/pull-requests.d.mts +2 -120
  117. package/dist/pull-requests.mjs +2 -2
  118. package/dist/{react-native-android-CM1XRDwv.mjs → react-native-android-6OZ7MSM7.mjs} +121 -72
  119. package/dist/{gradle-DSrXb6lG.mjs → react-native-build-Dvd8hC21.mjs} +292 -15
  120. package/dist/react-native-doctor-DsoSF1Y9.mjs +234 -0
  121. package/dist/{react-native-ios-Dhq_eiYQ.mjs → react-native-ios-4u1Rn-GR.mjs} +71 -39
  122. package/dist/{recordings-D0d5aQt0.mjs → recordings-CQmfzAh7.mjs} +12 -9
  123. package/dist/registration-BcRgF6Ox.mjs +51 -0
  124. package/dist/{reload-C8JhxjSh.mjs → reload-BzkYabB-.mjs} +94 -47
  125. package/dist/reload-D7AVlb8N.mjs +2 -0
  126. package/dist/{remote-BiR6aYjy.mjs → remote-DjGDmlTZ.mjs} +26 -18
  127. package/dist/result-D2Ykz_iD.mjs +435 -0
  128. package/dist/{run-BtPgpKks.mjs → run-DidHghD1.mjs} +5 -5
  129. package/dist/selection-DSMbWEX2.mjs +620 -0
  130. package/dist/{server-bare-R2m_gsH8.mjs → server-bare-CafzuyFB.mjs} +3 -4
  131. package/dist/{server-command-CTtCkmcQ.mjs → server-command-C6QjeupS.mjs} +14 -7
  132. package/dist/{server-expo-DsguMGUF.mjs → server-expo-CrjB29pq.mjs} +54 -11
  133. package/dist/server-expo-D58c9L0M.mjs +2 -0
  134. package/dist/{settings-B717odTD.mjs → settings-C7O8A6ZI.mjs} +309 -119
  135. package/dist/settings-DpR-1Wnn.mjs +114 -0
  136. package/dist/settings-write-CzD60rOD.mjs +360 -0
  137. package/dist/settings.schema.json +176 -16
  138. package/dist/{simslim-CCr3rE-A.mjs → simslim-BNckWpXi.mjs} +14 -12
  139. package/dist/{slot-launch-DPt0m9Nd.mjs → slot-launch-CbfmNgM6.mjs} +15 -20
  140. package/dist/{spawn-entry-Dcb7sDUz.mjs → spawn-entry-jw19m8Nl.mjs} +0 -1
  141. package/dist/stage-Bps5oius.mjs +200 -0
  142. package/dist/{start-TXeMmmO2.mjs → start-Br-oxqMD.mjs} +1 -1
  143. package/dist/start-EojN4WFz.mjs +1364 -0
  144. package/dist/{state-D1Icyjlu.mjs → state--smjFSTL.mjs} +55 -13
  145. package/dist/{state-CG_OFoaA.mjs → state-08I3hw3_.mjs} +3 -2
  146. package/dist/{stats-BoY5sEPU.mjs → stats-D3GerlFW.mjs} +7 -13
  147. package/dist/stats-report-BzaPrRm6.mjs +16 -0
  148. package/dist/{status-BTA7kKiQ.mjs → status-BRROykC8.mjs} +7 -5
  149. package/dist/status-EvrK72L2.mjs +2 -0
  150. package/dist/{status-WgFbkRzj.mjs → status-WBDajew2.mjs} +554 -452
  151. package/dist/{status-watch-C23ILtf9.mjs → status-watch-Bg_u-pwR.mjs} +222 -25
  152. package/dist/status-worker.d.mts +1 -0
  153. package/dist/status-worker.mjs +19 -0
  154. package/dist/stim-desktop-D5OGpYZM.mjs +112 -0
  155. package/dist/{stop-DR6VoDIk.mjs → stop-DTZ9uTw3.mjs} +2 -2
  156. package/dist/{stop-DPEhunlw.mjs → stop-Dky10AEJ.mjs} +33 -23
  157. package/dist/{stop-BNmBMi1F.mjs → stop-Mv6W9FAk.mjs} +137 -98
  158. package/dist/supervisor-run.d.mts +8 -17
  159. package/dist/supervisor-run.mjs +56 -28
  160. package/dist/{support-CwjbFs9H.mjs → support-CNioewsd.mjs} +14 -14
  161. package/dist/{support-DMTqj3Hm.mjs → support-yUiOjnVY.mjs} +20 -13
  162. package/dist/swiftpm-artifact-inputs-DFcGsXDP.mjs +153 -0
  163. package/dist/swiftpm-macos-BIak-Rxv.mjs +532 -0
  164. package/dist/toolchain-BY8X8HXx.mjs +2 -0
  165. package/dist/toolchain-DpJMHq0n.mjs +876 -0
  166. package/dist/{trigger-CigAYjeL.mjs → trigger-Cbyc41d_.mjs} +21 -6
  167. package/dist/trigger-yb_nFzPF.mjs +2 -0
  168. package/dist/{warm-progress-BuY-Fvfn.mjs → warm-progress-CA3TgT3-.mjs} +263 -156
  169. package/dist/web-CZ07gd77.mjs +2 -0
  170. package/dist/{web-BDBMxDpx.mjs → web-DKx6yoFA.mjs} +20 -14
  171. package/dist/web-run.mjs +13 -12
  172. package/dist/windows-report.d.mts +1 -0
  173. package/dist/windows-report.mjs +18 -0
  174. package/dist/windows-report.ps1 +43 -0
  175. package/dist/{workspace-state-DJt_MMvf.mjs → workspace-state-U08dEzhG.mjs} +62 -58
  176. package/dist/{workspaces-C36s2jb9.mjs → workspaces-CpANmBlq.mjs} +1723 -1286
  177. package/dist/{worktree-CgvJYJ-Q.mjs → worktree-CHecZ4Oq.mjs} +1097 -547
  178. package/dist/worktree-ZqZVjD9K.mjs +2 -0
  179. package/dist/xcode-BXn9-xYx.mjs +896 -0
  180. package/package.json +7 -5
  181. package/shim/android-audio/Main.java +24 -0
  182. package/shim/android-audio/README.md +17 -0
  183. package/shim/android-audio.jar +0 -0
  184. package/shim/android-optimizations.gradle +7 -5
  185. package/dist/activity-BSV745Kz.mjs +0 -629
  186. package/dist/api-qvOzWyfO.mjs +0 -154
  187. package/dist/api-run.mjs +0 -294
  188. package/dist/app-install-BIKtMfPq.mjs +0 -896
  189. package/dist/artifact-lifecycle-DzPpBYvK.mjs +0 -30
  190. package/dist/budget-f7516Zk0.mjs +0 -4106
  191. package/dist/build-facts-DcLv2HSd.d.mts +0 -111
  192. package/dist/cancellation-C_22VQTH.mjs +0 -24
  193. package/dist/client-qQAaXSUs.mjs +0 -1771
  194. package/dist/config-BYDxlMY3.mjs +0 -562
  195. package/dist/device-DuiPJQSn.mjs +0 -290
  196. package/dist/device-lease-BX1sJQ2E.mjs +0 -1224
  197. package/dist/device-remote-xRT0Vmo9.mjs +0 -1272
  198. package/dist/eas-session-ledger-Cm0Gwq2F.mjs +0 -207
  199. package/dist/exec-DqKo9GKJ.mjs +0 -268
  200. package/dist/git-DDfFzkMm.mjs +0 -45
  201. package/dist/launch-verify-B6Q8hZP-.mjs +0 -428
  202. package/dist/logs-BGrEUaEs.mjs +0 -383
  203. package/dist/macos-Ch5VJmcS.mjs +0 -584
  204. package/dist/metro-gateway-CX9Xlkq2.mjs +0 -103
  205. package/dist/miss-reason-mKzZ4VvN.mjs +0 -238
  206. package/dist/native-android-D5qc3-_v.mjs +0 -226
  207. package/dist/native-gradle-inputs-BsLRrl7u.mjs +0 -349
  208. package/dist/ndjson-Du3j0AQ9.d.mts +0 -17
  209. package/dist/plan-C4TjdPsR.mjs +0 -260
  210. package/dist/prebuild-DYbucPXy.mjs +0 -212
  211. package/dist/preview-78a9kR_-.mjs +0 -1011
  212. package/dist/react-native-build-CtCccJtb.mjs +0 -239
  213. package/dist/react-native-doctor-CfRnFfjs.mjs +0 -78
  214. package/dist/remote-cache-OyOyVIS3.mjs +0 -526
  215. package/dist/result-CaJg5Iat.mjs +0 -186
  216. package/dist/server-expo-Bigriskl.mjs +0 -2
  217. package/dist/settings-BORNaaCO.mjs +0 -402
  218. package/dist/settings-vk3mJGSm.d.mts +0 -13
  219. package/dist/spawn-claims-DVS5RkMS.mjs +0 -72
  220. package/dist/stage-KNTQopqv.mjs +0 -157
  221. package/dist/start-BWqzODpb.mjs +0 -1116
  222. package/dist/stim-desktop-BzBN2Em2.mjs +0 -63
  223. package/dist/swiftpm-macos-DRNVJWlb.mjs +0 -119
  224. package/dist/tailnet-Dh-qiYzG.mjs +0 -265
  225. package/dist/toolchain-BFkAcstd.mjs +0 -2
  226. package/dist/toolchain-DxSsNssu.mjs +0 -1657
  227. package/dist/trigger-9_USgmxz.mjs +0 -2
  228. package/dist/web-Dq-i3wzJ.mjs +0 -2
  229. package/dist/worktree-DpLdCHga.mjs +0 -2
  230. package/dist/{api-run.d.mts → doctor-fingerprint.d.mts} +0 -0
  231. package/dist/{errors-DzkMYh8Q.mjs → errors-ccaPc4ox.mjs} +2 -2
@@ -1,1224 +0,0 @@
1
- import { B as deviceSlotKey, H as parseDeviceSlotKey, K as validateDeviceSlot, d as getConfigDir, j as ownership_claim_exports } from "./config-BYDxlMY3.mjs";
2
- import { t as getExecutor } from "./exec-DqKo9GKJ.mjs";
3
- import { r as gitQuiet, t as git } from "./git-DDfFzkMm.mjs";
4
- import { r as register } from "./cache-manifest-XH-fvDUj.mjs";
5
- import { m as defaultAndroidLayout } from "./settings-B717odTD.mjs";
6
- import { D as withDirLock$1, c as writeWorkspaceState, m as sharedBuildCache, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState$1 } from "./workspace-state-DJt_MMvf.mjs";
7
- import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-BTy8hZ0F.mjs";
8
- import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-DVS5RkMS.mjs";
9
- import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
10
- import { join } from "node:path";
11
- import { join as join$1, posix as posix$1, relative as relative$1, sep as sep$1 } from "path";
12
- import { isJsonObject } from "@stim-cli/core/state";
13
- import { buildUploadTimeoutMs } from "@stim-cli/cache";
14
- import { existsSync as existsSync$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, writeFileSync as writeFileSync$1 } from "fs";
15
- import { isDeepStrictEqual } from "util";
16
- import * as expoFingerprint from "@expo/fingerprint";
17
- import { loadConfigAsync } from "@expo/fingerprint/build/Config.js";
18
- import { artifactIn, buildCacheKey, resolveArtifact, storeArtifact } from "@stim-cli/core";
19
- import { createHash, randomBytes } from "node:crypto";
20
- const ASSET_MANIFEST_FILE = "assets-manifest.json";
21
- const MAX_DEPTH = 4;
22
- function hashFile(file) {
23
- try {
24
- return createHash("sha256").update(readFileSync(file)).digest("hex");
25
- } catch {
26
- return null;
27
- }
28
- }
29
- function readAssetManifest(root) {
30
- if (!existsSync(root)) return null;
31
- const assets = [];
32
- let broken = false;
33
- const walk = (abs, rel, depth) => {
34
- if (depth > MAX_DEPTH || broken) return;
35
- let names;
36
- try {
37
- names = readdirSync(abs);
38
- } catch {
39
- broken = true;
40
- return;
41
- }
42
- for (const name of names) {
43
- const child = join(abs, name);
44
- let stat;
45
- try {
46
- stat = statSync(child);
47
- } catch {
48
- broken = true;
49
- return;
50
- }
51
- const path = rel === "" ? name : `${rel}/${name}`;
52
- if (stat.isDirectory()) {
53
- walk(child, path, depth + 1);
54
- continue;
55
- }
56
- if (!stat.isFile()) continue;
57
- const sha256 = hashFile(child);
58
- if (sha256 === null) {
59
- broken = true;
60
- return;
61
- }
62
- assets.push({
63
- path,
64
- sha256
65
- });
66
- }
67
- };
68
- walk(root, "", 0);
69
- if (broken) return null;
70
- assets.sort((a, b) => a.path.localeCompare(b.path));
71
- return {
72
- version: 1,
73
- assets
74
- };
75
- }
76
- function parseAssetManifest(text) {
77
- let parsed;
78
- try {
79
- parsed = JSON.parse(String(text));
80
- } catch {
81
- return null;
82
- }
83
- const obj = parsed;
84
- if (!obj || obj.version !== 1 || !Array.isArray(obj.assets)) return null;
85
- const assets = [];
86
- for (const raw of obj.assets) {
87
- const entry = raw;
88
- if (typeof entry?.path !== "string" || typeof entry?.sha256 !== "string") return null;
89
- assets.push({
90
- path: entry.path,
91
- sha256: entry.sha256
92
- });
93
- }
94
- return {
95
- version: 1,
96
- assets
97
- };
98
- }
99
- function pickGeneratedResDir(names, variant, reactChildren = []) {
100
- const wanted = (variant ?? "").trim().toLowerCase();
101
- const modern = names.filter((n) => /^createBundle.+JsAndAssets$/.test(n)).toSorted();
102
- if (wanted) {
103
- const match = modern.find((n) => n.toLowerCase() === `createbundle${wanted}jsandassets`);
104
- if (match) return match;
105
- } else if (modern.length === 1) return modern[0];
106
- if (!names.includes("react")) return null;
107
- const children = reactChildren.toSorted();
108
- if (wanted) {
109
- const match = children.find((n) => n.toLowerCase() === wanted);
110
- if (match) return `react/${match}`;
111
- } else if (children.length === 1) return `react/${children[0]}`;
112
- return null;
113
- }
114
- function androidModuleDirs(root, { list = safeList } = {}) {
115
- const android = join(root, "android");
116
- return ["app", ...list(android).filter((name) => name !== "app" && existsSync(join(android, name, "build", "generated", "res"))).toSorted()];
117
- }
118
- function findGeneratedResDir(root, variant, { list = safeList, layout = defaultAndroidLayout(root) } = {}) {
119
- const modules = layout.custom ? [layout.moduleDir] : androidModuleDirs(root, { list }).map((m) => join(root, "android", m));
120
- for (const moduleDir of modules) {
121
- const base = join(moduleDir, "build", "generated", "res");
122
- const names = list(base);
123
- if (names.length === 0) continue;
124
- const picked = pickGeneratedResDir(names, variant, names.includes("react") ? list(join(base, "react")) : []);
125
- if (picked) return join(base, ...picked.split("/"));
126
- }
127
- return null;
128
- }
129
- function captureAssetManifest(root, { variant = null, layout = defaultAndroidLayout(root), findDir = findGeneratedResDir, read = readAssetManifest } = {}) {
130
- const dir = findDir(root, variant, { layout });
131
- return dir ? read(dir) : null;
132
- }
133
- function compareAssetManifests(fresh, stored) {
134
- const left = new Map(fresh.assets.map((a) => [a.path, a.sha256]));
135
- const right = new Map(stored.assets.map((a) => [a.path, a.sha256]));
136
- const added = [];
137
- const changed = [];
138
- const removed = [];
139
- for (const [path, sha256] of left) if (!right.has(path)) added.push(path);
140
- else if (right.get(path) !== sha256) changed.push(path);
141
- for (const path of right.keys()) if (!left.has(path)) removed.push(path);
142
- added.sort();
143
- changed.sort();
144
- removed.sort();
145
- return {
146
- same: added.length === 0 && changed.length === 0 && removed.length === 0,
147
- added,
148
- removed,
149
- changed,
150
- example: added[0] ?? changed[0] ?? removed[0] ?? null
151
- };
152
- }
153
- function assetDiffReason(diff) {
154
- const which = diff.added.length ? "added" : diff.changed.length ? "changed" : "removed";
155
- return `this workspace emits a different asset set than the cached build did (${diff.added.length} added, ${diff.changed.length} changed, ${diff.removed.length} removed; e.g. ${which} ${diff.example})`;
156
- }
157
- function safeList(dir) {
158
- try {
159
- return readdirSync(dir);
160
- } catch {
161
- return [];
162
- }
163
- }
164
- //#endregion
165
- //#region src/cache/build-cache.ts
166
- function entryDir(platform, key, root = sharedBuildCache()) {
167
- return join$1(root, platform, key);
168
- }
169
- const FINGERPRINT_PLATFORMS = /* @__PURE__ */ new Set(["ios", "android"]);
170
- /**
171
- * Paths that exist in a working checkout and not in a fresh one, which
172
- * @expo/fingerprint does not ignore on its own. A fingerprint that counts them
173
- * cannot match across checkouts, which is the parity failure `doctor` reports.
174
- *
175
- * The list stays narrow on purpose: a path belongs here only when no native
176
- * build on any project can read it. Anything a single project can judge --
177
- * a lockfile with machine paths, a generated report -- belongs in that
178
- * project's `.fingerprintignore`, which still applies on top of this.
179
- */
180
- const DEFAULT_FINGERPRINT_IGNORES = ["**/android/local.properties", "**/android/.idea/**"];
181
- const APP_ROOT_IOS_SKIPPED = /* @__PURE__ */ new Set([
182
- "node_modules",
183
- "Pods",
184
- "build",
185
- "android",
186
- ".git",
187
- ".DS_Store",
188
- ".stim.json"
189
- ]);
190
- function gitIgnoredNames(projectRoot, names) {
191
- if (names.length === 0) return /* @__PURE__ */ new Set();
192
- const out = gitQuiet(projectRoot, [
193
- "check-ignore",
194
- "--",
195
- ...names
196
- ]);
197
- return new Set(String(out ?? "").split("\n").map((line) => line.trim()).filter(Boolean));
198
- }
199
- /**
200
- * Sources and ignores that make an `ios.projectPath` outside `ios/` count the way
201
- * @expo/fingerprint's bare sourcer counts `ios/`. A project directory that is the
202
- * app itself cannot be told apart from the app's JavaScript, so every top-level
203
- * entry git tracks, except dependencies and build output, is hashed: a JS change
204
- * misses the cache instead of risking a stale native build.
205
- */
206
- function iosProjectFingerprintOptions(projectRoot, iosProjectPath, { list = (dir) => readdirSync$1(dir), isDirectory = (path) => statSync$1(path).isDirectory(), ignored = gitIgnoredNames } = {}) {
207
- if (iosProjectPath === "ios") return {};
208
- const appRoot = iosProjectPath === ".";
209
- const prefix = appRoot ? "" : `${iosProjectPath}/`;
210
- const reasons = ["ios.projectPath"];
211
- let extraSources = [{
212
- type: "dir",
213
- filePath: iosProjectPath,
214
- reasons
215
- }];
216
- if (appRoot) {
217
- const names = list(projectRoot).filter((name) => !APP_ROOT_IOS_SKIPPED.has(name) && !name.startsWith(".watchman-cookie-"));
218
- const skip = ignored(projectRoot, names);
219
- extraSources = names.filter((name) => !skip.has(name)).toSorted((a, b) => a.localeCompare(b)).map((filePath) => ({
220
- type: isDirectory(join$1(projectRoot, filePath)) ? "dir" : "file",
221
- filePath,
222
- reasons
223
- }));
224
- }
225
- return {
226
- extraSources,
227
- ignorePaths: [
228
- `${prefix}Pods/**/*`,
229
- `${prefix}build/**/*`,
230
- `${prefix}.xcode.env.local`,
231
- `${prefix}**/project.xcworkspace`,
232
- `${prefix}*.xcworkspace/xcuserdata/**/*`
233
- ]
234
- };
235
- }
236
- const ANDROID_ROOT_SOURCES = [
237
- "settings.gradle",
238
- "settings.gradle.kts",
239
- "build.gradle",
240
- "build.gradle.kts",
241
- "gradle.properties",
242
- "gradle"
243
- ];
244
- function androidProjectFingerprintOptions(projectRoot, layout, exists = existsSync$1) {
245
- if (!layout.custom) return {};
246
- const reasons = ["android.gradleRoot"];
247
- const extraSources = ANDROID_ROOT_SOURCES.filter((name) => exists(join$1(layout.gradleRoot, name))).map((name) => ({
248
- type: name === "gradle" ? "dir" : "file",
249
- filePath: posix$1.join(layout.gradleRootRelative, name),
250
- reasons
251
- }));
252
- let appRoot = projectRoot;
253
- try {
254
- appRoot = realpathSync$1(projectRoot);
255
- } catch {}
256
- const fromApp = relative$1(appRoot, layout.moduleDir).split(sep$1).join("/");
257
- if (fromApp === "android" || fromApp.startsWith("android/")) return fromApp === "android/app" ? { extraSources } : {
258
- extraSources,
259
- ignorePaths: [
260
- "build",
261
- ".cxx",
262
- ".gradle"
263
- ].map((dir) => `${fromApp}/${dir}/**/*`)
264
- };
265
- const moduleRel = posix$1.join(layout.gradleRootRelative, ...layout.module.split(":").filter(Boolean));
266
- extraSources.push({
267
- type: "dir",
268
- filePath: moduleRel,
269
- reasons: ["android.module"]
270
- });
271
- return {
272
- extraSources,
273
- ignorePaths: [
274
- "build",
275
- ".cxx",
276
- ".gradle"
277
- ].map((dir) => `${moduleRel}/${dir}/**/*`)
278
- };
279
- }
280
- async function fingerprintProject(projectRoot, { platform, iosProjectPath = "ios", androidLayout = null, createFingerprint = expoFingerprint.createFingerprintAsync, debug = false } = {}) {
281
- const platforms = platform && FINGERPRINT_PLATFORMS.has(platform) ? { platforms: [platform] } : void 0;
282
- const ios = platform === "ios" ? iosProjectFingerprintOptions(projectRoot, iosProjectPath) : androidLayout?.custom ? androidProjectFingerprintOptions(projectRoot, androidLayout) : {};
283
- if (ios.extraSources) ios.extraSources = [...(await loadConfigAsync(projectRoot, true))?.extraSources ?? [], ...ios.extraSources];
284
- const result = await createFingerprint(projectRoot, {
285
- ...platforms,
286
- ...debug ? { debug } : {},
287
- ...ios.extraSources ? { extraSources: ios.extraSources } : {},
288
- ignorePaths: [...DEFAULT_FINGERPRINT_IGNORES, ...ios.ignorePaths ?? []],
289
- silent: true
290
- });
291
- const hash = result?.hash ?? null;
292
- if (!hash) return null;
293
- return {
294
- hash,
295
- sources: Array.isArray(result?.sources) ? result.sources : []
296
- };
297
- }
298
- function registerOnce(root) {
299
- try {
300
- register({
301
- dir: root,
302
- name: "Build cache",
303
- prune: "entries",
304
- entriesDepth: 2,
305
- note: "built .app/.apk keyed on the native fingerprint"
306
- });
307
- } catch {}
308
- }
309
- function resolveBuild(platform, key, root = sharedBuildCache()) {
310
- return resolveArtifact(entryDir(platform, key, root));
311
- }
312
- function storeBuild(platform, key, buildPath, rootOrOptions = {}) {
313
- const options = typeof rootOrOptions === "string" ? { root: rootOrOptions } : rootOrOptions || {};
314
- const root = options.root || sharedBuildCache();
315
- if (!buildPath || !existsSync$1(buildPath)) throw new Error(`No build to store at ${buildPath}`);
316
- registerOnce(root);
317
- return storeArtifact(entryDir(platform, key, root), buildPath, {
318
- runFile: (file, args) => getExecutor().runFile(file, args, { timeoutMs: "unbounded" }),
319
- overwrite: Boolean(options.overwrite),
320
- writeMetadata: (staging) => {
321
- if (Array.isArray(options.sources)) try {
322
- writeFileSync$1(join$1(staging, SOURCES_FILE), JSON.stringify(options.sources.map(withoutContents)));
323
- } catch {}
324
- if (options.assetManifest) try {
325
- writeFileSync$1(join$1(staging, ASSET_MANIFEST_FILE), JSON.stringify(options.assetManifest));
326
- } catch {}
327
- }
328
- });
329
- }
330
- const PROVIDER_DOWNLOAD_DIR = "cache-provider";
331
- function providerDownloadPath(workspacePath) {
332
- return join$1(workspacePath, PROVIDER_DOWNLOAD_DIR);
333
- }
334
- /**
335
- * Empties and registers the scratch directory a provider downloads into. It is
336
- * created only when a provider is about to be asked, and registered so `gc`
337
- * reports what an interrupted run left behind.
338
- */
339
- function prepareProviderDownloadDir(dir) {
340
- rmSync$1(dir, {
341
- recursive: true,
342
- force: true
343
- });
344
- mkdirSync$1(dir, { recursive: true });
345
- try {
346
- register({
347
- dir,
348
- name: "Cache provider downloads",
349
- prune: "entries",
350
- entriesDepth: 1,
351
- note: "artifacts fetched from the project cache provider; anything left here is from an interrupted run"
352
- });
353
- } catch {}
354
- }
355
- function filesystemBuildCapability({ root, sources, assetManifest, resolve = resolveBuild, store = storeBuild } = {}) {
356
- const stored = {
357
- ...root === void 0 ? {} : { root },
358
- ...sources === void 0 ? {} : { sources },
359
- ...assetManifest === void 0 ? {} : { assetManifest }
360
- };
361
- return {
362
- resolve: ({ platform, key }) => root === void 0 ? resolve(platform, key) : resolve(platform, key, root),
363
- store: ({ platform, key, sourcePath, overwrite }) => store(platform, key, sourcePath, {
364
- ...stored,
365
- overwrite
366
- })
367
- };
368
- }
369
- function providerUploadOutcome(result, name) {
370
- if (!result) return null;
371
- const label = name || "the cache provider";
372
- if (result.timedOut) return {
373
- line: `${label} upload was cancelled after ${buildUploadTimeoutMs()}ms`,
374
- warn: true
375
- };
376
- if (result.failed) return {
377
- line: `${label} upload failed: ${result.failed}`,
378
- warn: true
379
- };
380
- return {
381
- line: `uploaded (${label})`,
382
- warn: false
383
- };
384
- }
385
- const SOURCES_FILE = "fingerprint-sources.json";
386
- function withoutContents(source) {
387
- return source.type === "contents" ? {
388
- ...source,
389
- contents: ""
390
- } : source;
391
- }
392
- function storedSources(platform, key, root = sharedBuildCache()) {
393
- try {
394
- const parsed = JSON.parse(readFileSync$1(join$1(entryDir(platform, key, root), SOURCES_FILE), "utf-8"));
395
- return Array.isArray(parsed) ? parsed : null;
396
- } catch {
397
- return null;
398
- }
399
- }
400
- function storedAssetManifest(platform, key, root = sharedBuildCache()) {
401
- try {
402
- return parseAssetManifest(readFileSync$1(join$1(entryDir(platform, key, root), ASSET_MANIFEST_FILE), "utf-8"));
403
- } catch {
404
- return null;
405
- }
406
- }
407
- function sourceName(source) {
408
- const s = source;
409
- if (typeof s?.filePath === "string" && s.filePath !== "") return s.filePath;
410
- if (typeof s?.id === "string" && s.id !== "") return s.id;
411
- return null;
412
- }
413
- function sourceReasons(source) {
414
- const reasons = source?.reasons;
415
- return Array.isArray(reasons) ? reasons.filter((reason) => typeof reason === "string") : [];
416
- }
417
- function diffItemChange(item) {
418
- const o = item;
419
- const [change, source] = o?.op === "added" ? ["added", o.addedSource] : o?.op === "removed" ? ["removed", o.removedSource] : ["changed", o?.afterSource ?? o?.beforeSource];
420
- const name = sourceName(source);
421
- return name === null ? null : {
422
- name,
423
- change,
424
- reasons: sourceReasons(source)
425
- };
426
- }
427
- function compareSourceLists(previous, current) {
428
- const previousByName = /* @__PURE__ */ new Map();
429
- for (const source of previous) {
430
- const name = sourceName(source);
431
- if (name !== null) previousByName.set(name, {
432
- hash: source.hash ?? null,
433
- reasons: sourceReasons(source)
434
- });
435
- }
436
- const changes = [];
437
- const seen = /* @__PURE__ */ new Set();
438
- for (const source of current) {
439
- const name = sourceName(source);
440
- if (name === null || seen.has(name)) continue;
441
- seen.add(name);
442
- const hash = source.hash ?? null;
443
- const before = previousByName.get(name);
444
- if (!before) changes.push({
445
- name,
446
- change: "added",
447
- reasons: sourceReasons(source)
448
- });
449
- else if (before.hash !== hash) changes.push({
450
- name,
451
- change: "changed",
452
- reasons: sourceReasons(source)
453
- });
454
- }
455
- for (const [name, before] of previousByName) if (!seen.has(name)) changes.push({
456
- name,
457
- change: "removed",
458
- reasons: before.reasons
459
- });
460
- return changes;
461
- }
462
- function diffFingerprintSources({ previous, previousHash = null, current, differ = null }) {
463
- if (typeof differ === "function") try {
464
- const items = differ({
465
- sources: previous,
466
- hash: previousHash ?? ""
467
- }, {
468
- sources: current.sources,
469
- hash: current.hash
470
- });
471
- if (Array.isArray(items)) {
472
- const changes = [];
473
- const seen = /* @__PURE__ */ new Set();
474
- for (const item of items) {
475
- const change = diffItemChange(item);
476
- if (change !== null && !seen.has(change.name)) {
477
- seen.add(change.name);
478
- changes.push(change);
479
- }
480
- }
481
- return changes;
482
- }
483
- } catch {}
484
- return compareSourceLists(previous, current.sources);
485
- }
486
- async function refingerprintAfterMutation({ projectRoot, platform, previousHash, iosProjectPath, androidLayout, fingerprint = fingerprintProject }) {
487
- let computed = null;
488
- try {
489
- computed = await fingerprint(projectRoot, {
490
- platform,
491
- ...iosProjectPath ? { iosProjectPath } : {},
492
- ...androidLayout ? { androidLayout } : {}
493
- });
494
- } catch {
495
- return null;
496
- }
497
- if (!computed?.hash) return null;
498
- return {
499
- hash: computed.hash,
500
- sources: computed.sources,
501
- moved: computed.hash !== previousHash
502
- };
503
- }
504
- const CONFIG_INPUT_REASONS = /* @__PURE__ */ new Set([
505
- "expoConfig",
506
- "expoConfigPlugins",
507
- "expoConfigExternalFile"
508
- ]);
509
- function writtenByBuild(name, platform) {
510
- return name === platform || name.startsWith(`${platform}/`) || name.startsWith("node_modules/") || name.includes("/node_modules/");
511
- }
512
- const PREBUILD_WRITTEN_IDS = [["ios", "bundleIdentifier"], ["android", "package"]];
513
- function expoConfigContents(sources) {
514
- const source = sources.find((s) => s.type === "contents" && s.id === "expoConfig");
515
- if (source?.type !== "contents") return null;
516
- try {
517
- const parsed = JSON.parse(String(source.contents));
518
- return parsed && typeof parsed === "object" ? parsed : null;
519
- } catch {
520
- return null;
521
- }
522
- }
523
- function onlyPrebuildIdsAdded(before, after) {
524
- const previous = expoConfigContents(before);
525
- const current = expoConfigContents(after);
526
- if (!previous || !current) return false;
527
- const stripped = { ...current };
528
- for (const [section, key] of PREBUILD_WRITTEN_IDS) {
529
- const was = previous[section];
530
- const now = stripped[section];
531
- if (now?.[key] === void 0 || was?.[key] !== void 0) continue;
532
- const rest = { ...now };
533
- delete rest[key];
534
- if (was === void 0 && Object.keys(rest).length === 0) delete stripped[section];
535
- else stripped[section] = rest;
536
- }
537
- return isDeepStrictEqual(previous, stripped);
538
- }
539
- function configInputsChanged(before, after, { prebuildRan }) {
540
- return compareSourceLists(before, after).filter((change) => change.reasons.some((reason) => CONFIG_INPUT_REASONS.has(reason))).filter((change) => !(prebuildRan && change.name === "expoConfig" && onlyPrebuildIdsAdded(before, after))).map((change) => change.name);
541
- }
542
- function inputsChangedDuringBuild({ platform, lookup, prebuildRan, compiled, current }) {
543
- const names = new Set(configInputsChanged(lookup, current, { prebuildRan }));
544
- for (const change of compareSourceLists(compiled, current)) if (!writtenByBuild(change.name, platform)) names.add(change.name);
545
- return [...names];
546
- }
547
- function changedDuringBuildLine(changed) {
548
- const shown = changed.slice(0, 3);
549
- const more = changed.length > shown.length ? `, and ${changed.length - shown.length} more` : "";
550
- return `${shown.join(", ")}${more} changed while the build ran, so the artifact may not match its key; the build will be installed but not cached`;
551
- }
552
- function untrackedNativeFiles({ projectRoot, iosProjectPath = "ios" }) {
553
- let out;
554
- try {
555
- out = git(projectRoot, [
556
- "ls-files",
557
- "--others",
558
- "--exclude-standard",
559
- "--",
560
- "ios",
561
- "android",
562
- ...iosProjectPath !== "ios" ? [iosProjectPath] : []
563
- ]);
564
- } catch {
565
- return [];
566
- }
567
- return String(out || "").split("\n").map((line) => line.trim()).filter((line) => line !== "");
568
- }
569
- function untrackedMissLine(files, cap = 3) {
570
- if (!Array.isArray(files) || files.length === 0) return null;
571
- const shown = files.slice(0, cap);
572
- const more = files.length > shown.length ? `, and ${files.length - shown.length} more` : "";
573
- return `no previous entry to diff against; ${files.length} untracked file${files.length === 1 ? "" : "s"} under ios/ or android/ are hashed like any other source: ${shown.join(", ")}${more} -- list the build-irrelevant ones in .fingerprintignore`;
574
- }
575
- function fingerprintDiffRecord({ changed, previousHash, hash }) {
576
- const shown = changed.slice(0, 20);
577
- return {
578
- src: "build",
579
- level: "info",
580
- event: "fingerprint_diff",
581
- msg: `fingerprint ${previousHash} -> ${hash}: ${changed.length} source${changed.length === 1 ? "" : "s"} changed: ` + shown.join(", ") + (changed.length > shown.length ? `, and ${changed.length - shown.length} more` : ""),
582
- changed: changed.length,
583
- sources: shown
584
- };
585
- }
586
- //#endregion
587
- //#region src/engine/build-lock.ts
588
- const LOCK_SUFFIX$1 = ".lock";
589
- const WAIT_POLL_MS = 1e3;
590
- const WAIT_PROGRESS_MS = 3e4;
591
- const WAIT_CEILING_MS = 54e5;
592
- function sleepAsync(ms) {
593
- return new Promise((resolve) => setTimeout(resolve, ms));
594
- }
595
- function segment(value) {
596
- return String(value).replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^\.+/, "") || "unknown";
597
- }
598
- function buildLocksDir() {
599
- return join(getConfigDir(), "build-locks");
600
- }
601
- function buildLockPath(platform, key) {
602
- return join(buildLocksDir(), `${segment(platform)}-${segment(key)}${LOCK_SUFFIX$1}`);
603
- }
604
- function label(platform) {
605
- return `${platform} build`;
606
- }
607
- function toRecord(holder) {
608
- const details = holder.details;
609
- return {
610
- pid: holder.owner.pid,
611
- projectRoot: typeof details.projectRoot === "string" ? details.projectRoot : null,
612
- startedAt: holder.startedAt || null,
613
- logFile: typeof details.logFile === "string" ? details.logFile : null
614
- };
615
- }
616
- function readBuildLock(pathOrSpec) {
617
- const path = typeof pathOrSpec === "string" ? pathOrSpec : buildLockPath(pathOrSpec.platform, pathOrSpec.key);
618
- const survey = (0, ownership_claim_exports.readClaimSet)(path);
619
- const holder = survey.live[0] ?? survey.dead[0];
620
- return holder ? toRecord(holder) : null;
621
- }
622
- function acquireBuildLock({ platform, key, root = null, logFile = null }) {
623
- const path = buildLockPath(platform, key);
624
- const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
625
- root: path,
626
- mode: "exclusive",
627
- label: label(platform),
628
- details: {
629
- projectRoot: root,
630
- logFile
631
- }
632
- });
633
- const reaped = attempt.reaped[0];
634
- if (attempt.acquired) {
635
- const handle = {
636
- acquired: true,
637
- path,
638
- lock: {
639
- pid: attempt.acquired.owner.pid,
640
- projectRoot: root,
641
- startedAt: attempt.acquired.startedAt,
642
- logFile
643
- },
644
- claim: attempt.acquired
645
- };
646
- declareSpawnsOn(attempt.acquired);
647
- return reaped ? {
648
- ...handle,
649
- tookOver: toRecord(reaped)
650
- } : handle;
651
- }
652
- if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
653
- const holder = attempt.held ?? attempt.waitingFor?.[0];
654
- return holder ? {
655
- held: toRecord(holder),
656
- path
657
- } : { path };
658
- }
659
- function releaseBuildLock(handle) {
660
- stopDeclaringSpawnsOn(handle?.claim);
661
- return (0, ownership_claim_exports.releaseClaim)(handle?.claim);
662
- }
663
- function listBuildLocks() {
664
- const dir = buildLocksDir();
665
- let names;
666
- try {
667
- names = readdirSync(dir);
668
- } catch {
669
- return [];
670
- }
671
- const locks = [];
672
- for (const name of names) {
673
- if (!name.endsWith(LOCK_SUFFIX$1)) continue;
674
- const path = join(dir, name);
675
- try {
676
- if (!statSync(path).isDirectory()) continue;
677
- } catch {
678
- continue;
679
- }
680
- const stem = name.slice(0, -5);
681
- const cut = stem.indexOf("-");
682
- const survey = (0, ownership_claim_exports.readClaimSet)(path);
683
- const holder = survey.live[0] ?? survey.dead[0];
684
- const info = holder ? toRecord(holder) : null;
685
- locks.push({
686
- path,
687
- name,
688
- platform: cut > 0 ? stem.slice(0, cut) : stem,
689
- key: cut > 0 ? stem.slice(cut + 1) : null,
690
- pid: info?.pid ?? null,
691
- projectRoot: info?.projectRoot ?? null,
692
- startedAt: info?.startedAt ?? null,
693
- logFile: info?.logFile ?? null,
694
- alive: survey.live.length > 0,
695
- unresolved: survey.unresolved.length > 0
696
- });
697
- }
698
- return locks;
699
- }
700
- function waitingLine({ projectRoot, pid, elapsedMs, logFile }) {
701
- const where = projectRoot || "another workspace";
702
- const tail = logFile ? ` -- tail ${logFile}` : "";
703
- return `${"build".padEnd(11)} waiting on ${where} (pid ${pid ?? "?"}, ${formatElapsed(elapsedMs)} elapsed)${tail} -- stim guide lifecycle concurrency`;
704
- }
705
- function takeoverLine({ projectRoot, pid, logFile, startedAt = null, now = Date.now }) {
706
- const where = projectRoot || "another workspace";
707
- const started = startedAt ? Date.parse(startedAt) : NaN;
708
- const ago = Number.isFinite(started) ? ` (it started ${formatElapsed(now() - started)} ago)` : "";
709
- const tail = logFile ? ` -- read ${logFile} before this one finishes` : "";
710
- return `RETRY: ${where}'s build of this fingerprint (pid ${pid ?? "?"}) FAILED without an artifact${ago}, and this run rebuilds the SAME inputs, so expect the same failure unless something changed${tail}`;
711
- }
712
- async function waitForBuild({ platform, key, resolve = resolveBuild, intervalMs = WAIT_POLL_MS, progressMs = WAIT_PROGRESS_MS, ceilingMs = WAIT_CEILING_MS, out = () => {}, now = Date.now, sleep = sleepAsync }) {
713
- const path = buildLockPath(platform, key);
714
- const started = now();
715
- let lastProgress = started;
716
- let holder = readBuildLock(path);
717
- for (;;) {
718
- const hit = resolve(platform, key);
719
- if (hit) return {
720
- hit,
721
- waitedMs: now() - started
722
- };
723
- const survey = (0, ownership_claim_exports.readClaimSet)(path);
724
- const unresolved = survey.unresolved[0];
725
- if (unresolved) throw new ownership_claim_exports.ClaimRefusedError({
726
- claimPath: unresolved.path,
727
- root: path,
728
- label: label(platform),
729
- reason: unresolved.reason
730
- });
731
- const live = survey.live[0];
732
- if (live) holder = toRecord(live);
733
- else {
734
- const dead = survey.dead[0];
735
- if (dead) return {
736
- builderFailed: `the builder (pid ${dead.owner.pid}) is gone`,
737
- holder: toRecord(dead),
738
- waitedMs: now() - started
739
- };
740
- return {
741
- lockReleased: true,
742
- holder,
743
- waitedMs: now() - started
744
- };
745
- }
746
- const elapsed = now() - started;
747
- if (elapsed >= ceilingMs) {
748
- const err = /* @__PURE__ */ new Error(`Waited ${formatElapsed(elapsed)} for ${holder?.projectRoot || "another workspace"}'s ${platform} build of ${key} without an artifact, and pid ${holder?.pid} is still the live holder. The lock is ${path}; remove it if that process is not really building:\n ${(0, ownership_claim_exports.claimRemoveCommand)(path)}`);
749
- err.code = "STIM_BUILD_WAIT_TIMEOUT";
750
- err.lockPath = path;
751
- err.holder = holder ?? void 0;
752
- throw err;
753
- }
754
- if (now() - lastProgress >= progressMs) {
755
- lastProgress = now();
756
- out(waitingLine({
757
- projectRoot: holder.projectRoot,
758
- pid: holder.pid,
759
- elapsedMs: elapsed,
760
- logFile: holder.logFile
761
- }));
762
- }
763
- await sleep(intervalMs);
764
- }
765
- }
766
- async function waitForSharedBuild({ platform, key, fingerprint, root, logFile, command, acquire, wait, now, phase, waitingOn, warn, out }) {
767
- const waitStarted = now();
768
- let failedHolder;
769
- let released = null;
770
- for (;;) {
771
- let attempt = null;
772
- try {
773
- attempt = acquire({
774
- platform,
775
- key,
776
- root,
777
- logFile
778
- });
779
- } catch (err) {
780
- const refusal = (0, ownership_claim_exports.claimFailure)(err, command);
781
- if (refusal) return { refusal };
782
- warn(`could not take the build lock: ${err?.message || err}; building anyway`);
783
- }
784
- if (attempt?.acquired) {
785
- const previous = attempt.tookOver ?? failedHolder;
786
- if (previous) warn(takeoverLine(previous));
787
- waitingOn(null);
788
- return {
789
- refusal: null,
790
- lock: attempt,
791
- hit: null,
792
- released
793
- };
794
- }
795
- if (!attempt?.held) {
796
- waitingOn(null);
797
- return {
798
- refusal: null,
799
- lock: null,
800
- hit: null,
801
- released
802
- };
803
- }
804
- released = null;
805
- const holder = attempt.held;
806
- const who = holder.projectRoot || "another workspace";
807
- phase(`${who} is already building ${shortHash(fingerprint)} (pid ${holder.pid})${holder.logFile ? ` -- tail ${holder.logFile}` : ""} -- stim guide lifecycle concurrency`);
808
- waitingOn(holder.projectRoot || null);
809
- let waited;
810
- try {
811
- const ceilingMs = WAIT_CEILING_MS - (now() - waitStarted);
812
- if (ceilingMs <= 0) throw Object.assign(/* @__PURE__ */ new Error(`Waited ${formatDuration(now() - waitStarted)} for shared builds without an artifact; ${who} (pid ${holder.pid}) holds ${attempt.path}.`), {
813
- code: "STIM_BUILD_WAIT_TIMEOUT",
814
- lockPath: attempt.path
815
- });
816
- waited = await wait({
817
- platform,
818
- key,
819
- out,
820
- ceilingMs
821
- });
822
- } catch (err) {
823
- const refusal = (0, ownership_claim_exports.claimFailure)(err, command);
824
- if (refusal) return { refusal };
825
- const timeout = err;
826
- if (timeout?.code !== "STIM_BUILD_WAIT_TIMEOUT") throw err;
827
- return { refusal: {
828
- code: "STIM_BUILD_WAIT_TIMEOUT",
829
- message: timeout.message,
830
- remedy: `Check pid ${holder.pid}; if it is not really building, remove ${timeout.lockPath} and run \`${command}\` again.`
831
- } };
832
- }
833
- if (waited.hit) {
834
- waitingOn(null);
835
- phase(`waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
836
- return {
837
- refusal: null,
838
- lock: null,
839
- hit: {
840
- path: waited.hit,
841
- waited: {
842
- pid: holder.pid,
843
- ms: waited.waitedMs
844
- }
845
- },
846
- released
847
- };
848
- }
849
- if (waited.lockReleased) {
850
- failedHolder = void 0;
851
- released = {
852
- facts: {
853
- pid: holder.pid,
854
- ms: waited.waitedMs
855
- },
856
- who
857
- };
858
- phase(`${who}'s build lock was released; rechecking this workspace before building`);
859
- } else {
860
- warn(`${who}'s build ended without an artifact (${waited.builderFailed}); retrying the build lock`);
861
- failedHolder = holder;
862
- }
863
- }
864
- }
865
- const DEFAULT_LEASE_MS = 3e5;
866
- const LEASE_SUFFIX = ".json";
867
- const LOCK_SUFFIX = ".lock";
868
- const DURATION = /^[1-9][0-9]*(s|m)$/;
869
- function deviceLocksDir() {
870
- return join(getConfigDir(), "device-locks");
871
- }
872
- function leaseStem(platform, id) {
873
- return `${segment(platform)}-${segment(id)}`;
874
- }
875
- function deviceLeasePath(platform, id) {
876
- return join(deviceLocksDir(), `${leaseStem(platform, id)}${LEASE_SUFFIX}`);
877
- }
878
- function deviceLeaseLockPath(platform, id) {
879
- return join(deviceLocksDir(), `${leaseStem(platform, id)}${LOCK_SUFFIX}`);
880
- }
881
- const fileLeaseIo = {
882
- withHolderLock: withWorkspaceStateLock,
883
- now: () => Date.now(),
884
- readLease(path) {
885
- try {
886
- return readFileSync(path, "utf-8");
887
- } catch {
888
- return null;
889
- }
890
- },
891
- writeLease(path, body) {
892
- mkdirSync(deviceLocksDir(), { recursive: true });
893
- const tmp = `${path}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
894
- writeFileSync(tmp, body);
895
- try {
896
- renameSync(tmp, path);
897
- } catch (error) {
898
- rmSync(tmp, { force: true });
899
- throw error;
900
- }
901
- },
902
- removeLease(path) {
903
- rmSync(path, { force: true });
904
- },
905
- listLeaseNames() {
906
- try {
907
- return readdirSync(deviceLocksDir());
908
- } catch {
909
- return [];
910
- }
911
- },
912
- withLeaseLock(lockPath, fn) {
913
- return withDirLock$1(lockPath, fn, { ensureParent: () => mkdirSync(deviceLocksDir(), { recursive: true }) });
914
- },
915
- readHolder(root) {
916
- return parseWorkspaceLeases(readWorkspaceState$1(root)?.deviceLeases);
917
- },
918
- writeHolder(root, leases) {
919
- if (Object.keys(leases).length === 0) clearWorkspaceStateKeys(root, ["deviceLeases"]);
920
- else writeWorkspaceState(root, { deviceLeases: leases });
921
- }
922
- };
923
- function text(value) {
924
- return typeof value === "string" && value.trim() !== "" ? value : null;
925
- }
926
- function isPlatform(value) {
927
- return value === "ios" || value === "android";
928
- }
929
- function parseLease(raw) {
930
- if (typeof raw !== "string") return null;
931
- let parsed;
932
- try {
933
- parsed = JSON.parse(raw);
934
- } catch {
935
- return null;
936
- }
937
- if (!isJsonObject(parsed)) return null;
938
- const entry = parsed;
939
- if (entry.version !== 1 || !isPlatform(entry.platform)) return null;
940
- const id = text(entry.id);
941
- const holder = text(entry.holder);
942
- const token = text(entry.token);
943
- const expiresAt = text(entry.expiresAt);
944
- if (!id || !holder || !token || !expiresAt || !Number.isFinite(Date.parse(expiresAt))) return null;
945
- if (entry.slot !== void 0) {
946
- if (typeof entry.slot !== "string") return null;
947
- try {
948
- validateDeviceSlot(entry.slot);
949
- } catch {
950
- return null;
951
- }
952
- }
953
- return {
954
- version: 1,
955
- ...typeof entry.slot === "string" ? { slot: entry.slot } : {},
956
- platform: entry.platform,
957
- id,
958
- deviceName: text(entry.deviceName),
959
- holder,
960
- token,
961
- grantedAt: text(entry.grantedAt),
962
- expiresAt
963
- };
964
- }
965
- function parseWorkspaceLeases(value) {
966
- if (!value || typeof value !== "object" || Array.isArray(value)) return {};
967
- const leases = {};
968
- for (const [platform, entry] of Object.entries(value)) {
969
- if (!parseDeviceSlotKey(platform) || !entry || typeof entry !== "object") continue;
970
- const record = entry;
971
- const id = text(record.id);
972
- const token = text(record.token);
973
- if (!id || !token) continue;
974
- leases[platform] = {
975
- id,
976
- token,
977
- kind: record.kind === "run" ? "run" : "declared"
978
- };
979
- }
980
- return leases;
981
- }
982
- function leaseIsExpired(lease, now) {
983
- return Date.parse(lease.expiresAt) <= now;
984
- }
985
- function parseLeaseDuration(value) {
986
- const raw = String(value ?? "").trim();
987
- const match = DURATION.exec(raw);
988
- if (!match) return { error: `--for must be a whole number of seconds or minutes, such as 90s or 5m (got "${raw}").` };
989
- const ms = Number(raw.slice(0, -1)) * (match[1] === "s" ? 1e3 : 6e4);
990
- if (ms < 1e4 || ms > 18e5) return { error: `--for must be between 10s and 30m (got "${raw}").` };
991
- return { ms };
992
- }
993
- function listLeaseFiles(io = fileLeaseIo) {
994
- const dir = deviceLocksDir();
995
- const entries = [];
996
- for (const name of io.listLeaseNames().toSorted()) {
997
- if (!name.endsWith(LEASE_SUFFIX)) continue;
998
- const path = join(dir, name);
999
- const lease = parseLease(io.readLease(path));
1000
- const stem = name.slice(0, -5);
1001
- const cut = stem.indexOf("-");
1002
- entries.push({
1003
- path,
1004
- name,
1005
- platform: lease?.platform ?? (cut > 0 ? stem.slice(0, cut) : stem),
1006
- id: lease?.id ?? (cut > 0 ? stem.slice(cut + 1) : null),
1007
- lease
1008
- });
1009
- }
1010
- return entries;
1011
- }
1012
- function writeLease(lease, io) {
1013
- io.writeLease(deviceLeasePath(lease.platform, lease.id), `${JSON.stringify(lease, null, 2)}\n`);
1014
- return lease;
1015
- }
1016
- function takeLease({ root, platform, slot = "default", id, deviceName = null, kind, durationMs = DEFAULT_LEASE_MS }, io = fileLeaseIo) {
1017
- return withHolderLock(io, root, () => {
1018
- const key = deviceSlotKey(platform, slot);
1019
- const previous = io.readHolder(root)[key];
1020
- if (previous && previous.id !== id) releaseHeld(root, platform, previous, io, slot);
1021
- for (const entry of listLeaseFiles(io)) {
1022
- const other = entry.lease;
1023
- if (!other || other.platform !== platform || other.holder !== root || (other.slot ?? "default") !== slot || other.id === id) continue;
1024
- releaseByRoot(root, platform, other.id, io);
1025
- }
1026
- const declared = previous?.kind === "declared" && previous.id === id;
1027
- const path = deviceLeasePath(platform, id);
1028
- const result = io.withLeaseLock(deviceLeaseLockPath(platform, id), () => {
1029
- const now = io.now();
1030
- const raw = io.readLease(path);
1031
- const current = parseLease(raw);
1032
- if (raw !== null && !current) return {
1033
- status: "unreadable",
1034
- path
1035
- };
1036
- const asked = now + durationMs;
1037
- if (current && !leaseIsExpired(current, now)) {
1038
- if (current.holder !== root || (current.slot ?? "default") !== slot) return {
1039
- status: "held",
1040
- lease: current
1041
- };
1042
- const floor = declared && kind === "run" ? Math.max(asked, Date.parse(current.expiresAt)) : asked;
1043
- return {
1044
- status: "set",
1045
- lease: writeLease({
1046
- ...current,
1047
- deviceName: deviceName ?? current.deviceName,
1048
- expiresAt: new Date(floor).toISOString()
1049
- }, io)
1050
- };
1051
- }
1052
- const expiresAt = new Date(asked).toISOString();
1053
- return {
1054
- status: "taken",
1055
- lease: writeLease({
1056
- version: 1,
1057
- ...slot === "default" ? {} : { slot },
1058
- platform,
1059
- id,
1060
- deviceName,
1061
- holder: root,
1062
- token: randomBytes(12).toString("hex"),
1063
- grantedAt: new Date(now).toISOString(),
1064
- expiresAt
1065
- }, io)
1066
- };
1067
- });
1068
- if (result.status === "taken" || result.status === "set") {
1069
- const recorded = result.status === "set" && declared ? "declared" : kind;
1070
- io.writeHolder(root, {
1071
- ...io.readHolder(root),
1072
- [key]: {
1073
- id,
1074
- token: result.lease.token,
1075
- kind: recorded
1076
- }
1077
- });
1078
- }
1079
- return result;
1080
- });
1081
- }
1082
- function raiseLease({ root, platform, slot = "default", minMs }, io = fileLeaseIo) {
1083
- const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
1084
- if (!record) return { status: "none" };
1085
- const path = deviceLeasePath(platform, record.id);
1086
- return io.withLeaseLock(deviceLeaseLockPath(platform, record.id), () => {
1087
- const current = parseLease(io.readLease(path));
1088
- if (!current || current.token !== record.token) return {
1089
- status: "lost",
1090
- lease: current
1091
- };
1092
- const floor = io.now() + minMs;
1093
- if (Date.parse(current.expiresAt) >= floor) return {
1094
- status: "raised",
1095
- lease: current
1096
- };
1097
- return {
1098
- status: "raised",
1099
- lease: writeLease({
1100
- ...current,
1101
- expiresAt: new Date(floor).toISOString()
1102
- }, io)
1103
- };
1104
- });
1105
- }
1106
- function summarize(lease) {
1107
- return {
1108
- platform: lease.platform,
1109
- id: lease.id,
1110
- deviceName: lease.deviceName,
1111
- expiresAt: lease.expiresAt
1112
- };
1113
- }
1114
- function releaseHeld(root, platform, record, io, slot = "default") {
1115
- const key = deviceSlotKey(platform, slot);
1116
- const path = deviceLeasePath(platform, record.id);
1117
- const released = io.withLeaseLock(deviceLeaseLockPath(platform, record.id), () => {
1118
- const current = parseLease(io.readLease(path));
1119
- if (!current || current.token !== record.token) return null;
1120
- io.removeLease(path);
1121
- return current;
1122
- });
1123
- const held = io.readHolder(root);
1124
- if (held[key]?.token === record.token) {
1125
- delete held[key];
1126
- io.writeHolder(root, held);
1127
- }
1128
- return released;
1129
- }
1130
- function releaseByRoot(root, platform, id, io) {
1131
- const path = deviceLeasePath(platform, id);
1132
- return io.withLeaseLock(deviceLeaseLockPath(platform, id), () => {
1133
- const current = parseLease(io.readLease(path));
1134
- if (!current || current.holder !== root) return null;
1135
- io.removeLease(path);
1136
- return current;
1137
- });
1138
- }
1139
- function releaseRunLease({ root, platform, slot = "default" }, io = fileLeaseIo) {
1140
- return withHolderLock(io, root, () => {
1141
- const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
1142
- if (!record || record.kind !== "run") return null;
1143
- const released = releaseHeld(root, platform, record, io, slot);
1144
- return released ? summarize(released) : null;
1145
- });
1146
- }
1147
- function releaseWorkspaceLeases(root, { platform = null, slot = null } = {}, io = fileLeaseIo) {
1148
- return withHolderLock(io, root, () => {
1149
- const released = [];
1150
- for (const [name, record] of Object.entries(io.readHolder(root))) {
1151
- const parsed = parseDeviceSlotKey(name);
1152
- if (!parsed || platform && parsed.platform !== platform || slot !== null && parsed.slot !== slot) continue;
1153
- const lease = releaseHeld(root, parsed.platform, record, io, parsed.slot);
1154
- if (lease) released.push(summarize(lease));
1155
- }
1156
- for (const entry of listLeaseFiles(io)) {
1157
- const lease = entry.lease;
1158
- if (!lease || lease.holder !== root) continue;
1159
- if (platform && lease.platform !== platform) continue;
1160
- if (slot !== null && (lease.slot ?? "default") !== slot) continue;
1161
- const byRoot = releaseByRoot(root, lease.platform, lease.id, io);
1162
- if (byRoot) released.push(summarize(byRoot));
1163
- }
1164
- return released;
1165
- });
1166
- }
1167
- function removeExpiredLease(entry, io = fileLeaseIo) {
1168
- const lease = entry.lease;
1169
- if (!lease) return false;
1170
- return io.withLeaseLock(deviceLeaseLockPath(lease.platform, lease.id), () => {
1171
- const current = parseLease(io.readLease(entry.path));
1172
- if (!current || !leaseIsExpired(current, io.now())) return false;
1173
- io.removeLease(entry.path);
1174
- return true;
1175
- });
1176
- }
1177
- function selectPoolDevice({ candidates, leases, held = null, now }) {
1178
- const ordered = candidates.toSorted((a, b) => {
1179
- const left = a.id.toLowerCase();
1180
- const right = b.id.toLowerCase();
1181
- return left < right ? -1 : left > right ? 1 : 0;
1182
- });
1183
- if (held) {
1184
- const mine = ordered.find((candidate) => candidate.id === held);
1185
- return mine ? {
1186
- status: "selected",
1187
- candidate: mine
1188
- } : {
1189
- status: "held-disconnected",
1190
- id: held
1191
- };
1192
- }
1193
- const preferred = ordered.filter((candidate) => !candidate.fallback);
1194
- const pool = preferred.length > 0 ? preferred : ordered;
1195
- if (pool.length === 0) return { status: "none" };
1196
- const byId = new Map(leases.map((lease) => [lease.id, lease]));
1197
- const free = (candidate) => {
1198
- const lease = byId.get(candidate.id);
1199
- return !lease || leaseIsExpired(lease, now);
1200
- };
1201
- const holders = [];
1202
- for (const candidate of pool) {
1203
- if (free(candidate)) return {
1204
- status: "selected",
1205
- candidate
1206
- };
1207
- const lease = byId.get(candidate.id);
1208
- holders.push({
1209
- id: lease.id,
1210
- holder: lease.holder,
1211
- expiresAt: lease.expiresAt
1212
- });
1213
- }
1214
- return {
1215
- status: "busy",
1216
- holders,
1217
- spare: ordered.filter((candidate) => candidate.fallback && free(candidate)).map((candidate) => candidate.id)
1218
- };
1219
- }
1220
- function withHolderLock(io, root, fn) {
1221
- return io.withHolderLock ? io.withHolderLock(root, fn) : fn();
1222
- }
1223
- //#endregion
1224
- export { providerDownloadPath as A, captureAssetManifest as B, diffFingerprintSources as C, fingerprintProject as D, fingerprintDiffRecord as E, storedAssetManifest as F, readAssetManifest as H, storedSources as I, untrackedMissLine as L, refingerprintAfterMutation as M, resolveBuild as N, inputsChangedDuringBuild as O, storeBuild as P, untrackedNativeFiles as R, configInputsChanged as S, filesystemBuildCapability as T, compareAssetManifests as V, waitForBuild as _, parseLease as a, buildCacheKey as b, raiseLease as c, removeExpiredLease as d, selectPoolDevice as f, releaseBuildLock as g, listBuildLocks as h, listLeaseFiles as i, providerUploadOutcome as j, prepareProviderDownloadDir as k, releaseRunLease as l, acquireBuildLock as m, fileLeaseIo as n, parseLeaseDuration as o, takeLease as p, leaseIsExpired as r, parseWorkspaceLeases as s, deviceLeasePath as t, releaseWorkspaceLeases as u, waitForSharedBuild as v, entryDir as w, changedDuringBuildLine as x, artifactIn as y, assetDiffReason as z };