stim 1.10.0 → 1.11.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-BtG4Va9l.mjs} +8 -182
- package/dist/{android-DPov-fv2.mjs → android-BA2i_KAV.mjs} +1 -1
- package/dist/{android-DBkeHwpm.mjs → android-C1q4l523.mjs} +376 -99
- package/dist/{android-787HhP5w.mjs → android-C4zG2Tuc.mjs} +63 -18
- package/dist/{android-cas-CITmIeWY.mjs → android-cas-DfJkwCXW.mjs} +3 -3
- package/dist/{app-install-CrJ6t_bv.mjs → app-install-DHut6JnV.mjs} +1 -1
- package/dist/{budget-BP4jZZL4.mjs → budget-De3P2rq_.mjs} +9 -9
- package/dist/{dev-client-92U593KT.mjs → build-plan-CeAYgKAx.mjs} +415 -24
- package/dist/build-progress-CDwogz6R.mjs +179 -0
- package/dist/cli.mjs +14 -14
- package/dist/collector-run.mjs +1 -1
- package/dist/{deps-DP4eWqea.mjs → deps-OF7CD5Dx.mjs} +2 -2
- package/dist/{device-DhTZe7L4.mjs → device-BwH2iI3O.mjs} +47 -9
- package/dist/{device-lease-CbEaIk7b.mjs → device-lease-D83nqeMX.mjs} +51 -51
- package/dist/{device-pool-Di8Q-9Ro.mjs → device-pool-DHj7cdPy.mjs} +2 -2
- package/dist/{device-remote-Dy1jGj3U.mjs → device-remote-BQZSnnmE.mjs} +7 -7
- package/dist/{doctor-B-inAJmt.mjs → doctor-D8QdMRMf.mjs} +12 -12
- package/dist/{error-diagnostics-xxWKNO3-.mjs → error-diagnostics-UYFSQwRy.mjs} +3 -3
- package/dist/{gc-ChPFZMRC.mjs → gc-COZteyiq.mjs} +27 -253
- package/dist/{guide-DwmaD_Wa.mjs → guide-NDvTmMi-.mjs} +336 -48
- package/dist/{idle-DpRLp5lj.mjs → idle-NETbeNiQ.mjs} +4 -3
- package/dist/{in-use-CjUluqIX.mjs → in-use-DVhlSoyn.mjs} +4 -4
- package/dist/{ios-Dd-hYJBh.mjs → ios-CQLKpNfr.mjs} +294 -81
- package/dist/{ios-B2Fpqvlb.mjs → ios-DArbmwer.mjs} +16 -7
- package/dist/{ios-device-CtRdEVjx.mjs → ios-device-CwDZo0X4.mjs} +1 -1
- package/dist/{launch-verify-CQS_Kg-C.mjs → launch-verify-CuPDGXdr.mjs} +1 -1
- package/dist/logs-B44a1UeU.mjs +485 -0
- package/dist/merge-state-DDxS-XIZ.mjs +232 -0
- package/dist/{named-ports-DiTfSz6p.mjs → named-ports-Dc7neAlc.mjs} +1 -1
- package/dist/native-run-D7yB_AsB.mjs +143 -0
- package/dist/{ownership-DZjdx8Gq.mjs → ownership-BiojdtAp.mjs} +3 -3
- package/dist/{ports-CLSSFOuz.mjs → ports-DwBaRFJs.mjs} +1 -1
- package/dist/{reload-DoETJ3rl.mjs → reload-By9Dwsrt.mjs} +4 -4
- package/dist/{server-bare-DVbb7kXH.mjs → server-bare-D_RK1RCj.mjs} +1 -1
- package/dist/server-expo-B-CCUJZj.mjs +2 -0
- package/dist/{server-expo-BvOCeakp.mjs → server-expo-o6H5gcBc.mjs} +1 -1
- package/dist/{settings-DOugPlPz.mjs → settings-B5oFZEri.mjs} +12 -3
- package/dist/{settings-EWYNwe72.mjs → settings-C0ugALSC.mjs} +1 -1
- package/dist/settings.schema.json +52 -0
- package/dist/{simslim-Ca3487H6.mjs → simslim-CIR3IYJO.mjs} +150 -95
- package/dist/{slot-launch-C5cg0fmk.mjs → slot-launch-rggO1Esw.mjs} +2 -2
- package/dist/{spawn-claims-CUleC2cM.mjs → spawn-claims-BJwXTMPt.mjs} +24 -1
- package/dist/{start-F9gv5dcC.mjs → start-BSsf2tUs.mjs} +4 -4
- package/dist/{start-VoFsPSes.mjs → start-i8zc9VmS.mjs} +1 -1
- package/dist/{status-DLCD-o3s.mjs → status-CitcBRsC.mjs} +227 -22
- package/dist/{status-Dr4fRkym.mjs → status-Q-WDEGCB.mjs} +20 -6
- package/dist/{stop-Dj4pfmyv.mjs → stop-DIhrvu0B.mjs} +188 -27
- package/dist/stop-rst5mL14.mjs +3 -0
- package/dist/supervisor-run.mjs +7 -7
- package/dist/{workspace-process-lock-BXA96myD.mjs → workspace-process-lock-Cx74RSDZ.mjs} +9 -4
- package/dist/{workspaces-BTUyFfkv.mjs → workspaces-COD2xCz-.mjs} +15 -5
- package/dist/{worktree-4aZg2NnI.mjs → worktree-DhxDTK44.mjs} +1 -1
- package/dist/{worktree-BMS7GeqB.mjs → worktree-KOjMX3zA.mjs} +8 -8
- package/dist/{xcode-B2tBkf0m.mjs → xcode-B5SPD--o.mjs} +3 -3
- package/package.json +5 -5
- package/dist/logs-MARIy6m9.mjs +0 -209
- package/dist/server-expo-DBVTdult.mjs +0 -2
- package/dist/stop-ZvOeTmKb.mjs +0 -3
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
import { H as projectDeviceSlots, O as acquireAvdClaim, S as setDevice, T as withConfigLock$1, g as loadConfig$1, i as clearDevice, t as allConsolePortsAndSerials, u as getConfigDir, v as releaseAndroidConsolePort, x as saveConfig, z as deviceSlotPlatforms } from "./config-BEq0Qw_a.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-BWLOJ_Wr.mjs";
|
|
4
|
-
import { D as makeTemporaryDirectory, O as removeTemporaryEntry } from "./worktree-BlizKDpZ.mjs";
|
|
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-BWLOJ_Wr.mjs";
|
|
4
|
+
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, c as gitCommonDirOnDisk } from "./worktree-BlizKDpZ.mjs";
|
|
5
5
|
import { a as detectIsExpo, c as ownedDeviceLabel, s as isPackageResolvable } from "./project-BXZzOABt.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-
|
|
6
|
+
import { a as spawnDeclared } from "./spawn-claims-BJwXTMPt.mjs";
|
|
7
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-Cx74RSDZ.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-D5z2_bcF.mjs";
|
|
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-D5z2_bcF.mjs";
|
|
10
|
+
import { N as storedSources, S as entryDir, x as diffFingerprintSources, y as artifactIn } from "./device-lease-D83nqeMX.mjs";
|
|
11
|
+
import { D as listAvds, F as ownedAvdName, H as resolveOwnedAvdSerial, L as ownedAvdSystemImage, M as ownedAvdDeviceProfile, O as listInstalledSystemImages, P as ownedAvdMatchesConfiguration, S as getAvdNameForSerial, T as listAdbDevices, f as avdPoolConfiguration, g as createOwnedAvd, h as configureNewOwnedAvd, j as nextConsolePort, m as bootAndroidEmulator, q as waitForBoot, t as DEFAULT_AVD_DEVICE_PROFILE, u as assertOwnedAvdStopped, z as pickDefaultSystemImage } from "./android-C4zG2Tuc.mjs";
|
|
12
|
+
import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting, v as iosSimSlimProfileSetting } from "./settings-B5oFZEri.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-BiojdtAp.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-DArbmwer.mjs";
|
|
15
|
+
import { o as estimateBuild } from "./build-progress-CDwogz6R.mjs";
|
|
16
|
+
import { i as expoBinRefusal, r as expoBinPath, t as cleanLine } from "./server-expo-o6H5gcBc.mjs";
|
|
17
|
+
import { N as easAuthNote } from "./device-remote-BQZSnnmE.mjs";
|
|
18
|
+
import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-CuPDGXdr.mjs";
|
|
19
|
+
import { a as liveOwnedDeviceCount, i as deviceTypeMismatch, t as reconcileSimSlim } from "./simslim-CIR3IYJO.mjs";
|
|
20
|
+
import { s as startDevServer } from "./start-BSsf2tUs.mjs";
|
|
21
|
+
import { r as readStats } from "./stats-BAgJaLA4.mjs";
|
|
22
|
+
import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-B5SPD--o.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-/, ""))
|
|
@@ -669,7 +677,9 @@ const ANDROID_FRESH_BOOT_TIMEOUT_MS = process.platform === "win32" ? 12e5 : 12e4
|
|
|
669
677
|
const ANDROID_BOOT_TIMEOUT_MS = process.platform === "win32" ? 12e5 : 24e4;
|
|
670
678
|
async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoot, label, settings, flags, note, out, logFile, alive, configureAvd, teardownAvd }) {
|
|
671
679
|
const avdConfig = androidAvdConfigSetting(settings, settingsRoot);
|
|
672
|
-
const
|
|
680
|
+
const requestedProfile = flags.deviceProfile || settings.android?.deviceProfile || null;
|
|
681
|
+
const deviceProfile = requestedProfile ?? DEFAULT_AVD_DEVICE_PROFILE;
|
|
682
|
+
const configuration = avdPoolConfiguration(androidDataPartitionSizeGbSetting(settings), avdConfig, deviceProfile);
|
|
673
683
|
if (record?.setupIncomplete && record.avdName) {
|
|
674
684
|
const avdName = record.avdName;
|
|
675
685
|
const cleanup = teardownAvd(avdName, {
|
|
@@ -689,6 +699,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
689
699
|
if (record?.avdName) {
|
|
690
700
|
if (record.owned) {
|
|
691
701
|
const resolved = resolveOwnedAvdSerial(record.avdName);
|
|
702
|
+
const currentProfile = resolved.notOwned || resolved.missing ? null : ownedAvdDeviceProfile(record.avdName);
|
|
703
|
+
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
704
|
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
705
|
else if (resolved.serial) {
|
|
694
706
|
const consolePort = Number(resolved.serial.replace(/^emulator-/, ""));
|
|
@@ -703,7 +715,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
703
715
|
setDevice(projectPath, "android", updated, slot);
|
|
704
716
|
return {
|
|
705
717
|
...updated,
|
|
706
|
-
systemImage: ownedAvdSystemImage(record.avdName)
|
|
718
|
+
systemImage: ownedAvdSystemImage(record.avdName),
|
|
719
|
+
deviceProfile: currentProfile
|
|
707
720
|
};
|
|
708
721
|
} else if (!resolved.missing) {
|
|
709
722
|
out(chalk.dim(`Recorded port for owned AVD ${record.avdName} is not currently ours; booting it on a freshly allocated port...`));
|
|
@@ -718,7 +731,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
718
731
|
logFile,
|
|
719
732
|
alive
|
|
720
733
|
}),
|
|
721
|
-
systemImage: ownedAvdSystemImage(record.avdName)
|
|
734
|
+
systemImage: ownedAvdSystemImage(record.avdName),
|
|
735
|
+
deviceProfile: currentProfile
|
|
722
736
|
};
|
|
723
737
|
}
|
|
724
738
|
} else if (listAvds().includes(record.avdName)) {
|
|
@@ -780,7 +794,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
780
794
|
alive
|
|
781
795
|
}),
|
|
782
796
|
adopted: true,
|
|
783
|
-
systemImage
|
|
797
|
+
systemImage,
|
|
798
|
+
deviceProfile
|
|
784
799
|
};
|
|
785
800
|
}
|
|
786
801
|
}
|
|
@@ -790,6 +805,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
790
805
|
label,
|
|
791
806
|
previousAvdName: record?.avdName,
|
|
792
807
|
systemImage: flags.systemImage || settings.android?.systemImage || void 0,
|
|
808
|
+
deviceProfile,
|
|
793
809
|
configuration,
|
|
794
810
|
configure: (avdName) => configureAvd(avdName, {
|
|
795
811
|
dataPartitionSizeGb: androidDataPartitionSizeGbSetting(settings),
|
|
@@ -817,7 +833,8 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
|
|
|
817
833
|
alive
|
|
818
834
|
}),
|
|
819
835
|
created: created.created,
|
|
820
|
-
systemImage: created.systemImage
|
|
836
|
+
systemImage: created.systemImage,
|
|
837
|
+
deviceProfile: created.deviceProfile
|
|
821
838
|
};
|
|
822
839
|
}
|
|
823
840
|
function claimAndroidConsolePort({ projectPath, slot, avdName, deviceName, livePorts = [], metadata }, { lock = withConfigLock$1, recordedPorts = () => allConsolePortsAndSerials().androidConsolePorts, record = setDevice } = {}) {
|
|
@@ -1572,4 +1589,378 @@ function hasDevClient(root) {
|
|
|
1572
1589
|
return isPackageResolvable(root, "expo-dev-client");
|
|
1573
1590
|
}
|
|
1574
1591
|
//#endregion
|
|
1575
|
-
|
|
1592
|
+
//#region src/cache/miss-reason.ts
|
|
1593
|
+
const MISS_CHANGE_CAP = 20;
|
|
1594
|
+
function recordedBuilds(state, platform) {
|
|
1595
|
+
const builds = [];
|
|
1596
|
+
for (const value of [state?.[LAST_BUILD_KEYS[platform]], state?.lastBuild]) {
|
|
1597
|
+
const record = value;
|
|
1598
|
+
if (record?.platform !== platform) continue;
|
|
1599
|
+
const startedAt = typeof record.startedAt === "string" ? record.startedAt : "";
|
|
1600
|
+
if (typeof record.fingerprint === "string" && typeof record.cacheKey === "string") builds.push({
|
|
1601
|
+
fingerprint: record.fingerprint,
|
|
1602
|
+
cacheKey: record.cacheKey,
|
|
1603
|
+
startedAt
|
|
1604
|
+
});
|
|
1605
|
+
const carried = record.missReason?.baseline;
|
|
1606
|
+
if (typeof carried?.fingerprint === "string" && typeof carried.cacheKey === "string") builds.push({
|
|
1607
|
+
fingerprint: carried.fingerprint,
|
|
1608
|
+
cacheKey: carried.cacheKey,
|
|
1609
|
+
startedAt
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
return builds;
|
|
1613
|
+
}
|
|
1614
|
+
function projectIdentity(root) {
|
|
1615
|
+
let real;
|
|
1616
|
+
try {
|
|
1617
|
+
real = realpathSync.native(root);
|
|
1618
|
+
} catch {
|
|
1619
|
+
return null;
|
|
1620
|
+
}
|
|
1621
|
+
for (let dir = real;; dir = dirname(dir)) {
|
|
1622
|
+
if (existsSync(join(dir, ".git"))) {
|
|
1623
|
+
const common = gitCommonDirOnDisk(dir);
|
|
1624
|
+
return common ? `${common}\n${relative(dir, real)}` : null;
|
|
1625
|
+
}
|
|
1626
|
+
if (dirname(dir) === dir) return null;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
function findMissBaseline(root, platform, { readState = readWorkspaceState, projectRoots = () => Object.keys(loadConfig()?.projects ?? {}), identity = projectIdentity, sourcesOf = (p, key) => storedSources(p, key, sharedBuildCache()) } = {}) {
|
|
1630
|
+
for (const build of recordedBuilds(readState(root), platform)) {
|
|
1631
|
+
const sources = sourcesOf(platform, build.cacheKey);
|
|
1632
|
+
if (sources) return {
|
|
1633
|
+
fingerprint: build.fingerprint,
|
|
1634
|
+
cacheKey: build.cacheKey,
|
|
1635
|
+
sources,
|
|
1636
|
+
from: "workspace"
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
const project = identity(root);
|
|
1640
|
+
if (!project) return null;
|
|
1641
|
+
const others = [];
|
|
1642
|
+
for (const other of projectRoots()) {
|
|
1643
|
+
if (other === root || identity(other) !== project) continue;
|
|
1644
|
+
others.push(...recordedBuilds(readState(other), platform));
|
|
1645
|
+
}
|
|
1646
|
+
for (const build of others.toSorted((a, b) => (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0))) {
|
|
1647
|
+
const sources = sourcesOf(platform, build.cacheKey);
|
|
1648
|
+
if (sources) return {
|
|
1649
|
+
fingerprint: build.fingerprint,
|
|
1650
|
+
cacheKey: build.cacheKey,
|
|
1651
|
+
sources,
|
|
1652
|
+
from: "project"
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
const CATEGORY_RANK = {
|
|
1658
|
+
"native-dependency": 0,
|
|
1659
|
+
"app-config": 1,
|
|
1660
|
+
"config-plugin": 2,
|
|
1661
|
+
package: 3,
|
|
1662
|
+
"native-dir": 4,
|
|
1663
|
+
file: 5,
|
|
1664
|
+
"app-asset": 6,
|
|
1665
|
+
other: 7,
|
|
1666
|
+
"package-scripts": 8,
|
|
1667
|
+
autolinking: 9
|
|
1668
|
+
};
|
|
1669
|
+
function packageIn(path) {
|
|
1670
|
+
return [...path.matchAll(/(?:^|\/)node_modules\/((?:@[^/]+\/)?[^/]+)/g)].at(-1)?.[1] ?? null;
|
|
1671
|
+
}
|
|
1672
|
+
function describeChange({ name, change, reasons }) {
|
|
1673
|
+
const pkg = packageIn(name);
|
|
1674
|
+
if (name === "expoConfig" || reasons.includes("expoConfig")) return {
|
|
1675
|
+
category: "app-config",
|
|
1676
|
+
label: "app config changed"
|
|
1677
|
+
};
|
|
1678
|
+
if (pkg && reasons.some((reason) => /^(expo|rncore)Autolinking(Ios|Android)?$/.test(reason))) return {
|
|
1679
|
+
category: "native-dependency",
|
|
1680
|
+
label: `native dependency ${change}: ${pkg}`
|
|
1681
|
+
};
|
|
1682
|
+
if (pkg && reasons.includes("expoConfigPlugins")) return {
|
|
1683
|
+
category: "config-plugin",
|
|
1684
|
+
label: `config plugin ${change}: ${pkg}`
|
|
1685
|
+
};
|
|
1686
|
+
if (/^(expoAutolinkingConfig|rncoreAutolinkingConfig)(:|$)/.test(name)) return {
|
|
1687
|
+
category: "autolinking",
|
|
1688
|
+
label: "autolinking config changed"
|
|
1689
|
+
};
|
|
1690
|
+
if (name.startsWith("package:")) return {
|
|
1691
|
+
category: "package",
|
|
1692
|
+
label: `${name.slice(8)} ${change}`
|
|
1693
|
+
};
|
|
1694
|
+
if (name === "packageJson:scripts") return {
|
|
1695
|
+
category: "package-scripts",
|
|
1696
|
+
label: "package.json scripts changed"
|
|
1697
|
+
};
|
|
1698
|
+
if (reasons.includes("expoConfigExternalFile")) return {
|
|
1699
|
+
category: "app-asset",
|
|
1700
|
+
label: `app asset ${change}: ${name}`
|
|
1701
|
+
};
|
|
1702
|
+
if (reasons.includes("bareNativeDir")) return {
|
|
1703
|
+
category: "native-dir",
|
|
1704
|
+
label: `${name}/ ${change}`
|
|
1705
|
+
};
|
|
1706
|
+
if (reasons.length || name.includes("/") || name.includes(".")) return {
|
|
1707
|
+
category: "file",
|
|
1708
|
+
label: `${name} ${change}`
|
|
1709
|
+
};
|
|
1710
|
+
return {
|
|
1711
|
+
category: "other",
|
|
1712
|
+
label: `${name} ${change}`
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function rekeyPrefix(rekeyedBy) {
|
|
1716
|
+
return rekeyedBy.length ? `${rekeyedBy.join(" and ")} changed native inputs; ` : "";
|
|
1717
|
+
}
|
|
1718
|
+
function missReasonFromChanges({ changes, baseline, rekeyedBy = [] }) {
|
|
1719
|
+
const prefix = rekeyPrefix(rekeyedBy);
|
|
1720
|
+
if (!baseline) return {
|
|
1721
|
+
kind: "no-baseline",
|
|
1722
|
+
summary: `${prefix}no earlier build of this project in the cache to compare with`,
|
|
1723
|
+
changes: [],
|
|
1724
|
+
changeCount: 0,
|
|
1725
|
+
baseline: null,
|
|
1726
|
+
rekeyedBy
|
|
1727
|
+
};
|
|
1728
|
+
if (!changes.length) return {
|
|
1729
|
+
kind: "same-sources",
|
|
1730
|
+
summary: `${prefix}native inputs match the last build; no cached app for this configuration or target`,
|
|
1731
|
+
changes: [],
|
|
1732
|
+
changeCount: 0,
|
|
1733
|
+
baseline,
|
|
1734
|
+
rekeyedBy
|
|
1735
|
+
};
|
|
1736
|
+
const described = changes.map((change, index) => ({
|
|
1737
|
+
change,
|
|
1738
|
+
index,
|
|
1739
|
+
...describeChange(change)
|
|
1740
|
+
})).toSorted((a, b) => CATEGORY_RANK[a.category] - CATEGORY_RANK[b.category] || a.index - b.index);
|
|
1741
|
+
const dependencyMoved = described.some((entry) => entry.category === "native-dependency");
|
|
1742
|
+
const labels = [...new Set(described.filter((entry) => !(dependencyMoved && entry.category === "autolinking")).map((entry) => entry.label))];
|
|
1743
|
+
const more = labels.length > 1 ? ` (+${labels.length - 1} more)` : "";
|
|
1744
|
+
return {
|
|
1745
|
+
kind: "changed",
|
|
1746
|
+
summary: `${prefix}${labels[0]}${more}`,
|
|
1747
|
+
changes: described.slice(0, MISS_CHANGE_CAP).map(({ change, category }) => ({
|
|
1748
|
+
source: change.name,
|
|
1749
|
+
change: change.change,
|
|
1750
|
+
category
|
|
1751
|
+
})),
|
|
1752
|
+
changeCount: changes.length,
|
|
1753
|
+
baseline,
|
|
1754
|
+
rekeyedBy
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
function explainBuildMiss({ root, platform, current, rekeyedBy = [], baselineDeps, differ = expoFingerprint.diffFingerprints }) {
|
|
1758
|
+
const baseline = findMissBaseline(root, platform, baselineDeps);
|
|
1759
|
+
const changes = baseline ? diffFingerprintSources({
|
|
1760
|
+
previous: baseline.sources,
|
|
1761
|
+
previousHash: baseline.fingerprint,
|
|
1762
|
+
current,
|
|
1763
|
+
differ
|
|
1764
|
+
}) : [];
|
|
1765
|
+
return {
|
|
1766
|
+
reason: missReasonFromChanges({
|
|
1767
|
+
changes,
|
|
1768
|
+
baseline: baseline ? {
|
|
1769
|
+
fingerprint: baseline.fingerprint,
|
|
1770
|
+
cacheKey: baseline.cacheKey,
|
|
1771
|
+
from: baseline.from
|
|
1772
|
+
} : null,
|
|
1773
|
+
rekeyedBy
|
|
1774
|
+
}),
|
|
1775
|
+
changedNames: changes.map((change) => change.name),
|
|
1776
|
+
previousHash: baseline?.fingerprint ?? null
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
function skippedMissReason(summary) {
|
|
1780
|
+
return {
|
|
1781
|
+
kind: "cache-skipped",
|
|
1782
|
+
summary,
|
|
1783
|
+
changes: [],
|
|
1784
|
+
changeCount: 0,
|
|
1785
|
+
baseline: null,
|
|
1786
|
+
rekeyedBy: []
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
function fingerprintErrorMissReason(message) {
|
|
1790
|
+
return {
|
|
1791
|
+
kind: "fingerprint-error",
|
|
1792
|
+
summary: `fingerprinting failed: ${message}`,
|
|
1793
|
+
changes: [],
|
|
1794
|
+
changeCount: 0,
|
|
1795
|
+
baseline: null,
|
|
1796
|
+
rekeyedBy: []
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
//#endregion
|
|
1800
|
+
//#region src/commands/build-plan.ts
|
|
1801
|
+
const DISCARD = {
|
|
1802
|
+
file: "",
|
|
1803
|
+
written: 0,
|
|
1804
|
+
dropped: 0,
|
|
1805
|
+
lastError: null,
|
|
1806
|
+
write: () => true,
|
|
1807
|
+
close: () => ({
|
|
1808
|
+
file: "",
|
|
1809
|
+
written: 0,
|
|
1810
|
+
dropped: 0,
|
|
1811
|
+
lastError: null
|
|
1812
|
+
})
|
|
1813
|
+
};
|
|
1814
|
+
async function providerHit({ root, platform, cacheKey, providerConfig }, deps) {
|
|
1815
|
+
if (!providerConfig) return null;
|
|
1816
|
+
let destinationDir;
|
|
1817
|
+
try {
|
|
1818
|
+
destinationDir = makeTemporaryDirectory(root, "stim-plan-");
|
|
1819
|
+
} catch (error) {
|
|
1820
|
+
deps.note(chalk.yellow(phaseLine("cache", `provider not checked: ${error?.message || error}`)));
|
|
1821
|
+
return null;
|
|
1822
|
+
}
|
|
1823
|
+
try {
|
|
1824
|
+
const found = await resolveTieredBuild({
|
|
1825
|
+
local: {
|
|
1826
|
+
resolve: () => null,
|
|
1827
|
+
store: () => null
|
|
1828
|
+
},
|
|
1829
|
+
loadProvider: () => deps.loadCacheProvider({
|
|
1830
|
+
projectRoot: root,
|
|
1831
|
+
config: providerConfig
|
|
1832
|
+
}),
|
|
1833
|
+
target: {
|
|
1834
|
+
projectRoot: root,
|
|
1835
|
+
platform,
|
|
1836
|
+
key: cacheKey
|
|
1837
|
+
},
|
|
1838
|
+
destinationDir,
|
|
1839
|
+
warn: (_code, message) => deps.note(chalk.yellow(phaseLine("cache", message)))
|
|
1840
|
+
});
|
|
1841
|
+
return found ? found.providerName ?? "the cache provider" : null;
|
|
1842
|
+
} finally {
|
|
1843
|
+
removeTemporaryEntry(destinationDir);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
async function expoRemoteHit(lookup, deps) {
|
|
1847
|
+
if (!lookup.expoRemote) return null;
|
|
1848
|
+
const { root, platform, isExpo, fingerprint } = lookup;
|
|
1849
|
+
const loaded = await deps.loadProjectProvider(root, { isExpo });
|
|
1850
|
+
if (loaded?.unavailable) {
|
|
1851
|
+
deps.note(chalk.yellow(phaseLine("cache", `provider not usable: ${loaded.unavailable}`)));
|
|
1852
|
+
return null;
|
|
1853
|
+
}
|
|
1854
|
+
if (!loaded?.provider) return null;
|
|
1855
|
+
const name = loaded.name ?? "the build cache provider";
|
|
1856
|
+
if (name === "eas") {
|
|
1857
|
+
const auth = deps.checkEasAuth({
|
|
1858
|
+
projectRoot: root,
|
|
1859
|
+
owner: loaded.owner || null
|
|
1860
|
+
});
|
|
1861
|
+
const authNote = easAuthNote(auth);
|
|
1862
|
+
if (authNote) deps.note(chalk.yellow(phaseLine("cache", authNote)));
|
|
1863
|
+
if (auth?.code === "logged-out") return null;
|
|
1864
|
+
}
|
|
1865
|
+
const hit = await deps.resolveRemote({
|
|
1866
|
+
provider: loaded.provider,
|
|
1867
|
+
platform,
|
|
1868
|
+
projectRoot: root,
|
|
1869
|
+
fingerprintHash: fingerprint,
|
|
1870
|
+
runOptions: lookup.expoRemote.runOptions,
|
|
1871
|
+
logWriter: DISCARD
|
|
1872
|
+
});
|
|
1873
|
+
if (hit?.timedOut) deps.note(chalk.yellow(phaseLine("cache", `${name} did not answer; counted as a miss`)));
|
|
1874
|
+
else if (hit?.failed) deps.note(chalk.yellow(phaseLine("cache", `${name} could not be used: ${hit.failed}`)));
|
|
1875
|
+
return hit?.appPath ? name : null;
|
|
1876
|
+
}
|
|
1877
|
+
async function planCachedBuild(lookup, deps) {
|
|
1878
|
+
const { root, platform, isExpo, fingerprint, sources, cacheKey, cachePolicy } = lookup;
|
|
1879
|
+
let cacheHit = false;
|
|
1880
|
+
let provider = null;
|
|
1881
|
+
if (cachePolicy.read) {
|
|
1882
|
+
if (artifactIn(entryDir(platform, cacheKey))) cacheHit = "local";
|
|
1883
|
+
else {
|
|
1884
|
+
provider = (cachePolicy.remote ? await providerHit(lookup, deps) : null) ?? await expoRemoteHit(lookup, deps);
|
|
1885
|
+
if (provider) cacheHit = "remote";
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
let prebuild = null;
|
|
1889
|
+
let refusal = null;
|
|
1890
|
+
if (!cacheHit) {
|
|
1891
|
+
prebuild = deps.planPrebuild(root, platform, {
|
|
1892
|
+
isExpo,
|
|
1893
|
+
fingerprint,
|
|
1894
|
+
sources
|
|
1895
|
+
});
|
|
1896
|
+
if (prebuild === "refuse") refusal = staleNativeDirRefusal(platform);
|
|
1897
|
+
}
|
|
1898
|
+
return planPayload(lookup, {
|
|
1899
|
+
fingerprint,
|
|
1900
|
+
cacheKey,
|
|
1901
|
+
cacheHit,
|
|
1902
|
+
provider,
|
|
1903
|
+
cacheSkipped: !cachePolicy.read,
|
|
1904
|
+
prebuild,
|
|
1905
|
+
refusal
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
function planPayload({ platform, slot, projectKey }, found) {
|
|
1909
|
+
const { refusal, ...rest } = found;
|
|
1910
|
+
const outcome = refusal ? null : found.cacheHit ? "hit" : "cold";
|
|
1911
|
+
const estimate = outcome ? estimateBuild(readStats().record?.history?.[projectKey], platform, outcome, "prepare") : {
|
|
1912
|
+
expectedMs: null,
|
|
1913
|
+
basis: 0
|
|
1914
|
+
};
|
|
1915
|
+
return {
|
|
1916
|
+
platform,
|
|
1917
|
+
...slot === "default" ? {} : { slot },
|
|
1918
|
+
...rest,
|
|
1919
|
+
outcome,
|
|
1920
|
+
expectedMs: estimate.expectedMs,
|
|
1921
|
+
basis: estimate.basis,
|
|
1922
|
+
...refusal ? { refusal } : {}
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
function cacheLine(plan) {
|
|
1926
|
+
if (plan.refusal) return `the build would refuse with ${plan.refusal.code}`;
|
|
1927
|
+
if (plan.cacheHit === "local") return "local cache hit";
|
|
1928
|
+
if (plan.cacheHit === "remote") return `remote cache hit (${plan.provider})`;
|
|
1929
|
+
return `${plan.cacheSkipped ? "cache reads are off" : "cache miss"}: compiles${plan.prebuild === "generate" || plan.prebuild === "regenerate" ? `, ${plan.prebuild}s the native dir` : ""}`;
|
|
1930
|
+
}
|
|
1931
|
+
function printPlan(plan, json) {
|
|
1932
|
+
if (json) {
|
|
1933
|
+
console.log(JSON.stringify(plan));
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
const slot = plan.slot ? ` [${plan.slot}]` : "";
|
|
1937
|
+
console.log(phaseLine("plan", `${plan.platform}${slot} ${shortHash(plan.fingerprint)} -> ${cacheLine(plan)}`));
|
|
1938
|
+
if (plan.refusal) {
|
|
1939
|
+
console.log(phaseLine("error", plan.refusal.message));
|
|
1940
|
+
console.log(phaseLine("remedy", plan.refusal.remedy));
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
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`)})`));
|
|
1944
|
+
}
|
|
1945
|
+
function refusePlan({ code, message, remedy }, json, lines = []) {
|
|
1946
|
+
console.error(chalk.red(phaseLine("error", message)));
|
|
1947
|
+
for (const line of lines) console.error(chalk.dim(phaseLine("", line)));
|
|
1948
|
+
console.error(chalk.dim(phaseLine("remedy", remedy)));
|
|
1949
|
+
console.error(chalk.red(phaseLine("failed", code)));
|
|
1950
|
+
if (json) console.log(JSON.stringify({
|
|
1951
|
+
code,
|
|
1952
|
+
message,
|
|
1953
|
+
remedy
|
|
1954
|
+
}));
|
|
1955
|
+
process.exitCode = 1;
|
|
1956
|
+
}
|
|
1957
|
+
const PLAN_ONLY_REMEDY = "Drop the flag to plan the owned simulator or emulator build, or run the command without --plan.";
|
|
1958
|
+
function planFlagRefusal(flag) {
|
|
1959
|
+
return {
|
|
1960
|
+
code: "STIM_BAD_ARG",
|
|
1961
|
+
message: `${flag} does not apply to --plan, which predicts the next local build without choosing a device.`,
|
|
1962
|
+
remedy: PLAN_ONLY_REMEDY
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
//#endregion
|
|
1966
|
+
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 };
|