stim 1.10.1 → 1.12.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/dist/{build-progress-BXx-ZBe4.mjs → activity-CCZJs__P.mjs} +9 -182
- package/dist/{android-787HhP5w.mjs → android-75Sk2rIR.mjs} +81 -19
- package/dist/{android-Bu4mbDzc.mjs → android-B7X-8H7h.mjs} +426 -106
- package/dist/{android-DPov-fv2.mjs → android-BQiKJYK0.mjs} +1 -1
- package/dist/{android-cas-CITmIeWY.mjs → android-cas-DzLPXiEn.mjs} +7 -7
- package/dist/{app-install-Cfmqr3TQ.mjs → app-install-CsZi0SAT.mjs} +1 -1
- package/dist/{workspaces-wrH6dXfu.mjs → budget-DbzW5VTD.mjs} +651 -32
- package/dist/{dev-client-DExzPiOa.mjs → build-plan-CqntbR7b.mjs} +514 -38
- package/dist/build-progress-h60GPV27.mjs +179 -0
- package/dist/{cache-manifest-Pubfp8wV.mjs → cache-manifest-DWPF4RKJ.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +16 -16
- package/dist/collector-run.mjs +4 -4
- package/dist/{command-output-D5z2_bcF.mjs → command-output-BWCj5Vh-.mjs} +1 -1
- package/dist/{config-BEq0Qw_a.mjs → config-0jCcMM0E.mjs} +10 -1
- package/dist/{dependency-state-r9UesA9a.mjs → dependency-state-DNN0yfx6.mjs} +1 -1
- package/dist/{deps-DP4eWqea.mjs → deps-CO-_7UOm.mjs} +2 -2
- package/dist/{device-Czh5kO8A.mjs → device-9r9xRYDa.mjs} +49 -11
- package/dist/{device-lease-CbEaIk7b.mjs → device-lease-B9LAXIUH.mjs} +55 -55
- package/dist/{device-pool-BBWLAr55.mjs → device-pool-BoezJd-c.mjs} +4 -4
- package/dist/{device-remote-powUHdbK.mjs → device-remote-C0_o-tJH.mjs} +254 -579
- package/dist/{doctor-CmqK1LcQ.mjs → doctor-LfDlbR-N.mjs} +20 -19
- package/dist/{error-diagnostics-xxWKNO3-.mjs → error-diagnostics-CCxjn31V.mjs} +7 -7
- package/dist/{gc-zCDpmdHU.mjs → gc-C4nQFcTX.mjs} +220 -282
- package/dist/{guide-DwmaD_Wa.mjs → guide-BPBlh9ML.mjs} +512 -73
- package/dist/{guide-status-CWvti2Lg.mjs → guide-status-i9ogHt4v.mjs} +2 -2
- package/dist/{idle--jn7pBNe.mjs → idle-S3tFZ53_.mjs} +7 -6
- package/dist/{in-use-CNIWU-r-.mjs → in-use-D9XYEgFD.mjs} +7 -7
- package/dist/{ios-CbdqTqOc.mjs → ios-CDzWL1mj.mjs} +302 -88
- package/dist/{ios-COGcGEcN.mjs → ios-CZWOHTTc.mjs} +24 -19
- package/dist/{ios-device-DNjPXBnb.mjs → ios-device-D9cLxVr5.mjs} +1 -1
- package/dist/{launch-verify-BhgkYAws.mjs → launch-verify-B2oR_kFN.mjs} +2 -2
- package/dist/logs-SzuYudOj.mjs +485 -0
- package/dist/merge-state-ytFQCeh4.mjs +262 -0
- package/dist/{metro-BWLOJ_Wr.mjs → metro-Dzo30hvJ.mjs} +1 -1
- package/dist/{named-ports-DiTfSz6p.mjs → named-ports-Dn1o61D9.mjs} +3 -3
- package/dist/{ownership-BfPNRf3-.mjs → ownership-B8d4kXBx.mjs} +49 -16
- package/dist/{ownership-D2xbLVoz.mjs → ownership-Dqnrjrio.mjs} +2 -2
- package/dist/{ports-CLSSFOuz.mjs → ports-Dp_QTwTC.mjs} +3 -3
- package/dist/{project-BXZzOABt.mjs → project-B8rbutVC.mjs} +4 -72
- package/dist/{reload-CymyF6Kk.mjs → reload-nocmx8KB.mjs} +9 -9
- package/dist/remote-cache-BRsRZru5.mjs +526 -0
- package/dist/{server-bare-DVbb7kXH.mjs → server-bare-Gn3ssSdK.mjs} +1 -1
- package/dist/{server-expo-BvOCeakp.mjs → server-expo-CeT1nIIk.mjs} +2 -2
- package/dist/server-expo-eWi9Coof.mjs +2 -0
- package/dist/{settings-DOugPlPz.mjs → settings-BlyDn2Oz.mjs} +14 -5
- package/dist/{settings-EWYNwe72.mjs → settings-bE-Gu3Ti.mjs} +23 -11
- package/dist/settings.schema.json +73 -1
- package/dist/{simslim-DZUxzsjq.mjs → simslim-COtEEP7x.mjs} +152 -96
- package/dist/{slot-launch-Dk4oTREM.mjs → slot-launch-DIACGEfH.mjs} +4 -4
- package/dist/{spawn-claims-CUleC2cM.mjs → spawn-claims-PT8aQpCa.mjs} +25 -2
- package/dist/{start-qWapeB4W.mjs → start-BFstVBlx.mjs} +1 -1
- package/dist/{start-RbmFPeZ7.mjs → start-BboE1HIS.mjs} +108 -17
- package/dist/{state-WGdIbi_d.mjs → state-DcDgzfu3.mjs} +15 -3
- package/dist/{stats-BAgJaLA4.mjs → stats-D8rUTPHd.mjs} +1 -1
- package/dist/{stats-DBYJ5bxL.mjs → stats-DiCtmluY.mjs} +4 -4
- package/dist/{status-Dr4fRkym.mjs → status-BUASYyy_.mjs} +92 -33
- package/dist/{status-Zp8kvexe.mjs → status-DI9Tjo7T.mjs} +255 -38
- package/dist/stim-desktop-D0xhpZt8.mjs +29 -0
- package/dist/stop-BI7QY76M.mjs +3 -0
- package/dist/{stop-DcEv5sk-.mjs → stop-Bn6v-GvS.mjs} +205 -37
- package/dist/supervisor-run.mjs +11 -11
- package/dist/{workspace-process-lock-BXA96myD.mjs → workspace-process-lock-BvvJTfo_.mjs} +10 -5
- package/dist/{worktree-DEG2o9F8.mjs → worktree-B5sTRskL.mjs} +1 -1
- package/dist/{worktree-Bd4dRvx7.mjs → worktree-ChDL5fO1.mjs} +17 -16
- package/dist/{worktree-BlizKDpZ.mjs → worktree-DeBQSVtR.mjs} +3 -1
- package/dist/{xcode-B2tBkf0m.mjs → xcode-BzplpQzc.mjs} +7 -7
- package/package.json +5 -5
- package/dist/budget-nDCEwtJv.mjs +0 -620
- package/dist/logs-MARIy6m9.mjs +0 -209
- package/dist/server-expo-DBVTdult.mjs +0 -2
- package/dist/stop-DI4BG4Os.mjs +0 -3
|
@@ -1,29 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as deviceSlotPlatforms, C as setDevice, E as withConfigLock$1, S as saveConfig, U as projectDeviceSlots, _ as loadConfig$1, a as clearDevice, d as getConfigDir, i as clearAndroidBootPending, k as acquireAvdClaim, t as allConsolePortsAndSerials, y as releaseAndroidConsolePort } from "./config-0jCcMM0E.mjs";
|
|
2
2
|
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
-
import { g as updateWorkspaceState, i as pidExists, k as workspaceId, m as readWorkspaceState$1, s as resolveProjectMetro, x as ensureWorkspaceStorage } from "./metro-
|
|
4
|
-
import { D as makeTemporaryDirectory, O as removeTemporaryEntry } from "./worktree-
|
|
5
|
-
import { a as
|
|
6
|
-
import {
|
|
7
|
-
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-
|
|
3
|
+
import { S as sharedBuildCache, g as updateWorkspaceState, i as pidExists, k as workspaceId, m as readWorkspaceState$1, s as resolveProjectMetro, x as ensureWorkspaceStorage } from "./metro-Dzo30hvJ.mjs";
|
|
4
|
+
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, c as gitCommonDirOnDisk } from "./worktree-DeBQSVtR.mjs";
|
|
5
|
+
import { a as isPackageResolvable, o as ownedDeviceLabel, r as detectIsExpo } from "./project-B8rbutVC.mjs";
|
|
6
|
+
import { a as spawnDeclared } from "./spawn-claims-PT8aQpCa.mjs";
|
|
7
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BvvJTfo_.mjs";
|
|
8
8
|
import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
9
|
-
import { l as phaseLine } from "./command-output-
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
9
|
+
import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-BWCj5Vh-.mjs";
|
|
10
|
+
import { N as storedSources, S as entryDir, x as diffFingerprintSources, y as artifactIn } from "./device-lease-B9LAXIUH.mjs";
|
|
11
|
+
import { B as pickDefaultSystemImage, C as getAvdNameForSerial, E as listAdbDevices, F as ownedAvdMatchesConfiguration, I as ownedAvdName, M as nextConsolePort, N as ownedAvdDeviceProfile, O as listAvds, R as ownedAvdSystemImage, W as resolveOwnedAvdSerial, X as waitForBoot, _ as createOwnedAvd, d as assertOwnedAvdStopped, g as configureNewOwnedAvd, h as bootAndroidEmulator, k as listInstalledSystemImages, n as OWNED_AVD_CONFIG_DEFAULTS, p as avdPoolConfiguration, t as DEFAULT_AVD_DEVICE_PROFILE } from "./android-75Sk2rIR.mjs";
|
|
12
|
+
import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, v as iosSimSlimProfileSetting } from "./settings-BlyDn2Oz.mjs";
|
|
13
|
+
import { _ as dropParked, b as readParked, d as teardownOwnedAvd, g as adoptParked, m as teardownParkedIosSim, p as teardownParkedAvd, v as isLegacyDeletionClaim, x as selectParked, y as parkedMaxSetting } from "./ownership-B8d4kXBx.mjs";
|
|
14
|
+
import { E as configuredIosSimulatorViewer, O as hostMemoryPressureAdvice, S as resolveOwnedIosSim, b as resetIosPrivacy, c as iosSimulatorFailureAdvice, d as listIosDeviceTypes, g as parseRuntimeVersion, k as readHostMemoryPressure, l as listAllIosSims, m as ownedSimName, n as bootIosSim, r as createOwnedIosSim, v as renameIosSim, x as resolveIosCreation, y as resetIosKeychain } from "./ios-CZWOHTTc.mjs";
|
|
15
|
+
import { o as estimateBuild } from "./build-progress-h60GPV27.mjs";
|
|
16
|
+
import { i as expoBinRefusal, r as expoBinPath, t as cleanLine } from "./server-expo-CeT1nIIk.mjs";
|
|
17
|
+
import { a as easAuthNote } from "./remote-cache-BRsRZru5.mjs";
|
|
18
|
+
import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-B2oR_kFN.mjs";
|
|
19
|
+
import { a as liveOwnedDeviceCount, i as deviceTypeMismatch, t as reconcileSimSlim } from "./simslim-COtEEP7x.mjs";
|
|
20
|
+
import { s as startDevServer } from "./start-BboE1HIS.mjs";
|
|
21
|
+
import { r as readStats } from "./stats-D8rUTPHd.mjs";
|
|
22
|
+
import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-BzplpQzc.mjs";
|
|
23
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
24
|
+
import { LAST_BUILD_KEYS, isJsonObject, loadConfig, readIdleStop, readJsonObject, readWorkspaceState } from "@stim-cli/core/state";
|
|
25
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
22
26
|
import { claimRemoveCommand, clearClaimChild, markClaimChildPending, processGroupAlive, releaseClaim, setClaimChild } from "@stim-cli/core/ownership-claim";
|
|
23
27
|
import { captureProcessIdentity } from "@stim-cli/core/process-identity";
|
|
24
28
|
import { randomUUID } from "node:crypto";
|
|
25
29
|
import chalk from "chalk";
|
|
26
30
|
import { isDeepStrictEqual } from "node:util";
|
|
31
|
+
import * as expoFingerprint from "@expo/fingerprint";
|
|
32
|
+
import { resolveTieredBuild } from "@stim-cli/cache";
|
|
27
33
|
import { request } from "node:http";
|
|
28
34
|
//#region src/engine/metro-warmup.ts
|
|
29
35
|
const WARMUP_TIMEOUT_MS = 6e4;
|
|
@@ -526,7 +532,7 @@ function findOtherOwner(avdName, projectPath, selectedSlot = "default") {
|
|
|
526
532
|
for (const [path, project] of Object.entries(loadConfig$1()?.projects ?? {})) if (projectDeviceSlots(project).some(({ slot, platforms }) => (path !== projectPath || slot !== selectedSlot) && platforms.android?.avdName?.toLowerCase() === avdName.toLowerCase())) return path;
|
|
527
533
|
return null;
|
|
528
534
|
}
|
|
529
|
-
async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, systemImage, configuration, configure, teardown = teardownOwnedAvd }) {
|
|
535
|
+
async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, systemImage, deviceProfile, configuration, configure, teardown = teardownOwnedAvd }) {
|
|
530
536
|
const { claim, reservation, previous, avdName } = withConfigLock$1(() => {
|
|
531
537
|
const previous = deviceSlotPlatforms(loadConfig$1()?.projects?.[projectPath], slot)?.android;
|
|
532
538
|
if (previous?.avdName && previous.avdName !== previousAvdName) throw new Error(`Another Stim run assigned AVD ${previous.avdName} to this workspace. Retry to use it.`);
|
|
@@ -572,6 +578,7 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
|
|
|
572
578
|
try {
|
|
573
579
|
const creation = await createOwnedAvd(label, {
|
|
574
580
|
systemImage,
|
|
581
|
+
deviceProfile,
|
|
575
582
|
spawn: (...args) => {
|
|
576
583
|
markClaimChildPending(claim);
|
|
577
584
|
child = getExecutor().spawn(...args);
|
|
@@ -607,6 +614,7 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
|
|
|
607
614
|
avdName,
|
|
608
615
|
created: false,
|
|
609
616
|
systemImage: ownedAvdSystemImage(avdName),
|
|
617
|
+
deviceProfile: ownedAvdDeviceProfile(avdName),
|
|
610
618
|
...resolved.serial ? {
|
|
611
619
|
serial: resolved.serial,
|
|
612
620
|
consolePort: Number(resolved.serial.replace(/^emulator-/, ""))
|
|
@@ -633,7 +641,10 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
|
|
|
633
641
|
avdName,
|
|
634
642
|
owned: true,
|
|
635
643
|
deviceName: avdName,
|
|
636
|
-
...prepared.created ? {
|
|
644
|
+
...prepared.created ? {
|
|
645
|
+
poolConfiguration: configuration,
|
|
646
|
+
bootPending: true
|
|
647
|
+
} : {},
|
|
637
648
|
...prepared.consolePort ? { consolePort: prepared.consolePort } : {}
|
|
638
649
|
}, slot);
|
|
639
650
|
});
|
|
@@ -668,8 +679,13 @@ async function prepareOwnedAvd({ projectPath, slot, label, previousAvdName, syst
|
|
|
668
679
|
const ANDROID_FRESH_BOOT_TIMEOUT_MS = process.platform === "win32" ? 12e5 : 12e4;
|
|
669
680
|
const ANDROID_BOOT_TIMEOUT_MS = process.platform === "win32" ? 12e5 : 24e4;
|
|
670
681
|
async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoot, label, settings, flags, note, out, logFile, alive, configureAvd, teardownAvd }) {
|
|
671
|
-
const
|
|
672
|
-
const
|
|
682
|
+
const projectAvdConfig = androidAvdConfigSetting(settings, settingsRoot);
|
|
683
|
+
const avdConfig = {
|
|
684
|
+
...OWNED_AVD_CONFIG_DEFAULTS,
|
|
685
|
+
...projectAvdConfig
|
|
686
|
+
};
|
|
687
|
+
const requestedProfile = flags.deviceProfile || settings.android?.deviceProfile || null;
|
|
688
|
+
let replacedProfile = null;
|
|
673
689
|
if (record?.setupIncomplete && record.avdName) {
|
|
674
690
|
const avdName = record.avdName;
|
|
675
691
|
const cleanup = teardownAvd(avdName, {
|
|
@@ -686,9 +702,35 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
686
702
|
if (cleanup.status === "failed" || cleanup.status === "skipped") throw new AvdRecoveryError(`Owned AVD ${record.avdName} has incomplete setup and could not be deleted (${cleanup.reason || cleanup.status}). Fix the cause, then retry; Stim kept the device record for cleanup.`, "Wait for any active AVD operation to finish. If the refusal names an unresolved claim, inspect its creator and native processes before removing only that claim. Retry `stim android` to reconcile the incomplete device.");
|
|
687
703
|
record = null;
|
|
688
704
|
}
|
|
705
|
+
const requestedImage = flags.systemImageFlag || null;
|
|
706
|
+
const currentImage = requestedImage && record?.owned && record.avdName ? ownedAvdSystemImage(record.avdName) : null;
|
|
707
|
+
if (record?.avdName && requestedImage && currentImage && currentImage !== requestedImage) {
|
|
708
|
+
const avdName = record.avdName;
|
|
709
|
+
if (!record.bootPending) throw new AvdRecoveryError(`this project's emulator ${avdName} uses system image ${currentImage}, but ${requestedImage} was requested. --system-image replaces only an emulator that never finished a boot, and this one has.`, "Run `stim worktree remove` (or `stim gc --delete`) to reap the current emulator, then `stim android` again to create the requested one, or pass `--slot <name>` to create it beside the current one.");
|
|
710
|
+
replacedProfile = ownedAvdDeviceProfile(avdName);
|
|
711
|
+
const cleanup = teardownAvd(avdName, {
|
|
712
|
+
del: true,
|
|
713
|
+
owner: {
|
|
714
|
+
projectPath,
|
|
715
|
+
slot,
|
|
716
|
+
expectedRecord: record
|
|
717
|
+
},
|
|
718
|
+
onRemoved: () => {
|
|
719
|
+
clearDevice(projectPath, "android", slot, avdName);
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
if (cleanup.status === "failed" || cleanup.status === "skipped") throw new AvdRecoveryError(`Owned AVD ${avdName} never finished a boot, but could not be deleted to create one on ${requestedImage} (${cleanup.reason || cleanup.status}). Stim kept the device record.`, "Fix the cause, then retry, or pass `--slot <name>` to create the requested emulator beside it.");
|
|
723
|
+
out(phaseLine("device", `deleted ${avdName}, which never finished a boot, to create one on ${requestedImage}`));
|
|
724
|
+
record = null;
|
|
725
|
+
}
|
|
726
|
+
const deviceProfile = requestedProfile ?? replacedProfile ?? DEFAULT_AVD_DEVICE_PROFILE;
|
|
727
|
+
const configuration = avdPoolConfiguration(androidDataPartitionSizeGbSetting(settings), avdConfig, deviceProfile);
|
|
728
|
+
const configurationBeforeDefaults = avdPoolConfiguration(androidDataPartitionSizeGbSetting(settings), projectAvdConfig, deviceProfile);
|
|
689
729
|
if (record?.avdName) {
|
|
690
730
|
if (record.owned) {
|
|
691
731
|
const resolved = resolveOwnedAvdSerial(record.avdName);
|
|
732
|
+
const currentProfile = resolved.notOwned || resolved.missing ? null : ownedAvdDeviceProfile(record.avdName);
|
|
733
|
+
if (requestedProfile && currentProfile && currentProfile !== requestedProfile) throw new AvdRecoveryError(`this project's emulator uses device profile ${currentProfile}, but ${requestedProfile} was requested. Stim will not silently boot a different model.`, "Run `stim worktree remove` (or `stim gc --delete`) to reap the current emulator, then `stim android` again to create the requested one, or pass `--slot <name>` to create it beside the current one.");
|
|
692
734
|
if (resolved.notOwned) note(chalk.yellow(`Note: recorded AVD ${record.avdName} is not Stim-owned by name -- creating a fresh owned AVD instead of reusing it.`));
|
|
693
735
|
else if (resolved.serial) {
|
|
694
736
|
const consolePort = Number(resolved.serial.replace(/^emulator-/, ""));
|
|
@@ -703,7 +745,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
703
745
|
setDevice(projectPath, "android", updated, slot);
|
|
704
746
|
return {
|
|
705
747
|
...updated,
|
|
706
|
-
systemImage: ownedAvdSystemImage(record.avdName)
|
|
748
|
+
systemImage: ownedAvdSystemImage(record.avdName),
|
|
749
|
+
deviceProfile: currentProfile
|
|
707
750
|
};
|
|
708
751
|
} else if (!resolved.missing) {
|
|
709
752
|
out(chalk.dim(`Recorded port for owned AVD ${record.avdName} is not currently ours; booting it on a freshly allocated port...`));
|
|
@@ -718,7 +761,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
718
761
|
logFile,
|
|
719
762
|
alive
|
|
720
763
|
}),
|
|
721
|
-
systemImage: ownedAvdSystemImage(record.avdName)
|
|
764
|
+
systemImage: ownedAvdSystemImage(record.avdName),
|
|
765
|
+
deviceProfile: currentProfile
|
|
722
766
|
};
|
|
723
767
|
}
|
|
724
768
|
} else if (listAvds().includes(record.avdName)) {
|
|
@@ -734,7 +778,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
734
778
|
}
|
|
735
779
|
if (parkedMaxSetting("android").max > 0) {
|
|
736
780
|
const systemImage = pickDefaultSystemImage(listInstalledSystemImages(), { systemImage: flags.systemImage || settings.android?.systemImage })?.pkg;
|
|
737
|
-
const candidates = readParked("android").filter((entry) => entry.systemImage === systemImage && entry.configuration === configuration).toSorted((a, b) => a.parkedAt.localeCompare(b.parkedAt));
|
|
781
|
+
const candidates = readParked("android").filter((entry) => entry.systemImage === systemImage && (entry.configuration === configuration || entry.configuration === configurationBeforeDefaults)).toSorted((a, b) => a.parkedAt.localeCompare(b.parkedAt));
|
|
738
782
|
for (const parked of candidates) {
|
|
739
783
|
if (isLegacyDeletionClaim(parked.deletionClaim)) continue;
|
|
740
784
|
const resolved = resolveOwnedAvdSerial(parked.name);
|
|
@@ -750,7 +794,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
750
794
|
out(phaseLine("device", `kept parked ${parked.name}: ${String(error?.message || error)}`));
|
|
751
795
|
continue;
|
|
752
796
|
}
|
|
753
|
-
if (ownedAvdSystemImage(parked.name) !== systemImage || !ownedAvdMatchesConfiguration(parked.name, configuration)) {
|
|
797
|
+
if (ownedAvdSystemImage(parked.name) !== systemImage || !ownedAvdMatchesConfiguration(parked.name, parked.configuration)) {
|
|
754
798
|
const result = teardownParkedAvd(parked.name);
|
|
755
799
|
if (result.status === "failed") out(phaseLine("device", `kept ${parked.name}: ${result.reason}`));
|
|
756
800
|
continue;
|
|
@@ -759,7 +803,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
759
803
|
avdName: parked.name,
|
|
760
804
|
deviceName: parked.name,
|
|
761
805
|
owned: true,
|
|
762
|
-
poolConfiguration: configuration,
|
|
806
|
+
poolConfiguration: parked.configuration,
|
|
763
807
|
adoptionPending: true
|
|
764
808
|
};
|
|
765
809
|
if (!adoptParked({
|
|
@@ -769,6 +813,15 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
769
813
|
udid: parked.udid,
|
|
770
814
|
device: adopted
|
|
771
815
|
})) continue;
|
|
816
|
+
if (parked.configuration !== configuration) try {
|
|
817
|
+
configureAvd(parked.name, {
|
|
818
|
+
dataPartitionSizeGb: androidDataPartitionSizeGbSetting(settings),
|
|
819
|
+
avdConfig
|
|
820
|
+
});
|
|
821
|
+
adopted.poolConfiguration = configuration;
|
|
822
|
+
} catch (error) {
|
|
823
|
+
out(phaseLine("device", `${parked.name} keeps its old AVD settings: ${String(error?.message || error)}`));
|
|
824
|
+
}
|
|
772
825
|
return {
|
|
773
826
|
...await bootOwnedAvdOnFreshPort({
|
|
774
827
|
avdName: parked.name,
|
|
@@ -780,7 +833,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
780
833
|
alive
|
|
781
834
|
}),
|
|
782
835
|
adopted: true,
|
|
783
|
-
systemImage
|
|
836
|
+
systemImage,
|
|
837
|
+
deviceProfile
|
|
784
838
|
};
|
|
785
839
|
}
|
|
786
840
|
}
|
|
@@ -790,6 +844,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
790
844
|
label,
|
|
791
845
|
previousAvdName: record?.avdName,
|
|
792
846
|
systemImage: flags.systemImage || settings.android?.systemImage || void 0,
|
|
847
|
+
deviceProfile,
|
|
793
848
|
configuration,
|
|
794
849
|
configure: (avdName) => configureAvd(avdName, {
|
|
795
850
|
dataPartitionSizeGb: androidDataPartitionSizeGbSetting(settings),
|
|
@@ -808,7 +863,10 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
808
863
|
return {
|
|
809
864
|
...await bootOwnedAvdOnFreshPort({
|
|
810
865
|
avdName: created.avdName,
|
|
811
|
-
metadata: created.created ? {
|
|
866
|
+
metadata: created.created ? {
|
|
867
|
+
poolConfiguration: configuration,
|
|
868
|
+
bootPending: true
|
|
869
|
+
} : void 0,
|
|
812
870
|
projectPath,
|
|
813
871
|
slot,
|
|
814
872
|
deviceName: created.avdName,
|
|
@@ -817,7 +875,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
817
875
|
alive
|
|
818
876
|
}),
|
|
819
877
|
created: created.created,
|
|
820
|
-
systemImage: created.systemImage
|
|
878
|
+
systemImage: created.systemImage,
|
|
879
|
+
deviceProfile: created.deviceProfile
|
|
821
880
|
};
|
|
822
881
|
}
|
|
823
882
|
function claimAndroidConsolePort({ projectPath, slot, avdName, deviceName, livePorts = [], metadata }, { lock = withConfigLock$1, recordedPorts = () => allConsolePortsAndSerials().androidConsolePorts, record = setDevice } = {}) {
|
|
@@ -826,6 +885,7 @@ function claimAndroidConsolePort({ projectPath, slot, avdName, deviceName, liveP
|
|
|
826
885
|
const claim = {
|
|
827
886
|
...metadata?.poolConfiguration ? { poolConfiguration: metadata.poolConfiguration } : {},
|
|
828
887
|
...metadata?.adoptionPending ? { adoptionPending: true } : {},
|
|
888
|
+
...metadata?.bootPending ? { bootPending: true } : {},
|
|
829
889
|
avdName,
|
|
830
890
|
consolePort,
|
|
831
891
|
owned: true,
|
|
@@ -863,8 +923,10 @@ async function bootOwnedAvdOnFreshPort({ avdName, metadata, projectPath, slot, d
|
|
|
863
923
|
if (result.failed) throw new AvdBootError(result.reason, result.remedy);
|
|
864
924
|
const running = getAvdNameForSerial(serial);
|
|
865
925
|
if (running && running !== avdName) throw new Error(`${serial} is running AVD ${running}, not this workspace's owned AVD ${avdName}; refusing to use it.`);
|
|
926
|
+
clearAndroidBootPending(projectPath, avdName, slot);
|
|
927
|
+
const { bootPending: _booted, ...booted } = claim;
|
|
866
928
|
return {
|
|
867
|
-
...
|
|
929
|
+
...booted,
|
|
868
930
|
serial
|
|
869
931
|
};
|
|
870
932
|
} catch (error) {
|
|
@@ -943,17 +1005,22 @@ async function ensureAndroidBooted({ device, projectPath, slot, timeoutMs, out,
|
|
|
943
1005
|
failed: true,
|
|
944
1006
|
reason: `AVD ${device.avdName} is not Stim-owned by name; refusing to boot it.`
|
|
945
1007
|
};
|
|
946
|
-
|
|
1008
|
+
const avdName = device.avdName;
|
|
1009
|
+
const booted = (result) => {
|
|
1010
|
+
if (!result.failed) clearAndroidBootPending(projectPath, avdName, slot);
|
|
1011
|
+
return result;
|
|
1012
|
+
};
|
|
1013
|
+
if (resolved.serial) return booted(await waitForAndroidBoot({
|
|
947
1014
|
serial: resolved.serial,
|
|
948
1015
|
timeoutMs,
|
|
949
1016
|
out
|
|
950
|
-
});
|
|
1017
|
+
}));
|
|
951
1018
|
const freshSerial = `emulator-${device.consolePort}`;
|
|
952
|
-
if (device.owned && device.serial === freshSerial) return waitForAndroidBoot({
|
|
1019
|
+
if (device.owned && device.serial === freshSerial) return booted(await waitForAndroidBoot({
|
|
953
1020
|
serial: freshSerial,
|
|
954
1021
|
timeoutMs,
|
|
955
1022
|
out
|
|
956
|
-
});
|
|
1023
|
+
}));
|
|
957
1024
|
const claim = claimAndroidConsolePort({
|
|
958
1025
|
projectPath,
|
|
959
1026
|
slot,
|
|
@@ -983,7 +1050,7 @@ async function ensureAndroidBooted({ device, projectPath, slot, timeoutMs, out,
|
|
|
983
1050
|
out
|
|
984
1051
|
});
|
|
985
1052
|
if (result.failed) releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
|
|
986
|
-
return result;
|
|
1053
|
+
return booted(result);
|
|
987
1054
|
}
|
|
988
1055
|
//#endregion
|
|
989
1056
|
//#region src/engine/device.ts
|
|
@@ -1572,4 +1639,413 @@ function hasDevClient(root) {
|
|
|
1572
1639
|
return isPackageResolvable(root, "expo-dev-client");
|
|
1573
1640
|
}
|
|
1574
1641
|
//#endregion
|
|
1575
|
-
|
|
1642
|
+
//#region src/cache/miss-reason.ts
|
|
1643
|
+
const MISS_CHANGE_CAP = 20;
|
|
1644
|
+
function recordedBuilds(state, platform) {
|
|
1645
|
+
const builds = [];
|
|
1646
|
+
for (const value of [state?.[LAST_BUILD_KEYS[platform]], state?.lastBuild]) {
|
|
1647
|
+
const record = value;
|
|
1648
|
+
if (record?.platform !== platform) continue;
|
|
1649
|
+
const startedAt = typeof record.startedAt === "string" ? record.startedAt : "";
|
|
1650
|
+
if (typeof record.fingerprint === "string" && typeof record.cacheKey === "string") builds.push({
|
|
1651
|
+
fingerprint: record.fingerprint,
|
|
1652
|
+
cacheKey: record.cacheKey,
|
|
1653
|
+
startedAt
|
|
1654
|
+
});
|
|
1655
|
+
const carried = record.missReason?.baseline;
|
|
1656
|
+
if (typeof carried?.fingerprint === "string" && typeof carried.cacheKey === "string") builds.push({
|
|
1657
|
+
fingerprint: carried.fingerprint,
|
|
1658
|
+
cacheKey: carried.cacheKey,
|
|
1659
|
+
startedAt
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
return builds;
|
|
1663
|
+
}
|
|
1664
|
+
function projectIdentity(root) {
|
|
1665
|
+
let real;
|
|
1666
|
+
try {
|
|
1667
|
+
real = realpathSync.native(root);
|
|
1668
|
+
} catch {
|
|
1669
|
+
return null;
|
|
1670
|
+
}
|
|
1671
|
+
for (let dir = real;; dir = dirname(dir)) {
|
|
1672
|
+
if (existsSync(join(dir, ".git"))) {
|
|
1673
|
+
const common = gitCommonDirOnDisk(dir);
|
|
1674
|
+
return common ? `${common}\n${relative(dir, real)}` : null;
|
|
1675
|
+
}
|
|
1676
|
+
if (dirname(dir) === dir) return null;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
function findMissBaseline(root, platform, { readState = readWorkspaceState, projectRoots = () => Object.keys(loadConfig()?.projects ?? {}), identity = projectIdentity, sourcesOf = (p, key) => storedSources(p, key, sharedBuildCache()) } = {}) {
|
|
1680
|
+
for (const build of recordedBuilds(readState(root), platform)) {
|
|
1681
|
+
const sources = sourcesOf(platform, build.cacheKey);
|
|
1682
|
+
if (sources) return {
|
|
1683
|
+
fingerprint: build.fingerprint,
|
|
1684
|
+
cacheKey: build.cacheKey,
|
|
1685
|
+
sources,
|
|
1686
|
+
from: "workspace"
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
const project = identity(root);
|
|
1690
|
+
if (!project) return null;
|
|
1691
|
+
const others = [];
|
|
1692
|
+
for (const other of projectRoots()) {
|
|
1693
|
+
if (other === root || identity(other) !== project) continue;
|
|
1694
|
+
others.push(...recordedBuilds(readState(other), platform));
|
|
1695
|
+
}
|
|
1696
|
+
for (const build of others.toSorted((a, b) => (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0))) {
|
|
1697
|
+
const sources = sourcesOf(platform, build.cacheKey);
|
|
1698
|
+
if (sources) return {
|
|
1699
|
+
fingerprint: build.fingerprint,
|
|
1700
|
+
cacheKey: build.cacheKey,
|
|
1701
|
+
sources,
|
|
1702
|
+
from: "project"
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
return null;
|
|
1706
|
+
}
|
|
1707
|
+
const CATEGORY_RANK = {
|
|
1708
|
+
"native-dependency": 0,
|
|
1709
|
+
"app-config": 1,
|
|
1710
|
+
"config-plugin": 2,
|
|
1711
|
+
package: 3,
|
|
1712
|
+
"native-dir": 4,
|
|
1713
|
+
file: 5,
|
|
1714
|
+
"app-asset": 6,
|
|
1715
|
+
other: 7,
|
|
1716
|
+
"package-scripts": 8,
|
|
1717
|
+
autolinking: 9
|
|
1718
|
+
};
|
|
1719
|
+
function packageIn(path) {
|
|
1720
|
+
return [...path.matchAll(/(?:^|\/)node_modules\/((?:@[^/]+\/)?[^/]+)/g)].at(-1)?.[1] ?? null;
|
|
1721
|
+
}
|
|
1722
|
+
function describeChange({ name, change, reasons }) {
|
|
1723
|
+
const pkg = packageIn(name);
|
|
1724
|
+
if (name === "expoConfig" || reasons.includes("expoConfig")) return {
|
|
1725
|
+
category: "app-config",
|
|
1726
|
+
label: "app config changed"
|
|
1727
|
+
};
|
|
1728
|
+
if (pkg && reasons.some((reason) => /^(expo|rncore)Autolinking(Ios|Android)?$/.test(reason))) return {
|
|
1729
|
+
category: "native-dependency",
|
|
1730
|
+
label: `native dependency ${change}: ${pkg}`
|
|
1731
|
+
};
|
|
1732
|
+
if (pkg && reasons.includes("expoConfigPlugins")) return {
|
|
1733
|
+
category: "config-plugin",
|
|
1734
|
+
label: `config plugin ${change}: ${pkg}`
|
|
1735
|
+
};
|
|
1736
|
+
if (/^(expoAutolinkingConfig|rncoreAutolinkingConfig)(:|$)/.test(name)) return {
|
|
1737
|
+
category: "autolinking",
|
|
1738
|
+
label: "autolinking config changed"
|
|
1739
|
+
};
|
|
1740
|
+
if (name.startsWith("package:")) return {
|
|
1741
|
+
category: "package",
|
|
1742
|
+
label: `${name.slice(8)} ${change}`
|
|
1743
|
+
};
|
|
1744
|
+
if (name === "packageJson:scripts") return {
|
|
1745
|
+
category: "package-scripts",
|
|
1746
|
+
label: "package.json scripts changed"
|
|
1747
|
+
};
|
|
1748
|
+
if (reasons.includes("expoConfigExternalFile")) return {
|
|
1749
|
+
category: "app-asset",
|
|
1750
|
+
label: `app asset ${change}: ${name}`
|
|
1751
|
+
};
|
|
1752
|
+
if (reasons.includes("bareNativeDir")) return {
|
|
1753
|
+
category: "native-dir",
|
|
1754
|
+
label: `${name}/ ${change}`
|
|
1755
|
+
};
|
|
1756
|
+
if (reasons.length || name.includes("/") || name.includes(".")) return {
|
|
1757
|
+
category: "file",
|
|
1758
|
+
label: `${name} ${change}`
|
|
1759
|
+
};
|
|
1760
|
+
return {
|
|
1761
|
+
category: "other",
|
|
1762
|
+
label: `${name} ${change}`
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
function rekeyPrefix(rekeyedBy) {
|
|
1766
|
+
return rekeyedBy.length ? `${rekeyedBy.join(" and ")} changed native inputs; ` : "";
|
|
1767
|
+
}
|
|
1768
|
+
function missReasonFromChanges({ changes, baseline, rekeyedBy = [] }) {
|
|
1769
|
+
const prefix = rekeyPrefix(rekeyedBy);
|
|
1770
|
+
if (!baseline) return {
|
|
1771
|
+
kind: "no-baseline",
|
|
1772
|
+
summary: `${prefix}no earlier build of this project in the cache to compare with`,
|
|
1773
|
+
changes: [],
|
|
1774
|
+
changeCount: 0,
|
|
1775
|
+
baseline: null,
|
|
1776
|
+
rekeyedBy
|
|
1777
|
+
};
|
|
1778
|
+
if (!changes.length) return {
|
|
1779
|
+
kind: "same-sources",
|
|
1780
|
+
summary: `${prefix}native inputs match the last build; no cached app for this configuration or target`,
|
|
1781
|
+
changes: [],
|
|
1782
|
+
changeCount: 0,
|
|
1783
|
+
baseline,
|
|
1784
|
+
rekeyedBy
|
|
1785
|
+
};
|
|
1786
|
+
const described = changes.map((change, index) => ({
|
|
1787
|
+
change,
|
|
1788
|
+
index,
|
|
1789
|
+
...describeChange(change)
|
|
1790
|
+
})).toSorted((a, b) => CATEGORY_RANK[a.category] - CATEGORY_RANK[b.category] || a.index - b.index);
|
|
1791
|
+
const dependencyMoved = described.some((entry) => entry.category === "native-dependency");
|
|
1792
|
+
const labels = [...new Set(described.filter((entry) => !(dependencyMoved && entry.category === "autolinking")).map((entry) => entry.label))];
|
|
1793
|
+
const more = labels.length > 1 ? ` (+${labels.length - 1} more)` : "";
|
|
1794
|
+
return {
|
|
1795
|
+
kind: "changed",
|
|
1796
|
+
summary: `${prefix}${labels[0]}${more}`,
|
|
1797
|
+
changes: described.slice(0, MISS_CHANGE_CAP).map(({ change, category }) => ({
|
|
1798
|
+
source: change.name,
|
|
1799
|
+
change: change.change,
|
|
1800
|
+
category
|
|
1801
|
+
})),
|
|
1802
|
+
changeCount: changes.length,
|
|
1803
|
+
baseline,
|
|
1804
|
+
rekeyedBy
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
function explainBuildMiss({ root, platform, current, rekeyedBy = [], baselineDeps, differ = expoFingerprint.diffFingerprints }) {
|
|
1808
|
+
const baseline = findMissBaseline(root, platform, baselineDeps);
|
|
1809
|
+
const changes = baseline ? diffFingerprintSources({
|
|
1810
|
+
previous: baseline.sources,
|
|
1811
|
+
previousHash: baseline.fingerprint,
|
|
1812
|
+
current,
|
|
1813
|
+
differ
|
|
1814
|
+
}) : [];
|
|
1815
|
+
return {
|
|
1816
|
+
reason: missReasonFromChanges({
|
|
1817
|
+
changes,
|
|
1818
|
+
baseline: baseline ? {
|
|
1819
|
+
fingerprint: baseline.fingerprint,
|
|
1820
|
+
cacheKey: baseline.cacheKey,
|
|
1821
|
+
from: baseline.from
|
|
1822
|
+
} : null,
|
|
1823
|
+
rekeyedBy
|
|
1824
|
+
}),
|
|
1825
|
+
changedNames: changes.map((change) => change.name),
|
|
1826
|
+
previousHash: baseline?.fingerprint ?? null
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
function predictBuildMiss({ root, platform, current, prebuild }) {
|
|
1830
|
+
const { reason } = explainBuildMiss({
|
|
1831
|
+
root,
|
|
1832
|
+
platform,
|
|
1833
|
+
current
|
|
1834
|
+
});
|
|
1835
|
+
const baseline = reason.baseline && {
|
|
1836
|
+
fingerprint: reason.baseline.fingerprint,
|
|
1837
|
+
from: reason.baseline.from
|
|
1838
|
+
};
|
|
1839
|
+
if (!prebuild || !baseline) return {
|
|
1840
|
+
...reason,
|
|
1841
|
+
baseline
|
|
1842
|
+
};
|
|
1843
|
+
const dir = platform === "ios" ? "ios/" : "android/";
|
|
1844
|
+
return {
|
|
1845
|
+
...reason,
|
|
1846
|
+
kind: "prebuild-pending",
|
|
1847
|
+
summary: `${reason.summary} (before prebuild ${prebuild}s ${dir})`,
|
|
1848
|
+
baseline
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
function skippedMissReason(summary) {
|
|
1852
|
+
return {
|
|
1853
|
+
kind: "cache-skipped",
|
|
1854
|
+
summary,
|
|
1855
|
+
changes: [],
|
|
1856
|
+
changeCount: 0,
|
|
1857
|
+
baseline: null,
|
|
1858
|
+
rekeyedBy: []
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
function fingerprintErrorMissReason(message) {
|
|
1862
|
+
return {
|
|
1863
|
+
kind: "fingerprint-error",
|
|
1864
|
+
summary: `fingerprinting failed: ${message}`,
|
|
1865
|
+
changes: [],
|
|
1866
|
+
changeCount: 0,
|
|
1867
|
+
baseline: null,
|
|
1868
|
+
rekeyedBy: []
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
//#endregion
|
|
1872
|
+
//#region src/commands/build-plan.ts
|
|
1873
|
+
const DISCARD = {
|
|
1874
|
+
file: "",
|
|
1875
|
+
written: 0,
|
|
1876
|
+
dropped: 0,
|
|
1877
|
+
lastError: null,
|
|
1878
|
+
write: () => true,
|
|
1879
|
+
close: () => ({
|
|
1880
|
+
file: "",
|
|
1881
|
+
written: 0,
|
|
1882
|
+
dropped: 0,
|
|
1883
|
+
lastError: null
|
|
1884
|
+
})
|
|
1885
|
+
};
|
|
1886
|
+
async function providerHit({ root, platform, cacheKey, providerConfig }, deps) {
|
|
1887
|
+
if (!providerConfig) return null;
|
|
1888
|
+
let destinationDir;
|
|
1889
|
+
try {
|
|
1890
|
+
destinationDir = makeTemporaryDirectory(root, "stim-plan-");
|
|
1891
|
+
} catch (error) {
|
|
1892
|
+
deps.note(chalk.yellow(phaseLine("cache", `provider not checked: ${error?.message || error}`)));
|
|
1893
|
+
return null;
|
|
1894
|
+
}
|
|
1895
|
+
try {
|
|
1896
|
+
const found = await resolveTieredBuild({
|
|
1897
|
+
local: {
|
|
1898
|
+
resolve: () => null,
|
|
1899
|
+
store: () => null
|
|
1900
|
+
},
|
|
1901
|
+
loadProvider: () => deps.loadCacheProvider({
|
|
1902
|
+
projectRoot: root,
|
|
1903
|
+
config: providerConfig
|
|
1904
|
+
}),
|
|
1905
|
+
target: {
|
|
1906
|
+
projectRoot: root,
|
|
1907
|
+
platform,
|
|
1908
|
+
key: cacheKey
|
|
1909
|
+
},
|
|
1910
|
+
destinationDir,
|
|
1911
|
+
warn: (_code, message) => deps.note(chalk.yellow(phaseLine("cache", message)))
|
|
1912
|
+
});
|
|
1913
|
+
return found ? found.providerName ?? "the cache provider" : null;
|
|
1914
|
+
} finally {
|
|
1915
|
+
removeTemporaryEntry(destinationDir);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
async function expoRemoteHit(lookup, deps) {
|
|
1919
|
+
if (!lookup.expoRemote) return null;
|
|
1920
|
+
const { root, platform, isExpo, fingerprint } = lookup;
|
|
1921
|
+
const loaded = await deps.loadProjectProvider(root, { isExpo });
|
|
1922
|
+
if (loaded?.unavailable) {
|
|
1923
|
+
deps.note(chalk.yellow(phaseLine("cache", `provider not usable: ${loaded.unavailable}`)));
|
|
1924
|
+
return null;
|
|
1925
|
+
}
|
|
1926
|
+
if (!loaded?.provider) return null;
|
|
1927
|
+
const name = loaded.name ?? "the build cache provider";
|
|
1928
|
+
if (name === "eas") {
|
|
1929
|
+
const auth = deps.checkEasAuth({
|
|
1930
|
+
projectRoot: root,
|
|
1931
|
+
owner: loaded.owner || null
|
|
1932
|
+
});
|
|
1933
|
+
const authNote = easAuthNote(auth);
|
|
1934
|
+
if (authNote) deps.note(chalk.yellow(phaseLine("cache", authNote)));
|
|
1935
|
+
if (auth?.code === "logged-out") return null;
|
|
1936
|
+
}
|
|
1937
|
+
const hit = await deps.resolveRemote({
|
|
1938
|
+
provider: loaded.provider,
|
|
1939
|
+
platform,
|
|
1940
|
+
projectRoot: root,
|
|
1941
|
+
fingerprintHash: fingerprint,
|
|
1942
|
+
runOptions: lookup.expoRemote.runOptions,
|
|
1943
|
+
logWriter: DISCARD
|
|
1944
|
+
});
|
|
1945
|
+
if (hit?.timedOut) deps.note(chalk.yellow(phaseLine("cache", `${name} did not answer; counted as a miss`)));
|
|
1946
|
+
else if (hit?.failed) deps.note(chalk.yellow(phaseLine("cache", `${name} could not be used: ${hit.failed}`)));
|
|
1947
|
+
return hit?.appPath ? name : null;
|
|
1948
|
+
}
|
|
1949
|
+
async function planCachedBuild(lookup, deps) {
|
|
1950
|
+
const { root, platform, isExpo, fingerprint, sources, cacheKey, cachePolicy } = lookup;
|
|
1951
|
+
let cacheHit = false;
|
|
1952
|
+
let provider = null;
|
|
1953
|
+
if (cachePolicy.read) {
|
|
1954
|
+
if (artifactIn(entryDir(platform, cacheKey))) cacheHit = "local";
|
|
1955
|
+
else {
|
|
1956
|
+
provider = (cachePolicy.remote ? await providerHit(lookup, deps) : null) ?? await expoRemoteHit(lookup, deps);
|
|
1957
|
+
if (provider) cacheHit = "remote";
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
let prebuild = null;
|
|
1961
|
+
let refusal = null;
|
|
1962
|
+
let missReason;
|
|
1963
|
+
if (!cacheHit) {
|
|
1964
|
+
prebuild = deps.planPrebuild(root, platform, {
|
|
1965
|
+
isExpo,
|
|
1966
|
+
fingerprint,
|
|
1967
|
+
sources
|
|
1968
|
+
});
|
|
1969
|
+
if (prebuild === "refuse") refusal = staleNativeDirRefusal(platform);
|
|
1970
|
+
else if (cachePolicy.read) missReason = predictBuildMiss({
|
|
1971
|
+
root,
|
|
1972
|
+
platform,
|
|
1973
|
+
current: {
|
|
1974
|
+
hash: fingerprint,
|
|
1975
|
+
sources
|
|
1976
|
+
},
|
|
1977
|
+
prebuild: prebuild === "generate" || prebuild === "regenerate" ? prebuild : null
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
return planPayload(lookup, {
|
|
1981
|
+
fingerprint,
|
|
1982
|
+
cacheKey,
|
|
1983
|
+
cacheHit,
|
|
1984
|
+
provider,
|
|
1985
|
+
cacheSkipped: !cachePolicy.read,
|
|
1986
|
+
prebuild,
|
|
1987
|
+
...missReason ? { missReason } : {},
|
|
1988
|
+
refusal
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1991
|
+
function planPayload({ platform, slot, projectKey }, found) {
|
|
1992
|
+
const { refusal, missReason, ...rest } = found;
|
|
1993
|
+
const outcome = refusal ? null : found.cacheHit ? "hit" : "cold";
|
|
1994
|
+
const estimate = outcome ? estimateBuild(readStats().record?.history?.[projectKey], platform, outcome, "prepare") : {
|
|
1995
|
+
expectedMs: null,
|
|
1996
|
+
basis: 0
|
|
1997
|
+
};
|
|
1998
|
+
return {
|
|
1999
|
+
platform,
|
|
2000
|
+
...slot === "default" ? {} : { slot },
|
|
2001
|
+
...rest,
|
|
2002
|
+
outcome,
|
|
2003
|
+
expectedMs: estimate.expectedMs,
|
|
2004
|
+
basis: estimate.basis,
|
|
2005
|
+
...missReason ? { missReason } : {},
|
|
2006
|
+
...refusal ? { refusal } : {}
|
|
2007
|
+
};
|
|
2008
|
+
}
|
|
2009
|
+
function cacheLine(plan) {
|
|
2010
|
+
if (plan.refusal) return `the build would refuse with ${plan.refusal.code}`;
|
|
2011
|
+
if (plan.cacheHit === "local") return "local cache hit";
|
|
2012
|
+
if (plan.cacheHit === "remote") return `remote cache hit (${plan.provider})`;
|
|
2013
|
+
return `${plan.cacheSkipped ? "cache reads are off" : "cache miss"}: compiles${plan.prebuild === "generate" || plan.prebuild === "regenerate" ? `, ${plan.prebuild}s the native dir` : ""}`;
|
|
2014
|
+
}
|
|
2015
|
+
function printPlan(plan, json) {
|
|
2016
|
+
if (json) {
|
|
2017
|
+
console.log(JSON.stringify(plan));
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
const slot = plan.slot ? ` [${plan.slot}]` : "";
|
|
2021
|
+
console.log(phaseLine("plan", `${plan.platform}${slot} ${shortHash(plan.fingerprint)} -> ${cacheLine(plan)}`));
|
|
2022
|
+
if (plan.missReason) console.log(phaseLine("cache", `miss: ${plan.missReason.summary}`));
|
|
2023
|
+
if (plan.refusal) {
|
|
2024
|
+
console.log(phaseLine("error", plan.refusal.message));
|
|
2025
|
+
console.log(phaseLine("remedy", plan.refusal.remedy));
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
console.log(phaseLine("expect", plan.expectedMs === null ? `unknown: no ${plan.outcome} run of this project is recorded yet` : `~${formatDuration(plan.expectedMs)} (median of ${plural(plan.basis, `${plan.outcome} run`)})`));
|
|
2029
|
+
}
|
|
2030
|
+
function refusePlan({ code, message, remedy }, json, lines = []) {
|
|
2031
|
+
console.error(chalk.red(phaseLine("error", message)));
|
|
2032
|
+
for (const line of lines) console.error(chalk.dim(phaseLine("", line)));
|
|
2033
|
+
console.error(chalk.dim(phaseLine("remedy", remedy)));
|
|
2034
|
+
console.error(chalk.red(phaseLine("failed", code)));
|
|
2035
|
+
if (json) console.log(JSON.stringify({
|
|
2036
|
+
code,
|
|
2037
|
+
message,
|
|
2038
|
+
remedy
|
|
2039
|
+
}));
|
|
2040
|
+
process.exitCode = 1;
|
|
2041
|
+
}
|
|
2042
|
+
const PLAN_ONLY_REMEDY = "Drop the flag to plan the owned simulator or emulator build, or run the command without --plan.";
|
|
2043
|
+
function planFlagRefusal(flag) {
|
|
2044
|
+
return {
|
|
2045
|
+
code: "STIM_BAD_ARG",
|
|
2046
|
+
message: `${flag} does not apply to --plan, which predicts the next local build without choosing a device.`,
|
|
2047
|
+
remedy: PLAN_ONLY_REMEDY
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
//#endregion
|
|
2051
|
+
export { ensureDevServer as C, sleep$1 as D, launchOutcomeRecord as E, warmMetro as O, clearIosAdoptionPending as S, isPhysicalDeviceRequest as T, staleNativeDirRefusal as _, refusePlan as a, AvdBootError as b, skippedMissReason as c, schemesFromInfoPlist as d, detectEntryFile as f, runPrebuild as g, recordPrebuild as h, printPlan as i, devClientScheme as l, planPrebuild as m, planFlagRefusal as n, explainBuildMiss as o, swapJsBundle as p, planPayload as r, fingerprintErrorMissReason as s, planCachedBuild as t, pickDevClientScheme as u, ensureBooted as v, ensureWorkspaceStorageSafely as w, AvdRecoveryError as x, ensureOwnedDevice as y };
|