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.
- package/README.md +141 -8
- package/dist/acquire-artifact-Co2k4f7O.mjs +171 -0
- package/dist/act-DsuqEkiM.mjs +2801 -0
- package/dist/activity-CMLmzp7o.mjs +1397 -0
- package/dist/{agent-device-usage-output-Dzgv6lgR.mjs → agent-device-usage-output-DUFaTMdd.mjs} +23 -67
- package/dist/{android-DCKZrNvV.mjs → android-ByRMUoiJ.mjs} +157 -93
- package/dist/{android-Mz_Inn3w.mjs → android-C7sVmyI9.mjs} +299 -116
- package/dist/{android-Dlzxshgp.mjs → android-DGkNqa_c.mjs} +5 -5
- package/dist/{android-HqP1_vB_.mjs → android-DR5lZ2Az.mjs} +2 -2
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{android-cas-Bl-cLJBk.mjs → android-cas-ngferVq2.mjs} +7 -6
- package/dist/android-project-BCAAmMwz.mjs +12 -0
- package/dist/android-project-DVWyCbWU.d.mts +391 -0
- package/dist/api.d.mts +810 -64
- package/dist/api.mjs +782 -1
- package/dist/{artifact-HSwkIWEQ.mjs → artifact-8EY8SAPw.mjs} +240 -213
- package/dist/{artifact-D3czlUvT.mjs → artifact-DJPy518Y.mjs} +203 -164
- package/dist/artifact-platform-CXcmX_j3.mjs +27 -0
- package/dist/{browser-web-CA-F1GQo.mjs → browser-web-Bcv99PBM.mjs} +194 -64
- package/dist/budget-Cvwn54qV.mjs +9285 -0
- package/dist/{build-DYDbfkk5.mjs → build-CabC3G23.mjs} +153 -98
- package/dist/build-cache-Dv7RN200.mjs +602 -0
- package/dist/build-plan-DUC8Ka9y.mjs +248 -0
- package/dist/{cache-manifest-XH-fvDUj.mjs → cache-manifest-DSeQjt-Q.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{ccache-zdKiYQ3E.mjs → ccache-Btwc36-I.mjs} +10 -9
- package/dist/{cdp-Dq_ypAlJ.mjs → cdp-3fwICjzi.mjs} +18 -5
- package/dist/{chrome-BnzZMqsR.mjs → chrome-Bbw689-6.mjs} +7 -4
- package/dist/{cli-CLP3diBm.mjs → cli-CHZa9Lvk.mjs} +20 -20
- package/dist/cli.mjs +1 -1
- package/dist/client-OyRoEuSr.mjs +1577 -0
- package/dist/collector-run.d.mts +4 -40
- package/dist/collector-run.mjs +9 -8
- package/dist/{command-output-BTy8hZ0F.mjs → command-output-gwpm28fu.mjs} +1 -1
- package/dist/config-DlrsduwG.mjs +955 -0
- package/dist/{created-devices-Dzd5C2LF.mjs → created-devices-x9rN6bhs.mjs} +14 -4
- package/dist/{dependency-state-DG0p31-y.mjs → dependency-state-1I9KZT1Y.mjs} +20 -5
- package/dist/{detached-entry-B9C_zeQC.mjs → detached-entry-BwNEtIrP.mjs} +1 -1
- package/dist/{dev-client-DAWVIGBu.mjs → dev-client-BSDR5h3F.mjs} +2 -2
- package/dist/{device-BECOiufl.mjs → device-B-L3Fbxy.mjs} +91 -34
- package/dist/device-DklWZLqb.mjs +1068 -0
- package/dist/{device-capacity-CBUAcRyQ.mjs → device-capacity-DK5_EwRS.mjs} +42 -13
- package/dist/device-host-worker.mjs +76 -79
- package/dist/{device-ios-COSi6oS1.mjs → device-ios-DoB6KFCO.mjs} +77 -51
- package/dist/{device-lease-run-Cw2M4z51.mjs → device-lease-run-xJPQ_QQG.mjs} +14 -26
- package/dist/{device-pool-Bf-pOhSw.mjs → device-pool-BMkdsefq.mjs} +5 -5
- package/dist/{doctor-kfMBnUw6.mjs → doctor-Bo7BXLux.mjs} +271 -320
- package/dist/{doctor-CB8oZKCk.mjs → doctor-D5_EzwfA.mjs} +186 -91
- package/dist/doctor-fingerprint-DZLoYFVb.mjs +16 -0
- package/dist/doctor-fingerprint.mjs +19 -0
- package/dist/doctor-thjWAV0z.mjs +2 -0
- package/dist/{doctor-watchman-BRyV2adA.mjs → doctor-watchman-N2wDotwQ.mjs} +40 -21
- package/dist/{eas-build-JyfqyHfC.mjs → eas-build-CwknaQYj.mjs} +53 -20
- package/dist/{build-plan-B7SZsc2N.mjs → eas-fallback-CSgAulr5.mjs} +41 -214
- package/dist/{watchman-CPQFZLXY.mjs → eas-session-ledger-DlY_serQ.mjs} +230 -11
- package/dist/{error-diagnostics-BR677IDM.mjs → error-diagnostics-DBWO2rxE.mjs} +172 -82
- package/dist/exec-DEoAhcze.mjs +858 -0
- package/dist/exec-DnYc4HGH.d.mts +78 -0
- package/dist/{gc-qWOMdnER.mjs → gc-CXxN-4SE.mjs} +1091 -1029
- package/dist/gc-DnNDQyaP.mjs +3 -0
- package/dist/{guide-G9cAbVAc.mjs → guide-Br0LEfKy.mjs} +1165 -297
- package/dist/{hosted-android-C3MHGld1.mjs → hosted-android-B-9f-jkd.mjs} +10 -8
- package/dist/{hosted-client-ZEKpS9vw.mjs → hosted-client-DZAPrcro.mjs} +34 -19
- package/dist/{hosted-ios-l-o3iM9J.mjs → hosted-ios-CsCS21hn.mjs} +1 -1
- package/dist/{hosted-logs-EzQM7-3Z.mjs → hosted-logs-FIRx9X2f.mjs} +77 -43
- package/dist/{hosted-macos-BACVAVqE.mjs → hosted-macos-_awYSpfv.mjs} +35 -22
- package/dist/{hosted-native-A4s1QPXe.mjs → hosted-native-BdzE668-.mjs} +49 -27
- package/dist/idle-C4cZ757K.d.mts +24 -0
- package/dist/{idle-shutdown-DtxhPTKe.mjs → idle-shutdown-DDg3SdvY.mjs} +11 -10
- package/dist/ios-CDvlmUs7.d.mts +24 -0
- package/dist/{ios-BwVCF7oA.mjs → ios-Ds1clbIt.mjs} +8 -8
- package/dist/{ios-device-D7pi2Sca.mjs → ios-device--h_-8ENa.mjs} +51 -26
- package/dist/{ios-device-B5veSDDQ.mjs → ios-device-BXZ0c7Kb.mjs} +1 -1
- package/dist/{ios-RkW4pnQf.mjs → ios-hcBpnEGk.mjs} +200 -93
- package/dist/{ios-Ce7bc7br.mjs → ios-pm4nL6By.mjs} +160 -108
- package/dist/{ios-project-K9hY_Jab.mjs → ios-project-CZhKODF3.mjs} +13 -13
- package/dist/ios-signing-BtAffm0A.mjs +489 -0
- package/dist/{ios-state-CBLe1xYw.mjs → ios-state-Dtw5K2aI.mjs} +1 -1
- package/dist/{ios-nOLJGVdy.mjs → ios-xtlRHaeB.mjs} +1 -1
- package/dist/{js-swap-Bb3nsS07.mjs → js-swap-Bwhh4S7V.mjs} +30 -25
- package/dist/{launch-C3qJ6Agb.mjs → launch-BjxemNpK.mjs} +85 -543
- package/dist/{launch-F4sOqAhB.mjs → launch-CCzXlc8r.mjs} +91 -90
- package/dist/logs-BfwwadsZ.mjs +479 -0
- package/dist/logs-jTS_lHSo.mjs +248 -0
- package/dist/{macos-D7uDvO-z.mjs → macos-DVlSSyUg.mjs} +1 -1
- package/dist/macos-NfV2D7dO.mjs +694 -0
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +123 -248
- package/dist/{metro-DnCEjCxs.mjs → metro-DiXtA3Ty.mjs} +107 -30
- package/dist/metro-gateway-CKZqKqx7.mjs +209 -0
- package/dist/{named-ports-4WopRPg-.mjs → named-ports-DkEzOfzO.mjs} +42 -90
- package/dist/native-android-CqgnOgsT.mjs +645 -0
- package/dist/native-gradle-artifact-inputs-DHFnmrVf.mjs +270 -0
- package/dist/native-gradle-inputs-Dnh84Zbd.mjs +211 -0
- package/dist/native-inputs-TRTTTRxt.mjs +91 -0
- package/dist/native-run-NwUuxbkg.mjs +276 -0
- package/dist/{native-runtime-k8dTIrbK.mjs → native-runtime-B7KIafpM.mjs} +11 -9
- package/dist/native-source-Y0yLhFyQ.mjs +133 -0
- package/dist/{native-xcode-inputs-CykopJdM.mjs → native-xcode-inputs-D_VOfFL7.mjs} +26 -18
- package/dist/{native-xcode-ios-Bbf30Gm0.mjs → native-xcode-ios-BYDOHWLN.mjs} +104 -60
- package/dist/{ndjson-GOnbyvso.mjs → ndjson-CgE_o3Dl.mjs} +3 -3
- package/dist/offload-worker.d.mts +59 -41
- package/dist/offload-worker.mjs +478 -115
- package/dist/operation-exec-BW0em61G.mjs +171 -0
- package/dist/{ownership-DCtw_2IA.mjs → ownership-C5fo5Edb.mjs} +3 -3
- package/dist/{page-DRHy329N.mjs → page-DBL2PiUU.mjs} +12 -5
- package/dist/paths-BsJMoPP2.mjs +12 -0
- package/dist/paths-DQtTgsBf.mjs +42 -0
- package/dist/{placement-log-MOfPVgaM.mjs → placement-log-BnGgJRRZ.mjs} +5 -5
- package/dist/{plan-placement-V0TdzYXp.mjs → plan-placement-DIsMtN-d.mjs} +15 -11
- package/dist/{ports-DdSUicVo.mjs → ports-Cy1RkmuQ.mjs} +3 -3
- package/dist/prebuild-D4JoEAEs.mjs +484 -0
- package/dist/{project-_phNIJfs.mjs → project-wM_XFTvE.mjs} +65 -37
- package/dist/{pull-request-CQ8sPbZa.mjs → pull-request-W1IuLXDt.mjs} +102 -20
- package/dist/pull-request-eODyIqZF.d.mts +124 -0
- package/dist/pull-requests.d.mts +2 -120
- package/dist/pull-requests.mjs +2 -2
- package/dist/{react-native-android-CM1XRDwv.mjs → react-native-android-6OZ7MSM7.mjs} +121 -72
- package/dist/{gradle-DSrXb6lG.mjs → react-native-build-Dvd8hC21.mjs} +292 -15
- package/dist/react-native-doctor-DsoSF1Y9.mjs +234 -0
- package/dist/{react-native-ios-Dhq_eiYQ.mjs → react-native-ios-4u1Rn-GR.mjs} +71 -39
- package/dist/{recordings-D0d5aQt0.mjs → recordings-CQmfzAh7.mjs} +12 -9
- package/dist/registration-BcRgF6Ox.mjs +51 -0
- package/dist/{reload-C8JhxjSh.mjs → reload-BzkYabB-.mjs} +94 -47
- package/dist/reload-D7AVlb8N.mjs +2 -0
- package/dist/{remote-BiR6aYjy.mjs → remote-DjGDmlTZ.mjs} +26 -18
- package/dist/result-D2Ykz_iD.mjs +435 -0
- package/dist/{run-BtPgpKks.mjs → run-DidHghD1.mjs} +5 -5
- package/dist/selection-DSMbWEX2.mjs +620 -0
- package/dist/{server-bare-R2m_gsH8.mjs → server-bare-CafzuyFB.mjs} +3 -4
- package/dist/{server-command-CTtCkmcQ.mjs → server-command-C6QjeupS.mjs} +14 -7
- package/dist/{server-expo-DsguMGUF.mjs → server-expo-CrjB29pq.mjs} +54 -11
- package/dist/server-expo-D58c9L0M.mjs +2 -0
- package/dist/{settings-B717odTD.mjs → settings-C7O8A6ZI.mjs} +309 -119
- package/dist/settings-DpR-1Wnn.mjs +114 -0
- package/dist/settings-write-CzD60rOD.mjs +360 -0
- package/dist/settings.schema.json +176 -16
- package/dist/{simslim-CCr3rE-A.mjs → simslim-BNckWpXi.mjs} +14 -12
- package/dist/{slot-launch-DPt0m9Nd.mjs → slot-launch-CbfmNgM6.mjs} +15 -20
- package/dist/{spawn-entry-Dcb7sDUz.mjs → spawn-entry-jw19m8Nl.mjs} +0 -1
- package/dist/stage-Bps5oius.mjs +200 -0
- package/dist/{start-TXeMmmO2.mjs → start-Br-oxqMD.mjs} +1 -1
- package/dist/start-EojN4WFz.mjs +1364 -0
- package/dist/{state-D1Icyjlu.mjs → state--smjFSTL.mjs} +55 -13
- package/dist/{state-CG_OFoaA.mjs → state-08I3hw3_.mjs} +3 -2
- package/dist/{stats-BoY5sEPU.mjs → stats-D3GerlFW.mjs} +7 -13
- package/dist/stats-report-BzaPrRm6.mjs +16 -0
- package/dist/{status-BTA7kKiQ.mjs → status-BRROykC8.mjs} +7 -5
- package/dist/status-EvrK72L2.mjs +2 -0
- package/dist/{status-WgFbkRzj.mjs → status-WBDajew2.mjs} +554 -452
- package/dist/{status-watch-C23ILtf9.mjs → status-watch-Bg_u-pwR.mjs} +222 -25
- package/dist/status-worker.d.mts +1 -0
- package/dist/status-worker.mjs +19 -0
- package/dist/stim-desktop-D5OGpYZM.mjs +112 -0
- package/dist/{stop-DR6VoDIk.mjs → stop-DTZ9uTw3.mjs} +2 -2
- package/dist/{stop-DPEhunlw.mjs → stop-Dky10AEJ.mjs} +33 -23
- package/dist/{stop-BNmBMi1F.mjs → stop-Mv6W9FAk.mjs} +137 -98
- package/dist/supervisor-run.d.mts +8 -17
- package/dist/supervisor-run.mjs +56 -28
- package/dist/{support-CwjbFs9H.mjs → support-CNioewsd.mjs} +14 -14
- package/dist/{support-DMTqj3Hm.mjs → support-yUiOjnVY.mjs} +20 -13
- package/dist/swiftpm-artifact-inputs-DFcGsXDP.mjs +153 -0
- package/dist/swiftpm-macos-BIak-Rxv.mjs +532 -0
- package/dist/toolchain-BY8X8HXx.mjs +2 -0
- package/dist/toolchain-DpJMHq0n.mjs +876 -0
- package/dist/{trigger-CigAYjeL.mjs → trigger-Cbyc41d_.mjs} +21 -6
- package/dist/trigger-yb_nFzPF.mjs +2 -0
- package/dist/{warm-progress-BuY-Fvfn.mjs → warm-progress-CA3TgT3-.mjs} +263 -156
- package/dist/web-CZ07gd77.mjs +2 -0
- package/dist/{web-BDBMxDpx.mjs → web-DKx6yoFA.mjs} +20 -14
- package/dist/web-run.mjs +13 -12
- package/dist/windows-report.d.mts +1 -0
- package/dist/windows-report.mjs +18 -0
- package/dist/windows-report.ps1 +43 -0
- package/dist/{workspace-state-DJt_MMvf.mjs → workspace-state-U08dEzhG.mjs} +62 -58
- package/dist/{workspaces-C36s2jb9.mjs → workspaces-CpANmBlq.mjs} +1723 -1286
- package/dist/{worktree-CgvJYJ-Q.mjs → worktree-CHecZ4Oq.mjs} +1097 -547
- package/dist/worktree-ZqZVjD9K.mjs +2 -0
- package/dist/xcode-BXn9-xYx.mjs +896 -0
- package/package.json +7 -5
- package/shim/android-audio/Main.java +24 -0
- package/shim/android-audio/README.md +17 -0
- package/shim/android-audio.jar +0 -0
- package/shim/android-optimizations.gradle +7 -5
- package/dist/activity-BSV745Kz.mjs +0 -629
- package/dist/api-qvOzWyfO.mjs +0 -154
- package/dist/api-run.mjs +0 -294
- package/dist/app-install-BIKtMfPq.mjs +0 -896
- package/dist/artifact-lifecycle-DzPpBYvK.mjs +0 -30
- package/dist/budget-f7516Zk0.mjs +0 -4106
- package/dist/build-facts-DcLv2HSd.d.mts +0 -111
- package/dist/cancellation-C_22VQTH.mjs +0 -24
- package/dist/client-qQAaXSUs.mjs +0 -1771
- package/dist/config-BYDxlMY3.mjs +0 -562
- package/dist/device-DuiPJQSn.mjs +0 -290
- package/dist/device-lease-BX1sJQ2E.mjs +0 -1224
- package/dist/device-remote-xRT0Vmo9.mjs +0 -1272
- package/dist/eas-session-ledger-Cm0Gwq2F.mjs +0 -207
- package/dist/exec-DqKo9GKJ.mjs +0 -268
- package/dist/git-DDfFzkMm.mjs +0 -45
- package/dist/launch-verify-B6Q8hZP-.mjs +0 -428
- package/dist/logs-BGrEUaEs.mjs +0 -383
- package/dist/macos-Ch5VJmcS.mjs +0 -584
- package/dist/metro-gateway-CX9Xlkq2.mjs +0 -103
- package/dist/miss-reason-mKzZ4VvN.mjs +0 -238
- package/dist/native-android-D5qc3-_v.mjs +0 -226
- package/dist/native-gradle-inputs-BsLRrl7u.mjs +0 -349
- package/dist/ndjson-Du3j0AQ9.d.mts +0 -17
- package/dist/plan-C4TjdPsR.mjs +0 -260
- package/dist/prebuild-DYbucPXy.mjs +0 -212
- package/dist/preview-78a9kR_-.mjs +0 -1011
- package/dist/react-native-build-CtCccJtb.mjs +0 -239
- package/dist/react-native-doctor-CfRnFfjs.mjs +0 -78
- package/dist/remote-cache-OyOyVIS3.mjs +0 -526
- package/dist/result-CaJg5Iat.mjs +0 -186
- package/dist/server-expo-Bigriskl.mjs +0 -2
- package/dist/settings-BORNaaCO.mjs +0 -402
- package/dist/settings-vk3mJGSm.d.mts +0 -13
- package/dist/spawn-claims-DVS5RkMS.mjs +0 -72
- package/dist/stage-KNTQopqv.mjs +0 -157
- package/dist/start-BWqzODpb.mjs +0 -1116
- package/dist/stim-desktop-BzBN2Em2.mjs +0 -63
- package/dist/swiftpm-macos-DRNVJWlb.mjs +0 -119
- package/dist/tailnet-Dh-qiYzG.mjs +0 -265
- package/dist/toolchain-BFkAcstd.mjs +0 -2
- package/dist/toolchain-DxSsNssu.mjs +0 -1657
- package/dist/trigger-9_USgmxz.mjs +0 -2
- package/dist/web-Dq-i3wzJ.mjs +0 -2
- package/dist/worktree-DpLdCHga.mjs +0 -2
- package/dist/{api-run.d.mts → doctor-fingerprint.d.mts} +0 -0
- 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 };
|