stim 1.5.0 → 1.7.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 +1 -1
- package/dist/{android-DJWiTBkz.mjs → android-DWssqHQ8.mjs} +165 -661
- package/dist/{android-sobvO9ia.mjs → android-Da-G_o3n.mjs} +646 -548
- package/dist/{android-BW8i_YxU.mjs → android-DeYdjjBP.mjs} +6 -2
- package/dist/{android-cas-CSS8gjR7.mjs → android-cas-SWIjzeuL.mjs} +54 -18
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/{app-install--PAocWvq.mjs → app-install-DJg7bVv-.mjs} +36 -10
- package/dist/build-lock-CgtsB6TV.mjs +604 -0
- package/dist/{build-slots-CuPszJDu.mjs → build-slots-C-oAHRT1.mjs} +2 -2
- package/dist/{cache-manifest-uoOKhgUD.mjs → cache-manifest-CYJi8-Hx.mjs} +2 -2
- package/dist/cache-manifest.d.mts +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +14 -14
- package/dist/collector-run.d.mts +2 -0
- package/dist/collector-run.mjs +7 -7
- package/dist/{command-output-BuIb_ZOI.mjs → command-output-B3ycpsYv.mjs} +2 -2
- package/dist/{config-DMP7L7H6.mjs → config-B7iyKWkL.mjs} +56 -16
- package/dist/{dependency-state-CTL8A9p3.mjs → dependency-state-D0M7oKA7.mjs} +2 -2
- package/dist/{deps-BAGOnrc7.mjs → deps-oCXVNEqQ.mjs} +6 -6
- package/dist/detached-entry-Bpo6ZY_I.mjs +65 -0
- package/dist/{dev-client-BoifdqkU.mjs → dev-client-DDUkWgJL.mjs} +12 -12
- package/dist/{device-BUw4JBuH.mjs → device-CBSaC6BU.mjs} +7 -7
- package/dist/{device-DvKJ454A.mjs → device-CLeDp-KH.mjs} +17 -15
- package/dist/{device-lease-r7yEQ1R8.mjs → device-lease-Ffz_M5t0.mjs} +3 -3
- package/dist/{device-pool-DU4FQKp5.mjs → device-pool-DNSHL5z8.mjs} +4 -4
- package/dist/{device-remote-BTUqbl0i.mjs → device-remote-BprxZp-m.mjs} +11 -11
- package/dist/{doctor-Di_B26uh.mjs → doctor-CTAx9gMu.mjs} +90 -46
- package/dist/{error-diagnostics-CTePWAzW.mjs → error-diagnostics-D1Kaf-eT.mjs} +23 -17
- package/dist/{exec-CyylIdq9.mjs → exec-B29XNuhP.mjs} +14 -2
- package/dist/{gc-C20dWw_8.mjs → gc-CX0KRfLG.mjs} +17 -16
- package/dist/{guide-sAnoty2y.mjs → guide-CPmAG9me.mjs} +61 -22
- package/dist/{guide-status-BF6RsJJp.mjs → guide-status-Craq9z0t.mjs} +3 -3
- package/dist/{ios-BdpkncL6.mjs → ios-BRXYSxSc.mjs} +34 -37
- package/dist/{ios-device-DJOi9FR5.mjs → ios-device-CDbvn5H2.mjs} +2 -2
- package/dist/{ios-device-D3pzTKQE.mjs → ios-device-CFXA46ez.mjs} +1 -1
- package/dist/{ios-Deh5js5V.mjs → ios-lEJ5pKsq.mjs} +3 -3
- package/dist/{logs-BfQdIhiF.mjs → logs-CakdBTtm.mjs} +6 -6
- package/dist/{logs-query-Cdp5aaCH.mjs → logs-query-BGL-Pl12.mjs} +1 -1
- package/dist/metro-C7bcJ0uo.mjs +233 -0
- package/dist/{named-ports-DH9SXd0f.mjs → named-ports-DzMOlmaX.mjs} +57 -42
- package/dist/{ownership-CD_XnpDu.mjs → ownership-Cp52QAK1.mjs} +3 -3
- package/dist/{ownership-CVY9cqCr.mjs → ownership-D5vkdm8H.mjs} +13 -13
- package/dist/{ports-A6uTJSPj.mjs → ports-BIbJaKNW.mjs} +3 -3
- package/dist/{process-output-C7IlIzd7.mjs → process-output-Dl5uKE03.mjs} +1 -1
- package/dist/{project-B_SDt4ip.mjs → project-DDZWf_16.mjs} +3 -3
- package/dist/{reclaim-C_rB25f4.mjs → reclaim-D614jI6_.mjs} +11 -11
- package/dist/{reload-CYBJbJfI.mjs → reload-ChW46VDb.mjs} +9 -9
- package/dist/{server-bare-H3pAhbMs.mjs → server-bare-BQkyxa_b.mjs} +1 -1
- package/dist/{server-expo-Ds8_mfng.mjs → server-expo-BVAA1OqJ.mjs} +23 -11
- package/dist/server-expo-YDDG3eP7.mjs +2 -0
- package/dist/{settings-kO2ZqIKy.mjs → settings-BXXorVM2.mjs} +8 -5
- package/dist/{slot-launch-BpHiHH6A.mjs → slot-launch-DbLvyrup.mjs} +4 -4
- package/dist/{start-DiEfqEWk.mjs → start-TTVILlUi.mjs} +111 -38
- package/dist/{state-CnWzvCWP.mjs → state-DZobzxLW.mjs} +2 -2
- package/dist/{stats-Fo9ACIv7.mjs → stats-BYcZ6n1h.mjs} +1 -1
- package/dist/{stats-DHrO0frH.mjs → stats-CLg5RuDS.mjs} +4 -4
- package/dist/{status-J5LAXpag.mjs → status-DUNy_HZs.mjs} +14 -15
- package/dist/{stop-CM1fejKW.mjs → stop-CnUpnFT5.mjs} +2 -2
- package/dist/{stop-0jJNahwN.mjs → stop-ZrfODZ51.mjs} +13 -13
- package/dist/supervisor-run.mjs +7 -5
- package/dist/{teardown-CAVcWeRD.mjs → teardown-5J5UXz2P.mjs} +14 -17
- package/dist/{workspace-process-lock-Bz19_R9R.mjs → workspace-process-lock-BYfZz_S-.mjs} +1 -1
- package/dist/{workspace-state-WVwWw_hB.mjs → workspace-state-BT5E3SA2.mjs} +3 -3
- package/dist/{worktree-CeUKDAQw.mjs → worktree-BfE4lzHK.mjs} +21 -16
- package/dist/{worktree-DJnBbQZ9.mjs → worktree-FG_ZKKdb.mjs} +13 -9
- package/dist/{xcode-BqDeRkpT.mjs → xcode-B6YaWeg0.mjs} +92 -17
- package/package.json +12 -8
- package/shim/android-optimizations.gradle +6 -0
- package/dist/metro-ato6ef3J.mjs +0 -99
- package/dist/ports-BZprTJRa.mjs +0 -90
- package/dist/server-expo-CSwUVYOa.mjs +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as findProjectRoot } from "./project-
|
|
2
|
-
import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-
|
|
3
|
-
import { t as guideStatus } from "./guide-status-
|
|
1
|
+
import { a as findProjectRoot } from "./project-DDZWf_16.mjs";
|
|
2
|
+
import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-BXXorVM2.mjs";
|
|
3
|
+
import { t as guideStatus } from "./guide-status-Craq9z0t.mjs";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
//#endregion
|
|
6
6
|
//#region src/guide/index.ts
|
|
@@ -516,7 +516,7 @@ line by design (see \`guide logs\`), not this single-payload contract.`,
|
|
|
516
516
|
the remedies all target -- read from the BUILT APK's
|
|
517
517
|
manifest, which on a flavored project is the flavor's
|
|
518
518
|
applicationId, not what the project files say
|
|
519
|
-
debugHttpHost "
|
|
519
|
+
debugHttpHost "127.0.0.1:<port>" on an emulator, "localhost:<port>" on a
|
|
520
520
|
physical device, when the app's SharedPreferences were
|
|
521
521
|
pointed at this workspace's Metro; null when they were not.
|
|
522
522
|
A healthy run reverses only <port> -> <port>, which is what
|
|
@@ -927,8 +927,9 @@ metro is reserved; use stim start and stim stop for managed Metro.
|
|
|
927
927
|
|
|
928
928
|
New allocations scan TCP ports 8900-8999. They skip registry reservations
|
|
929
929
|
and existing listeners, announcing occupied ports and upward retries on
|
|
930
|
-
stderr.
|
|
931
|
-
refusal; stop or release unused allocations in
|
|
930
|
+
stderr. Listener checks require lsof, or netstat on Windows. All 100 ports
|
|
931
|
+
occupied or reserved is a refusal; stop or release unused allocations in
|
|
932
|
+
their owning workspaces.
|
|
932
933
|
|
|
933
934
|
The machine registry, under STIM_HOME, serializes allocation and cleanup.
|
|
934
935
|
The workspace is the nearest package.json directory, resolved through
|
|
@@ -940,9 +941,11 @@ supervise, or capture logs for these servers.
|
|
|
940
941
|
|
|
941
942
|
ports lists named labels and ports, plus Metro marked managed.
|
|
942
943
|
ports stop [label] kills TCP listeners on those named ports and releases
|
|
943
|
-
the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed
|
|
944
|
-
|
|
945
|
-
|
|
944
|
+
the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed;
|
|
945
|
+
on Windows it terminates the listener's process tree with taskkill.
|
|
946
|
+
It prints the PID and command (the image name on Windows) for each stopped
|
|
947
|
+
process. The listener's cwd can be anywhere: the named reservation is
|
|
948
|
+
permission to stop that listener.
|
|
946
949
|
Reserve only services this workspace may stop. --dry-run prints what would
|
|
947
950
|
be stopped and released, without doing either. A failed inspection or stop
|
|
948
951
|
keeps that allocation for retry; other labels are still processed.
|
|
@@ -1380,6 +1383,25 @@ code, never on the message.`,
|
|
|
1380
1383
|
object, and upgrading the CLI does not clear it. Empty that one cache with
|
|
1381
1384
|
\`gc --delete --cache "compilation cache"\`, then build again. The next
|
|
1382
1385
|
build is a cold one.`
|
|
1386
|
+
},
|
|
1387
|
+
STIM_PATH_TOO_LONG: {
|
|
1388
|
+
summary: "Windows only: the project root leaves no room for the NDK object paths ninja must open",
|
|
1389
|
+
body: () => `STIM_PATH_TOO_LONG (android, Windows only)
|
|
1390
|
+
The NDK's ninja is not long-path aware, so every CMake object path has to
|
|
1391
|
+
stay under Windows' MAX_PATH. React Native's codegen objects are named
|
|
1392
|
+
after their mangled absolute source path, so each carries the project root
|
|
1393
|
+
twice unless CMake shortens it, and CMake only shortens a name that then
|
|
1394
|
+
fits the object path limit Stim's Gradle shim sets. The message names the
|
|
1395
|
+
longest known object (measured on the e2e fixture, for the ABI being
|
|
1396
|
+
built) and the longest root it leaves room for. Refused BEFORE Gradle runs
|
|
1397
|
+
-- a cache hit still installs -- because the build would otherwise fail
|
|
1398
|
+
deep inside ninja ("mkdir ... No such file or directory" or "Filename
|
|
1399
|
+
longer than 260 characters"). Map the project to a drive letter (\`subst
|
|
1400
|
+
X: <root>\`, then run Stim from X:\\) or move it under a shorter root.
|
|
1401
|
+
A custom Gradle buildStagingDirectory can be shorter than the default; this
|
|
1402
|
+
preflight does not inspect that configured path and may refuse it too.
|
|
1403
|
+
\`doctor --platform android\` reports the same root as a finding for the
|
|
1404
|
+
emulator's ABI.`
|
|
1383
1405
|
},
|
|
1384
1406
|
fallbacks: {
|
|
1385
1407
|
summary: "release cache-hit notes that are not codes: swap failure, asset gate, uninstall, device fallbacks",
|
|
@@ -2317,7 +2339,7 @@ not on any remote" (worktree remove)
|
|
|
2317
2339
|
the no-install form.
|
|
2318
2340
|
|
|
2319
2341
|
"Unsupported engine" or a syntax error before Stim starts
|
|
2320
|
-
Stim requires Node
|
|
2342
|
+
Stim requires Node 22.12.0 or later.
|
|
2321
2343
|
Switch Node versions, then run the command again.
|
|
2322
2344
|
|
|
2323
2345
|
|
|
@@ -2527,7 +2549,9 @@ from bundle delivery and readiness verification: it gives CoreSimulator time
|
|
|
2527
2549
|
to accept the launch, not the app extra time to report readiness. A timeout
|
|
2528
2550
|
still fails the launch; Stim does not automatically retry it.
|
|
2529
2551
|
|
|
2530
|
-
For an unverified Android launch, follow the emitted device-specific remedy.
|
|
2552
|
+
For an unverified local Android launch, follow the emitted device-specific remedy.
|
|
2553
|
+
Only a dev client gets the Expo development-server picker step; a bare app
|
|
2554
|
+
starts with the printed process restart.
|
|
2531
2555
|
If the app is stuck before loading its first bundle, restart its process with
|
|
2532
2556
|
the printed force-stop and launcher commands. Foregrounding the same process
|
|
2533
2557
|
does not restart initialization. Confirm the bundle request and expected UI.
|
|
@@ -3077,8 +3101,13 @@ with machine or project optimization settings; see \`guide settings\`:
|
|
|
3077
3101
|
ios xcodebuild carries COMPILATION_CACHE_ENABLE_CACHING, a shared
|
|
3078
3102
|
COMPILATION_CACHE_CAS_PATH and a clang prefix mapping of this
|
|
3079
3103
|
workspace's root, so compiled output crosses worktrees with no
|
|
3080
|
-
Podfile post_install block.
|
|
3081
|
-
|
|
3104
|
+
Podfile post_install block. Swift joins in, with its own prefix
|
|
3105
|
+
mapping, once xcrun swift reports 6.4 or newer (Xcode 27) and
|
|
3106
|
+
react-native is 0.87 or newer; the cache line names Swift on or
|
|
3107
|
+
off and why. Xcode still excludes Swift targets built without
|
|
3108
|
+
explicit modules; the post-build cache line counts those.
|
|
3109
|
+
Xcode 26+ only, and skipped entirely when the project
|
|
3110
|
+
configured ccache (the two defeat each other).
|
|
3082
3111
|
android gradlew carries --build-cache, so task outputs cross worktrees with
|
|
3083
3112
|
no org.gradle.caching=true in gradle.properties. Debug builds also
|
|
3084
3113
|
carry -PreactNativeArchitectures=<target ABI>, using the owned
|
|
@@ -3606,9 +3635,8 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
3606
3635
|
The build, the fingerprint, the build cache and the Metro port gate are
|
|
3607
3636
|
unchanged. What is skipped is everything that manages an owned device:
|
|
3608
3637
|
no capacity check, no AVD creation, no boot wait, and no owned-device
|
|
3609
|
-
registry entry.
|
|
3610
|
-
|
|
3611
|
-
10.0.2.2. Stim never creates, boots, shuts down, or deletes hardware.
|
|
3638
|
+
registry entry. A debug app uses localhost:<port> through adb reverse.
|
|
3639
|
+
Stim never creates, boots, shuts down, or deletes hardware.
|
|
3612
3640
|
|
|
3613
3641
|
\`ios --device [udid]\` selects a connected iPhone, the same way
|
|
3614
3642
|
\`android --device\` selects a connected phone: with no UDID it takes the
|
|
@@ -3817,7 +3845,7 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
|
|
|
3817
3845
|
workspace's bundle with the project's own tools (\`expo export:embed\` /
|
|
3818
3846
|
\`react-native bundle\`, then the project's own hermesc when
|
|
3819
3847
|
\`hermesEnabled\` is not false in android/gradle.properties), re-packs it
|
|
3820
|
-
into the copy with
|
|
3848
|
+
into the copy with the JDK's jar tool (stored, not deflated -- the runtime
|
|
3821
3849
|
mmaps it), then zipaligns and re-signs with apksigner. The keystore defaults
|
|
3822
3850
|
to android/app/debug.keystore with the standard password; android.keystore /
|
|
3823
3851
|
android.keystorePassword override it (see \`guide settings\`). The cache
|
|
@@ -4542,9 +4570,10 @@ be setup steps are supplied by Stim on the command lines it composes itself:
|
|
|
4542
4570
|
|
|
4543
4571
|
xcodebuild COMPILATION_CACHE_ENABLE_CACHING / COMPILATION_CACHE_CAS_PATH /
|
|
4544
4572
|
SWIFT_ENABLE_COMPILE_CACHE / CLANG_ENABLE_PREFIX_MAPPING /
|
|
4545
|
-
CLANG_OTHER_PREFIX_MAPPINGS
|
|
4546
|
-
|
|
4547
|
-
|
|
4573
|
+
CLANG_OTHER_PREFIX_MAPPINGS, plus SWIFT_ENABLE_PREFIX_MAPPING /
|
|
4574
|
+
SWIFT_OTHER_PREFIX_MAPPINGS on a Swift 6.4+ toolchain -- so no
|
|
4575
|
+
Podfile post_install block (Xcode 26+ only, and skipped when the
|
|
4576
|
+
project configured ccache, which defeats it)
|
|
4548
4577
|
gradlew --build-cache -- so no org.gradle.caching=true in a committed
|
|
4549
4578
|
gradle.properties. Debug builds add
|
|
4550
4579
|
-PreactNativeArchitectures=<target ABI> when the owned
|
|
@@ -4582,7 +4611,6 @@ key inherits the next layer. Changes apply on the next build or Metro restart.
|
|
|
4582
4611
|
"metroWarmup": true,
|
|
4583
4612
|
"ios": {
|
|
4584
4613
|
"compilationCache": true,
|
|
4585
|
-
"swiftCompilationCache": false,
|
|
4586
4614
|
"prefixMapping": true
|
|
4587
4615
|
},
|
|
4588
4616
|
"android": {
|
|
@@ -4619,7 +4647,18 @@ The example shows the defaults. Full setting names and behavior:
|
|
|
4619
4647
|
optimizations.ios.compilationCache
|
|
4620
4648
|
controls Xcode compilation caching (Xcode 26+).
|
|
4621
4649
|
optimizations.ios.swiftCompilationCache
|
|
4622
|
-
|
|
4650
|
+
unset by default: Swift caching turns on, with Swift prefix mapping, when
|
|
4651
|
+
xcrun swift reports Swift 6.4 or newer (Xcode 27), where swift-frontend no
|
|
4652
|
+
longer crashes on prefix-mapped batches, and the project's react-native is
|
|
4653
|
+
0.87 or newer. Older toolchains leave it off. React Native before 0.87
|
|
4654
|
+
sets SWIFT_ENABLE_EXPLICIT_MODULES=NO on every target for its prebuilt
|
|
4655
|
+
core, and Xcode refuses Swift caching without explicit modules, so those
|
|
4656
|
+
projects stay off too; the cache line names which gate applied.
|
|
4657
|
+
true forces it on any toolchain and React Native version, unmapped below
|
|
4658
|
+
Swift 6.4, so entries only hit within one checkout. false keeps it off.
|
|
4659
|
+
Requires compilationCache=true. A project that still disables explicit
|
|
4660
|
+
modules itself sees the excluded target count on the post-build cache
|
|
4661
|
+
line. Stim does not override that project setting.
|
|
4623
4662
|
optimizations.ios.prefixMapping
|
|
4624
4663
|
controls Clang source/DerivedData prefix mapping. false clears Stim's
|
|
4625
4664
|
mappings. The existing Xcode version and project-ccache guards still apply.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import { S as upsertProject, f as getProject, u as getConfigDir } from "./config-
|
|
1
|
+
import { t as getExecutor } from "./exec-B29XNuhP.mjs";
|
|
2
|
+
import { S as upsertProject, f as getProject, u as getConfigDir } from "./config-B7iyKWkL.mjs";
|
|
3
3
|
import { accessSync, constants, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { delimiter, join, resolve } from "node:path";
|
|
5
|
-
//#region src/stim-installations.ts
|
|
5
|
+
//#region src/diagnostics/stim-installations.ts
|
|
6
6
|
const VERSION_OUTPUT_LIMIT = 4096;
|
|
7
7
|
function parsedVersion(version) {
|
|
8
8
|
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(version);
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import { E as ownership_claim_exports,
|
|
1
|
+
import { t as getExecutor } from "./exec-B29XNuhP.mjs";
|
|
2
|
+
import { B as validateDeviceSlot, E as ownership_claim_exports, F as deviceSlotKey, P as deviceSlotFileKey, S as upsertProject, f as getProject, l as getConcurrencyLimits } from "./config-B7iyKWkL.mjs";
|
|
3
3
|
import { r as createNdjsonWriter } from "./ndjson-BZwI1NBv.mjs";
|
|
4
|
-
import { _ as workspaceLogsDir, h as workspaceDir, o as writeWorkspaceState, r as readWorkspaceState } from "./workspace-state-
|
|
5
|
-
import { T as removeTemporaryEntry, g as repoRoot, s as gitCommonDir, w as makeTemporaryDirectory } from "./worktree-
|
|
6
|
-
import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, r as detectBundleId, t as appProjectProblem } from "./project-
|
|
7
|
-
import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-
|
|
8
|
-
import {
|
|
9
|
-
import { n as verifyCollectorOwnership } from "./ownership-
|
|
10
|
-
import { _ as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { C as resolveRemote, S as resolveEasCliBin, _ as exitAfterFlush, a as POOL_SETTING_REMEDY, f as RESOLVE_TIMEOUT_MS, g as easAuthNote, h as checkEasAuth, l as parkedMaxSetting, m as cacheLevel, p as UPLOAD_TIMEOUT_MS, v as isEasAuthFailureText, w as uploadRemote, y as loadProjectProvider } from "./teardown-
|
|
14
|
-
import { _ as easDeviceBuildRemedy, a as swapJsBundle, c as ensureWorkspaceStorageSafely, d as launchOutcomeRecord, f as noMetroMessage, g as warmMetro, h as sleep, l as gateShouldRetry, m as resolveMetroWithRetry, n as pickDevClientScheme, o as needsPrebuild, p as noMetroRemedy, r as schemesFromInfoPlist, s as runPrebuild, t as devClientScheme, u as isPhysicalDeviceRequest, v as isEasBuildFailure, y as resolveEasDevelopmentBuild } from "./dev-client-
|
|
15
|
-
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-
|
|
16
|
-
import { C as launchIosApp, E as unverifiedLaunchLines, O as verifyLaunch, T as readMetroRecords, b as isBundleProof, c as RELEASE_VERIFY_WAIT_MS, f as clearOtherUserApps, i as LAUNCH_BUNDLING, k as verifyReleaseLaunch, m as devClientUrl, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, v as installIosApp, w as readCollectorRecords, y as iosAppProcess } from "./app-install
|
|
17
|
-
import { f as listIosRuntimes } from "./ios-
|
|
18
|
-
import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-
|
|
19
|
-
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-
|
|
20
|
-
import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-
|
|
21
|
-
import { c as unknownIosRuntimeRefusal, i as ensureOwnedDevice, n as clearIosAdoptionPending, r as ensureBooted, s as unknownIosDeviceTypeRefusal, t as checkDeviceCapacity } from "./device-
|
|
22
|
-
import { a as ensureMetroReachable, b as ownedSessionName, c as remoteIosDeps, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, w as gateMetroOrigin } from "./device-remote-
|
|
23
|
-
import { a as compilationCacheActivityLine, c as readBundleExecutable, l as readBundleId, p as describeDiagnostic, r as buildIos, s as discoverXcodeProject, t as COMPILATION_CACHE_NOT_RUN, u as resolveScheme } from "./xcode-
|
|
24
|
-
import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-
|
|
4
|
+
import { _ as workspaceLogsDir, h as workspaceDir, o as writeWorkspaceState, r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
|
|
5
|
+
import { T as removeTemporaryEntry, g as repoRoot, s as gitCommonDir, w as makeTemporaryDirectory } from "./worktree-FG_ZKKdb.mjs";
|
|
6
|
+
import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, r as detectBundleId, t as appProjectProblem } from "./project-DDZWf_16.mjs";
|
|
7
|
+
import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-DZobzxLW.mjs";
|
|
8
|
+
import { a as resolveProjectMetro, i as pidExists, o as signalProcessTree } from "./metro-C7bcJ0uo.mjs";
|
|
9
|
+
import { n as verifyCollectorOwnership } from "./ownership-Cp52QAK1.mjs";
|
|
10
|
+
import { A as resolveSettings, B as artifactCachePolicy, D as remoteDeviceSettingError, F as tunnelModeSetting, H as optimizationBuildProfile, I as unknownSettingKeys, N as settingShapeErrors, O as remoteIosSetting, R as detectProviders, T as publicUrlSetting, U as resolveOptimizations, _ as iosSigningIdentitySha1SettingError, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, f as iosLanHostSetting, g as iosSigningIdentitySha1Setting, h as iosSigningIdentitySettingError, k as resolveCacheProviderConfig, m as iosSigningIdentitySetting, p as iosLanHostSettingError, r as REMOTE_DEVICE_BACKENDS, x as metroWarmupUrlSetting } from "./settings-BXXorVM2.mjs";
|
|
11
|
+
import { _ as launchErrorPreview, c as launchErrorReport, g as stepTimer, h as stepClock, i as formatDuration, l as phaseLine, m as shortUdid, n as appReadinessMessage, p as shortHash } from "./command-output-B3ycpsYv.mjs";
|
|
12
|
+
import { C as untrackedNativeFiles, S as untrackedMissLine, _ as providerUploadOutcome, b as storeBuild, c as buildCacheKey, d as filesystemBuildCapability, f as fingerprintDiffRecord, g as providerDownloadPath, h as prepareProviderDownloadDir, i as releaseBuildLock, l as describeFingerprintMiss, m as fingerprintProject, n as acquireBuildLock, o as takeoverLine, p as fingerprintDiffSuffix, s as waitForBuild, t as WAIT_CEILING_MS, v as refingerprintAfterMutation, y as resolveBuild } from "./build-lock-CgtsB6TV.mjs";
|
|
13
|
+
import { C as resolveRemote, S as resolveEasCliBin, _ as exitAfterFlush, a as POOL_SETTING_REMEDY, f as RESOLVE_TIMEOUT_MS, g as easAuthNote, h as checkEasAuth, l as parkedMaxSetting, m as cacheLevel, p as UPLOAD_TIMEOUT_MS, v as isEasAuthFailureText, w as uploadRemote, y as loadProjectProvider } from "./teardown-5J5UXz2P.mjs";
|
|
14
|
+
import { _ as easDeviceBuildRemedy, a as swapJsBundle, c as ensureWorkspaceStorageSafely, d as launchOutcomeRecord, f as noMetroMessage, g as warmMetro, h as sleep, l as gateShouldRetry, m as resolveMetroWithRetry, n as pickDevClientScheme, o as needsPrebuild, p as noMetroRemedy, r as schemesFromInfoPlist, s as runPrebuild, t as devClientScheme, u as isPhysicalDeviceRequest, v as isEasBuildFailure, y as resolveEasDevelopmentBuild } from "./dev-client-DDUkWgJL.mjs";
|
|
15
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BYfZz_S-.mjs";
|
|
16
|
+
import { C as launchIosApp, E as unverifiedLaunchLines, O as verifyLaunch, T as readMetroRecords, b as isBundleProof, c as RELEASE_VERIFY_WAIT_MS, f as clearOtherUserApps, i as LAUNCH_BUNDLING, k as verifyReleaseLaunch, m as devClientUrl, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, v as installIosApp, w as readCollectorRecords, y as iosAppProcess } from "./app-install-DJg7bVv-.mjs";
|
|
17
|
+
import { f as listIosRuntimes } from "./ios-lEJ5pKsq.mjs";
|
|
18
|
+
import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-DNSHL5z8.mjs";
|
|
19
|
+
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-C-oAHRT1.mjs";
|
|
20
|
+
import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BYcZ6n1h.mjs";
|
|
21
|
+
import { c as unknownIosRuntimeRefusal, i as ensureOwnedDevice, n as clearIosAdoptionPending, r as ensureBooted, s as unknownIosDeviceTypeRefusal, t as checkDeviceCapacity } from "./device-CLeDp-KH.mjs";
|
|
22
|
+
import { a as ensureMetroReachable, b as ownedSessionName, c as remoteIosDeps, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, w as gateMetroOrigin } from "./device-remote-BprxZp-m.mjs";
|
|
23
|
+
import { a as compilationCacheActivityLine, c as readBundleExecutable, l as readBundleId, p as describeDiagnostic, r as buildIos, s as discoverXcodeProject, t as COMPILATION_CACHE_NOT_RUN, u as resolveScheme } from "./xcode-B6YaWeg0.mjs";
|
|
24
|
+
import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-DbLvyrup.mjs";
|
|
25
25
|
import { t as spawnEntry } from "./spawn-entry-nCE6m770.mjs";
|
|
26
|
-
import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-
|
|
27
|
-
import { a as iosDeviceProcess, c as listIosDevices, d as verifyIosDeviceReleaseLaunch, i as installIosDeviceApp, l as localNetworkPending, n as LAUNCH_PROBE_TIMEOUT_MS, o as iosPoolCandidates, r as awaitIosDeviceLaunch, s as iosPoolNoCandidatesRefusal, t as DEVICECTL_INSTALL_TIMEOUT_MS, u as resolveIosPhysicalDevice } from "./ios-device-
|
|
28
|
-
import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-
|
|
26
|
+
import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-D1Kaf-eT.mjs";
|
|
27
|
+
import { a as iosDeviceProcess, c as listIosDevices, d as verifyIosDeviceReleaseLaunch, i as installIosDeviceApp, l as localNetworkPending, n as LAUNCH_PROBE_TIMEOUT_MS, o as iosPoolCandidates, r as awaitIosDeviceLaunch, s as iosPoolNoCandidatesRefusal, t as DEVICECTL_INSTALL_TIMEOUT_MS, u as resolveIosPhysicalDevice } from "./ios-device-CDbvn5H2.mjs";
|
|
28
|
+
import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-oCXVNEqQ.mjs";
|
|
29
29
|
import { existsSync, mkdirSync, mkdtempSync, openSync, rmSync, writeFileSync } from "node:fs";
|
|
30
30
|
import { InvalidArgumentError } from "commander";
|
|
31
31
|
import { X509Certificate } from "node:crypto";
|
|
@@ -1503,14 +1503,14 @@ function hostLanCandidates(interfaces = networkInterfaces) {
|
|
|
1503
1503
|
//#endregion
|
|
1504
1504
|
//#region src/commands/ios/collector.ts
|
|
1505
1505
|
function collectorLogFile(root, slot) {
|
|
1506
|
-
return join(workspaceLogsDir(root), `collector-${
|
|
1506
|
+
return join(workspaceLogsDir(root), `collector-${deviceSlotFileKey("ios", slot)}.log`);
|
|
1507
1507
|
}
|
|
1508
1508
|
function collectorEntry() {
|
|
1509
1509
|
return spawnEntry("collector-run");
|
|
1510
1510
|
}
|
|
1511
1511
|
const COLLECTOR_EXIT_WAIT_MS = 2e3;
|
|
1512
1512
|
const COLLECTOR_POLL_MS = 25;
|
|
1513
|
-
async function stopPreviousCollector({ root, slot = "default", kill = (pid, signal) =>
|
|
1513
|
+
async function stopPreviousCollector({ root, slot = "default", kill = (pid, signal) => signalProcessTree(pid, signal), alive = pidExists, readState = readWorkspaceState, verify = verifyCollectorOwnership, waitMs = COLLECTOR_EXIT_WAIT_MS, note = (_line) => {} }) {
|
|
1514
1514
|
const previous = (readState(root)?.collectors)?.[deviceSlotKey("ios", slot)] || null;
|
|
1515
1515
|
const previousPid = Number(previous?.pid) || null;
|
|
1516
1516
|
let killed = null;
|
|
@@ -2138,14 +2138,11 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2138
2138
|
event: "launch",
|
|
2139
2139
|
msg: release ? `launched ${bundleId} on ${udid} (${configuration}, embedded JS bundle, no Metro)` : `launched ${bundleId} on ${udid} against Metro ${lanOriginUrl ?? `port ${metroPort}`}` + (launched?.mode === "openurl" || launched?.mode === "payload-url" ? " (expo-dev-client)" : "")
|
|
2140
2140
|
});
|
|
2141
|
-
if (remoteDevice)
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
appName,
|
|
2147
|
-
appExecutable,
|
|
2148
|
-
note
|
|
2141
|
+
if (remoteDevice) logWriter().write({
|
|
2142
|
+
src: "build",
|
|
2143
|
+
level: "info",
|
|
2144
|
+
event: "collector_skipped",
|
|
2145
|
+
msg: `remote session ${udid}: device logs come from agent-device/EAS, so no simctl log stream is attached`
|
|
2149
2146
|
});
|
|
2150
2147
|
if (physical) raiseLeaseFor(release ? RELEASE_VERIFY_WAIT_MS : DEBUG_VERIFY_STEP_MS, false);
|
|
2151
2148
|
const { state: launchState, warning: launchWarning } = await verifyIosRun({
|
|
@@ -2272,7 +2269,7 @@ function explicitSchemeRefusal(root, scheme, isExpo, d) {
|
|
|
2272
2269
|
return d.resolveScheme(project, { scheme }).error ?? null;
|
|
2273
2270
|
}
|
|
2274
2271
|
function iosSlotLogFile(root, slot) {
|
|
2275
|
-
return slot === "default" ? buildLogFile(root) : join(workspaceLogsDir(root), `build-${
|
|
2272
|
+
return slot === "default" ? buildLogFile(root) : join(workspaceLogsDir(root), `build-${deviceSlotFileKey("ios", slot)}.ndjson`);
|
|
2276
2273
|
}
|
|
2277
2274
|
function iosSlotDeps(d, slot) {
|
|
2278
2275
|
if (slot !== "default") {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import { r as INSTALL_ERROR } from "./app-install
|
|
1
|
+
import { t as getExecutor } from "./exec-B29XNuhP.mjs";
|
|
2
|
+
import { r as INSTALL_ERROR } from "./app-install-DJg7bVv-.mjs";
|
|
3
3
|
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-
|
|
1
|
+
import { t as getExecutor } from "./exec-B29XNuhP.mjs";
|
|
2
|
+
import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Dl5uKE03.mjs";
|
|
3
3
|
import { mkdtempSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
@@ -22,7 +22,7 @@ function hostMemoryPressureAdvice(pressure) {
|
|
|
22
22
|
return `macOS reports ${pressure} host memory pressure. Simulator processes can stall even when the device is Booted. Free memory before retrying: use \`stim stop\` only in workspaces you own, and ask before closing other apps or devices. For parallel iOS work, consider a reviewed SimSlim profile (\`stim guide lifecycle simslim\`). This observation does not establish an OOM crash.`;
|
|
23
23
|
}
|
|
24
24
|
//#endregion
|
|
25
|
-
//#region src/
|
|
25
|
+
//#region src/devices/ios.ts
|
|
26
26
|
function parseSimctlList(jsonOutput, { includeUnavailable = false } = {}) {
|
|
27
27
|
const parsed = JSON.parse(jsonOutput);
|
|
28
28
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Expected simctl list to return a JSON object with a devices object.");
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as validateDeviceSlot, g as loadConfig, h as isPathPrefix } from "./config-B7iyKWkL.mjs";
|
|
2
2
|
import { n as SOURCES, t as LEVELS } from "./ndjson-BZwI1NBv.mjs";
|
|
3
|
-
import { _ as workspaceLogsDir, r as readWorkspaceState } from "./workspace-state-
|
|
4
|
-
import { a as findProjectRoot } from "./project-
|
|
5
|
-
import { _ as launchErrorPreview } from "./command-output-
|
|
6
|
-
import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-
|
|
7
|
-
import { a as followLogs, c as queryLogs, i as fileSizes, n as buildCriteria, o as logFiles, r as compileGrep, s as parseSince } from "./logs-query-
|
|
3
|
+
import { _ as workspaceLogsDir, r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
|
|
4
|
+
import { a as findProjectRoot } from "./project-DDZWf_16.mjs";
|
|
5
|
+
import { _ as launchErrorPreview } from "./command-output-B3ycpsYv.mjs";
|
|
6
|
+
import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-D1Kaf-eT.mjs";
|
|
7
|
+
import { a as followLogs, c as queryLogs, i as fileSizes, n as buildCriteria, o as logFiles, r as compileGrep, s as parseSince } from "./logs-query-BGL-Pl12.mjs";
|
|
8
8
|
import { realpathSync } from "node:fs";
|
|
9
9
|
import { relative, sep } from "node:path";
|
|
10
10
|
import chalk from "chalk";
|
|
@@ -2,7 +2,7 @@ import { a as parseNdjsonLine, i as levelRank, o as parseNdjsonText } from "./nd
|
|
|
2
2
|
import { closeSync, openSync, readFileSync, readSync, readdirSync, statSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { StringDecoder } from "string_decoder";
|
|
5
|
-
//#region src/logs-query.ts
|
|
5
|
+
//#region src/diagnostics/logs-query.ts
|
|
6
6
|
const SINCE_UNITS = {
|
|
7
7
|
s: 1e3,
|
|
8
8
|
m: 6e4,
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { t as getExecutor } from "./exec-B29XNuhP.mjs";
|
|
2
|
+
import { A as isOnMountedVolume, g as loadConfig, j as listMountedVolumes, n as allMetroPorts, r as claimMetroPort, v as removeProject, w as process_identity_exports } from "./config-B7iyKWkL.mjs";
|
|
3
|
+
import { r as readWorkspaceState } from "./workspace-state-BT5E3SA2.mjs";
|
|
4
|
+
import { existsSync, readlinkSync, realpathSync } from "fs";
|
|
5
|
+
import { sep } from "path";
|
|
6
|
+
import { request } from "http";
|
|
7
|
+
import { connect } from "net";
|
|
8
|
+
//#region src/ports.ts
|
|
9
|
+
function isMetroRunning(port) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
const req = request({
|
|
12
|
+
hostname: "localhost",
|
|
13
|
+
port,
|
|
14
|
+
path: "/status",
|
|
15
|
+
timeout: 2e3
|
|
16
|
+
}, (res) => {
|
|
17
|
+
let data = "";
|
|
18
|
+
res.on("data", (chunk) => {
|
|
19
|
+
data += chunk;
|
|
20
|
+
});
|
|
21
|
+
res.on("end", () => resolve(data.includes("packager-status:running")));
|
|
22
|
+
});
|
|
23
|
+
req.on("error", () => resolve(false));
|
|
24
|
+
req.on("timeout", () => {
|
|
25
|
+
req.destroy();
|
|
26
|
+
resolve(false);
|
|
27
|
+
});
|
|
28
|
+
req.end();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function isPortFree(port, { timeoutMs = 400 } = {}) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
const sock = connect({
|
|
34
|
+
port,
|
|
35
|
+
host: "127.0.0.1"
|
|
36
|
+
});
|
|
37
|
+
const done = (free) => {
|
|
38
|
+
sock.removeAllListeners();
|
|
39
|
+
sock.destroy();
|
|
40
|
+
resolve(free);
|
|
41
|
+
};
|
|
42
|
+
sock.setTimeout(timeoutMs);
|
|
43
|
+
sock.once("connect", () => done(false));
|
|
44
|
+
sock.once("error", () => done(true));
|
|
45
|
+
sock.once("timeout", () => done(false));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const FIRST_PORT = 8082;
|
|
49
|
+
async function computeNextPort(isFree = isPortFree) {
|
|
50
|
+
const taken = new Set(allMetroPorts());
|
|
51
|
+
for (let port = FIRST_PORT; port < 8282; port++) {
|
|
52
|
+
if (taken.has(port)) continue;
|
|
53
|
+
if (await isFree(port)) return port;
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`Found no free Metro port between ${FIRST_PORT} and 8281. Free one up, or stop a stale bundler (\`stim status\`, \`stim stop <port>\`).`);
|
|
56
|
+
}
|
|
57
|
+
async function findReclaimablePort(excludeProjectPath, probe = isMetroRunning, { isMounted = isOnMountedVolume, mountedVolumes } = {}) {
|
|
58
|
+
const cfg = loadConfig();
|
|
59
|
+
if (!cfg?.projects) return null;
|
|
60
|
+
const mounted = mountedVolumes || listMountedVolumes();
|
|
61
|
+
const candidates = [];
|
|
62
|
+
for (const [path, proj] of Object.entries(cfg.projects)) {
|
|
63
|
+
if (path === excludeProjectPath) continue;
|
|
64
|
+
if (Object.keys(proj.ports ?? {}).length) continue;
|
|
65
|
+
if (existsSync(path)) continue;
|
|
66
|
+
if (!isMounted(path, mounted)) continue;
|
|
67
|
+
if (typeof proj.metroPort === "number") candidates.push({
|
|
68
|
+
port: proj.metroPort,
|
|
69
|
+
ownerPath: path
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
for (const c of candidates) if (!await probe(c.port)) return c;
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
async function allocatePort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
|
|
76
|
+
const reclaim = await findReclaimablePort(projectPath, probe);
|
|
77
|
+
if (reclaim && await isFree(reclaim.port)) {
|
|
78
|
+
removeProject(reclaim.ownerPath);
|
|
79
|
+
return reclaim.port;
|
|
80
|
+
}
|
|
81
|
+
return computeNextPort(isFree);
|
|
82
|
+
}
|
|
83
|
+
const RESERVE_ATTEMPTS = 5;
|
|
84
|
+
async function reserveMetroPort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
|
|
85
|
+
for (let attempt = 0; attempt < RESERVE_ATTEMPTS; attempt++) {
|
|
86
|
+
const port = await allocatePort(projectPath, probe, isFree);
|
|
87
|
+
const claimed = claimMetroPort(projectPath, port);
|
|
88
|
+
if (claimed !== null) return claimed;
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`Could not reserve a Metro port after ${RESERVE_ATTEMPTS} attempts: another Stim run claimed each one first. Retry.`);
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/metro.ts
|
|
94
|
+
/**
|
|
95
|
+
* True when some process with this pid exists right now. It cannot tell a recycled pid from the
|
|
96
|
+
* process that first held it, so it is only valid for a pid this run spawned and still holds a
|
|
97
|
+
* handle on. For a pid read from a record on disk, decide with `inspectProcessIdentity` on that
|
|
98
|
+
* record's process token, or take an ownership claim (`ownership-claim.ts`): this answer would keep
|
|
99
|
+
* a dead holder's record looking live until some unrelated process exits.
|
|
100
|
+
*/
|
|
101
|
+
function pidExists(pid) {
|
|
102
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
103
|
+
try {
|
|
104
|
+
process.kill(pid, 0);
|
|
105
|
+
return true;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return error.code !== "ESRCH";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function parseLsofPids(out) {
|
|
111
|
+
if (!out) return [];
|
|
112
|
+
return String(out).split("\n").map((s) => parseInt(s.trim(), 10)).filter((n) => Number.isFinite(n));
|
|
113
|
+
}
|
|
114
|
+
function addressPort(address) {
|
|
115
|
+
const colon = String(address ?? "").lastIndexOf(":");
|
|
116
|
+
return colon < 0 ? NaN : Number(String(address).slice(colon + 1));
|
|
117
|
+
}
|
|
118
|
+
function parseNetstatPids(out, port) {
|
|
119
|
+
const pids = [];
|
|
120
|
+
for (const line of String(out ?? "").split("\n")) {
|
|
121
|
+
const cols = line.trim().split(/\s+/);
|
|
122
|
+
if (cols.length < 5 || cols[0]?.toUpperCase() !== "TCP") continue;
|
|
123
|
+
if (addressPort(cols[1]) !== port || addressPort(cols[2]) !== 0) continue;
|
|
124
|
+
const pid = Number(cols.at(-1));
|
|
125
|
+
if (Number.isSafeInteger(pid) && pid > 0 && !pids.includes(pid)) pids.push(pid);
|
|
126
|
+
}
|
|
127
|
+
return pids;
|
|
128
|
+
}
|
|
129
|
+
function listeningPids(port, platform = process.platform) {
|
|
130
|
+
const pids = parseLsofPids(getExecutor().runQuiet(`lsof -nP -iTCP:${port} -sTCP:LISTEN -t`));
|
|
131
|
+
if (pids.length > 0 || platform !== "win32") return pids;
|
|
132
|
+
return parseNetstatPids(getExecutor().runQuiet("netstat -ano"), port);
|
|
133
|
+
}
|
|
134
|
+
function parseLsofCwd(out) {
|
|
135
|
+
if (!out) return null;
|
|
136
|
+
const lines = String(out).split("\n");
|
|
137
|
+
const idx = lines.findIndex((l) => l === "fcwd");
|
|
138
|
+
if (idx === -1) return null;
|
|
139
|
+
const nLine = lines.slice(idx + 1).find((l) => l.startsWith("n"));
|
|
140
|
+
return nLine ? nLine.slice(1) : null;
|
|
141
|
+
}
|
|
142
|
+
function processCwd(pid) {
|
|
143
|
+
if (process.platform === "linux") try {
|
|
144
|
+
return readlinkSync(`/proc/${pid}/cwd`);
|
|
145
|
+
} catch {}
|
|
146
|
+
return parseLsofCwd(getExecutor().runQuiet(`lsof -a -p ${pid} -d cwd -Fn`));
|
|
147
|
+
}
|
|
148
|
+
function canonicalPath(path) {
|
|
149
|
+
try {
|
|
150
|
+
return realpathSync(path);
|
|
151
|
+
} catch {
|
|
152
|
+
return path;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function isInsideProject(cwd, projectPath) {
|
|
156
|
+
if (!cwd || !projectPath) return false;
|
|
157
|
+
const a = canonicalPath(cwd);
|
|
158
|
+
const b = canonicalPath(projectPath);
|
|
159
|
+
return a === b || a.startsWith(b.endsWith(sep) ? b : b + sep);
|
|
160
|
+
}
|
|
161
|
+
const NOT_OURS_UNRESPONSIVE = "unresponsive";
|
|
162
|
+
const NOT_OURS_UNREADABLE_CWD = "unreadable-cwd";
|
|
163
|
+
const NOT_OURS_FOREIGN_CWD = "foreign-cwd";
|
|
164
|
+
function ownedSupervisor(projectPath, port) {
|
|
165
|
+
const supervisor = readWorkspaceState(canonicalPath(projectPath))?.supervisor;
|
|
166
|
+
if (supervisor?.port !== port || (0, process_identity_exports.inspectProcessIdentity)(supervisor) !== "same") return null;
|
|
167
|
+
return supervisor;
|
|
168
|
+
}
|
|
169
|
+
async function resolveProjectMetro(port, projectPath, { probe = isMetroRunning, cwdOf = processCwd } = {}) {
|
|
170
|
+
const pid = listeningPids(port)[0];
|
|
171
|
+
if (pid === void 0) return { missing: true };
|
|
172
|
+
if (!await probe(port)) return {
|
|
173
|
+
notOurs: `pid ${pid} on port ${port} does not answer Metro's /status`,
|
|
174
|
+
kind: NOT_OURS_UNRESPONSIVE,
|
|
175
|
+
pid
|
|
176
|
+
};
|
|
177
|
+
const cwd = cwdOf(pid);
|
|
178
|
+
if (!cwd) {
|
|
179
|
+
const owner = ownedSupervisor(projectPath, port);
|
|
180
|
+
if (owner && (pid === owner.pid || pid === owner.serverPid)) return { metro: {
|
|
181
|
+
pid,
|
|
182
|
+
leader: owner.pid,
|
|
183
|
+
cwd: canonicalPath(projectPath),
|
|
184
|
+
processToken: owner.processToken
|
|
185
|
+
} };
|
|
186
|
+
return {
|
|
187
|
+
notOurs: `pid ${pid} on port ${port}: working directory could not be read`,
|
|
188
|
+
kind: NOT_OURS_UNREADABLE_CWD,
|
|
189
|
+
pid
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (!isInsideProject(cwd, projectPath)) return {
|
|
193
|
+
notOurs: `pid ${pid} on port ${port} runs from ${cwd}, outside ${projectPath}`,
|
|
194
|
+
kind: NOT_OURS_FOREIGN_CWD,
|
|
195
|
+
pid
|
|
196
|
+
};
|
|
197
|
+
const owner = ownedSupervisor(projectPath, port);
|
|
198
|
+
return { metro: {
|
|
199
|
+
pid,
|
|
200
|
+
leader: owner ? owner.pid : pid,
|
|
201
|
+
cwd,
|
|
202
|
+
...owner ? { processToken: owner.processToken } : {}
|
|
203
|
+
} };
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Terminate a process and everything it spawned. On POSIX `group` signals the process group, which
|
|
207
|
+
* is what a detached leader owns. Windows has no process groups, and its SIGTERM is
|
|
208
|
+
* TerminateProcess: no handler runs and no child is reached, so a spawned dev server or `adb
|
|
209
|
+
* logcat` would outlive its parent. taskkill /T is the only route to the tree there, and it is
|
|
210
|
+
* always forceful.
|
|
211
|
+
*/
|
|
212
|
+
function signalProcessTree(pid, signal = "SIGTERM", { group = false, platform = process.platform } = {}) {
|
|
213
|
+
if (platform !== "win32") return process.kill(group ? -pid : pid, signal);
|
|
214
|
+
return getExecutor().runFileQuiet("taskkill", [
|
|
215
|
+
"/PID",
|
|
216
|
+
String(pid),
|
|
217
|
+
"/T",
|
|
218
|
+
"/F"
|
|
219
|
+
]) !== null;
|
|
220
|
+
}
|
|
221
|
+
function killMetroTree(leader, processToken) {
|
|
222
|
+
if (!leader || leader === process.pid || (0, process_identity_exports.inspectProcessIdentity)({
|
|
223
|
+
pid: leader,
|
|
224
|
+
processToken
|
|
225
|
+
}) !== "same") return false;
|
|
226
|
+
try {
|
|
227
|
+
return signalProcessTree(leader, "SIGTERM", { group: true });
|
|
228
|
+
} catch {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//#endregion
|
|
233
|
+
export { resolveProjectMetro as a, isPortFree as c, pidExists as i, reserveMetroPort as l, killMetroTree as n, signalProcessTree as o, listeningPids as r, isMetroRunning as s, NOT_OURS_FOREIGN_CWD as t };
|