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,10 +1,11 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import { r as register } from "./cache-manifest-
|
|
3
|
-
import { d as sharedCompilationCache, m as workspaceDerivedData } from "./workspace-state-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { U as resolveOptimizations } from "./settings-
|
|
7
|
-
import {
|
|
1
|
+
import { t as getExecutor } from "./exec-B29XNuhP.mjs";
|
|
2
|
+
import { r as register } from "./cache-manifest-CYJi8-Hx.mjs";
|
|
3
|
+
import { d as sharedCompilationCache, m as workspaceDerivedData } from "./workspace-state-BT5E3SA2.mjs";
|
|
4
|
+
import { l as resolvePackageJson } from "./project-DDZWf_16.mjs";
|
|
5
|
+
import { t as createLineReader } from "./process-output-Dl5uKE03.mjs";
|
|
6
|
+
import { U as resolveOptimizations } from "./settings-BXXorVM2.mjs";
|
|
7
|
+
import { a as formatElapsed, l as phaseLine } from "./command-output-B3ycpsYv.mjs";
|
|
8
|
+
import { t as cleanLine } from "./server-expo-BVAA1OqJ.mjs";
|
|
8
9
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
9
10
|
import { createHash } from "node:crypto";
|
|
10
11
|
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -332,18 +333,73 @@ function readPodfileProperties(root) {
|
|
|
332
333
|
return null;
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
|
-
|
|
336
|
+
const SWIFT_PREFIX_MAPPING_MIN_SWIFT = {
|
|
337
|
+
major: 6,
|
|
338
|
+
minor: 4
|
|
339
|
+
};
|
|
340
|
+
function swiftPrefixMappingSupported(version) {
|
|
341
|
+
if (!version) return false;
|
|
342
|
+
const min = SWIFT_PREFIX_MAPPING_MIN_SWIFT;
|
|
343
|
+
return version.major > min.major || version.major === min.major && version.minor >= min.minor;
|
|
344
|
+
}
|
|
345
|
+
const SWIFT_CACHE_MIN_REACT_NATIVE = {
|
|
346
|
+
major: 0,
|
|
347
|
+
minor: 87
|
|
348
|
+
};
|
|
349
|
+
function reactNativeSupportsSwiftCache(version) {
|
|
350
|
+
if (!version) return false;
|
|
351
|
+
const min = SWIFT_CACHE_MIN_REACT_NATIVE;
|
|
352
|
+
return version.major > min.major || version.major === min.major && version.minor >= min.minor;
|
|
353
|
+
}
|
|
354
|
+
function parseReactNativeVersion(packageJson) {
|
|
355
|
+
if (!packageJson || typeof packageJson !== "object") return null;
|
|
356
|
+
const raw = packageJson.version;
|
|
357
|
+
const m = typeof raw === "string" ? /^(\d+)\.(\d+)\./.exec(raw) : null;
|
|
358
|
+
if (!m || m[1] === void 0 || m[2] === void 0) return null;
|
|
359
|
+
return {
|
|
360
|
+
major: parseInt(m[1], 10),
|
|
361
|
+
minor: parseInt(m[2], 10)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function detectReactNativeVersion(root) {
|
|
365
|
+
const file = resolvePackageJson(root, "react-native");
|
|
366
|
+
if (!file) return null;
|
|
367
|
+
try {
|
|
368
|
+
return parseReactNativeVersion(JSON.parse(readFileSync(file, "utf-8")));
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function compilationCacheSettings({ workspaceRoot, derivedDataPath, casPath, xcodeMajor, swiftVersion = null, reactNativeVersion = null, ccache = false, optimizations = resolveOptimizations({}, {}).ios }) {
|
|
336
374
|
if (xcodeMajor === null || xcodeMajor === void 0) return [];
|
|
337
375
|
if (xcodeMajor < 26) return [];
|
|
338
376
|
if (ccache) return [];
|
|
377
|
+
const swiftMappable = swiftPrefixMappingSupported(swiftVersion);
|
|
378
|
+
const auto = swiftMappable && reactNativeSupportsSwiftCache(reactNativeVersion);
|
|
379
|
+
const swift = optimizations.compilationCache && (optimizations.swiftCompilationCache ?? auto);
|
|
380
|
+
const mappings = optimizations.prefixMapping ? compilationPrefixMappings(workspaceRoot, derivedDataPath) : "";
|
|
339
381
|
return [
|
|
340
382
|
`COMPILATION_CACHE_ENABLE_CACHING=${optimizations.compilationCache ? "YES" : "NO"}`,
|
|
341
383
|
`COMPILATION_CACHE_CAS_PATH=${casPath}`,
|
|
342
|
-
`SWIFT_ENABLE_COMPILE_CACHE=${
|
|
384
|
+
`SWIFT_ENABLE_COMPILE_CACHE=${swift ? "YES" : "NO"}`,
|
|
343
385
|
`CLANG_ENABLE_PREFIX_MAPPING=${optimizations.prefixMapping ? "YES" : "NO"}`,
|
|
344
|
-
`CLANG_OTHER_PREFIX_MAPPINGS=${
|
|
386
|
+
`CLANG_OTHER_PREFIX_MAPPINGS=${mappings}`,
|
|
387
|
+
...swift && swiftMappable && optimizations.prefixMapping ? ["SWIFT_ENABLE_PREFIX_MAPPING=YES", `SWIFT_OTHER_PREFIX_MAPPINGS=${mappings}`] : ["SWIFT_ENABLE_PREFIX_MAPPING=NO"]
|
|
345
388
|
];
|
|
346
389
|
}
|
|
390
|
+
function parseSwiftVersion(output) {
|
|
391
|
+
const m = /\bSwift version (\d+)\.(\d+)/.exec(String(output || ""));
|
|
392
|
+
if (!m || m[1] === void 0 || m[2] === void 0) return null;
|
|
393
|
+
const major = parseInt(m[1], 10);
|
|
394
|
+
const minor = parseInt(m[2], 10);
|
|
395
|
+
return Number.isFinite(major) && Number.isFinite(minor) ? {
|
|
396
|
+
major,
|
|
397
|
+
minor
|
|
398
|
+
} : null;
|
|
399
|
+
}
|
|
400
|
+
function detectSwiftVersion(exec = null) {
|
|
401
|
+
return parseSwiftVersion((exec || getExecutor()).runQuiet("xcrun swift --version", { timeoutMs: 1e4 }));
|
|
402
|
+
}
|
|
347
403
|
function parseXcodeMajor(output) {
|
|
348
404
|
const m = /^Xcode\s+(\d+)/m.exec(String(output || ""));
|
|
349
405
|
if (!m) return null;
|
|
@@ -356,12 +412,19 @@ function detectXcodeMajor(exec = null) {
|
|
|
356
412
|
return parseXcodeMajor((exec || getExecutor()).runQuiet("xcodebuild -version", { timeoutMs: 1e4 }));
|
|
357
413
|
}
|
|
358
414
|
function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, casPath = sharedCompilationCache(), optimizations = resolveOptimizations({}, {}).ios, onNote = (line) => console.error(line) }) {
|
|
415
|
+
const xcodeMajor = detectXcodeMajor(exec);
|
|
416
|
+
const ccache = ccacheEnabled(readPodfileProperties(root));
|
|
417
|
+
const probe = xcodeMajor !== null && xcodeMajor >= 26 && !ccache && optimizations.compilationCache && optimizations.swiftCompilationCache !== false;
|
|
418
|
+
const swiftVersion = probe ? detectSwiftVersion(exec) : null;
|
|
419
|
+
const reactNativeVersion = probe ? detectReactNativeVersion(root) : null;
|
|
359
420
|
const settings = compilationCacheSettings({
|
|
360
421
|
workspaceRoot: root,
|
|
361
422
|
derivedDataPath,
|
|
362
423
|
casPath,
|
|
363
|
-
xcodeMajor
|
|
364
|
-
|
|
424
|
+
xcodeMajor,
|
|
425
|
+
swiftVersion,
|
|
426
|
+
reactNativeVersion,
|
|
427
|
+
ccache,
|
|
365
428
|
optimizations
|
|
366
429
|
});
|
|
367
430
|
if (settings.length > 0 && optimizations.compilationCache) {
|
|
@@ -371,7 +434,8 @@ function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, c
|
|
|
371
434
|
prune: "atomic",
|
|
372
435
|
note: "shared Xcode compilation cache"
|
|
373
436
|
});
|
|
374
|
-
|
|
437
|
+
const swift = settings.includes("SWIFT_ENABLE_COMPILE_CACHE=YES") ? settings.includes("SWIFT_ENABLE_PREFIX_MAPPING=YES") ? "Swift on" : "Swift on, unmapped" : swiftVersion && !swiftPrefixMappingSupported(swiftVersion) ? `Swift off, ${swiftVersion.major}.${swiftVersion.minor} < ${SWIFT_PREFIX_MAPPING_MIN_SWIFT.major}.${SWIFT_PREFIX_MAPPING_MIN_SWIFT.minor}` : swiftVersion && reactNativeVersion && !reactNativeSupportsSwiftCache(reactNativeVersion) ? `Swift off, react-native ${reactNativeVersion.major}.${reactNativeVersion.minor} < ${SWIFT_CACHE_MIN_REACT_NATIVE.major}.${SWIFT_CACHE_MIN_REACT_NATIVE.minor}` : "Swift off";
|
|
438
|
+
onNote(chalk.dim(phaseLine("cache", `compilation cache on (CAS at ${casPath}, ${swift})`)));
|
|
375
439
|
}
|
|
376
440
|
return settings;
|
|
377
441
|
}
|
|
@@ -567,8 +631,12 @@ function parseCompilationCacheActivity(line) {
|
|
|
567
631
|
hitRatePercent: Number(match[3])
|
|
568
632
|
};
|
|
569
633
|
}
|
|
634
|
+
const SWIFT_CACHING_NEEDS_EXPLICIT_MODULES = /warning: swift compiler caching requires explicit module build/;
|
|
570
635
|
function compilationCacheActivityLine(activity) {
|
|
571
|
-
if (activity.status === "reported")
|
|
636
|
+
if (activity.status === "reported") {
|
|
637
|
+
const swift = activity.swiftTargetsWithoutExplicitModules ? `; Swift excluded: ${activity.swiftTargetsWithoutExplicitModules} targets build without explicit modules (SWIFT_ENABLE_EXPLICIT_MODULES=NO)` : "";
|
|
638
|
+
return `${activity.hits}/${activity.cacheableTasks} hits (${activity.hitRatePercent}%)${swift}`;
|
|
639
|
+
}
|
|
572
640
|
if (activity.status === "not-run") return "not run; artifact cache supplied the app";
|
|
573
641
|
return "unavailable; Xcode did not report reliable statistics";
|
|
574
642
|
}
|
|
@@ -648,6 +716,7 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
|
|
|
648
716
|
});
|
|
649
717
|
const transcript = [];
|
|
650
718
|
let compilationCacheActivity = COMPILATION_CACHE_UNAVAILABLE;
|
|
719
|
+
let swiftTargetsWithoutExplicitModules = 0;
|
|
651
720
|
const onLine = (line) => {
|
|
652
721
|
const msg = cleanLine(line);
|
|
653
722
|
transcript.push(msg);
|
|
@@ -657,8 +726,13 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
|
|
|
657
726
|
level: "debug",
|
|
658
727
|
msg
|
|
659
728
|
});
|
|
660
|
-
|
|
661
|
-
|
|
729
|
+
if (SWIFT_CACHING_NEEDS_EXPLICIT_MODULES.test(msg)) swiftTargetsWithoutExplicitModules += 1;
|
|
730
|
+
const parsed = parseCompilationCacheActivity(msg);
|
|
731
|
+
if (parsed) {
|
|
732
|
+
const activity = swiftTargetsWithoutExplicitModules ? {
|
|
733
|
+
...parsed,
|
|
734
|
+
swiftTargetsWithoutExplicitModules
|
|
735
|
+
} : parsed;
|
|
662
736
|
compilationCacheActivity = activity;
|
|
663
737
|
logWriter.write({
|
|
664
738
|
src: "build",
|
|
@@ -667,7 +741,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
|
|
|
667
741
|
event: "compilation_cache",
|
|
668
742
|
hits: activity.hits,
|
|
669
743
|
cacheableTasks: activity.cacheableTasks,
|
|
670
|
-
hitRatePercent: activity.hitRatePercent
|
|
744
|
+
hitRatePercent: activity.hitRatePercent,
|
|
745
|
+
...swiftTargetsWithoutExplicitModules ? { swiftTargetsWithoutExplicitModules } : {}
|
|
671
746
|
});
|
|
672
747
|
}
|
|
673
748
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stim",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Stim: fast, isolated React Native environments for coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -42,20 +42,24 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@expo/fingerprint": "^0.20.10",
|
|
45
|
-
"@stim-cli/cache": "^1.
|
|
46
|
-
"@stim-cli/core": "^1.
|
|
47
|
-
"@stim-cli/metro": "^1.
|
|
48
|
-
"chalk": "^
|
|
49
|
-
"commander": "^
|
|
45
|
+
"@stim-cli/cache": "^1.7.0",
|
|
46
|
+
"@stim-cli/core": "^1.7.0",
|
|
47
|
+
"@stim-cli/metro": "^1.7.0",
|
|
48
|
+
"chalk": "^6.0.0",
|
|
49
|
+
"commander": "^15.0.0",
|
|
50
|
+
"cross-spawn": "^7.0.6",
|
|
51
|
+
"which": "^5.0.0",
|
|
50
52
|
"ws": "^8.21.3"
|
|
51
53
|
},
|
|
52
54
|
"devDependencies": {
|
|
53
|
-
"@stim-cli/expo-build-cache": "1.
|
|
55
|
+
"@stim-cli/expo-build-cache": "1.7.0",
|
|
56
|
+
"@types/cross-spawn": "^6.0.6",
|
|
57
|
+
"@types/which": "^3.0.4",
|
|
54
58
|
"@types/ws": "^8.18.1",
|
|
55
59
|
"unique-pid": "0.1.1"
|
|
56
60
|
},
|
|
57
61
|
"engines": {
|
|
58
|
-
"node": "
|
|
62
|
+
"node": ">=22.12.0"
|
|
59
63
|
},
|
|
60
64
|
"scripts": {
|
|
61
65
|
"test": "node --test test/*.test.js",
|
|
@@ -7,6 +7,12 @@ gradle.beforeProject { project ->
|
|
|
7
7
|
def profile = System.getenv("STIM_ANDROID_NATIVE_PROFILE")
|
|
8
8
|
def staging = android.externalNativeBuild.cmake.buildStagingDirectory ?: new File(project.projectDir, ".cxx")
|
|
9
9
|
android.externalNativeBuild.cmake.buildStagingDirectory = new File(staging, "stim-" + profile)
|
|
10
|
+
// Windows caps a path at 260 characters and the SDK's ninja is not long-path aware, so the
|
|
11
|
+
// staging directory added above has to buy its room back: CMake replaces the mangled absolute
|
|
12
|
+
// source directory in an over-long object path with a hash of it.
|
|
13
|
+
if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
|
|
14
|
+
android.defaultConfig.externalNativeBuild.cmake.arguments.add("-DCMAKE_OBJECT_PATH_MAX=240")
|
|
15
|
+
}
|
|
10
16
|
def configs = [android.defaultConfig] + android.buildTypes.toList() + android.productFlavors.toList()
|
|
11
17
|
def pch = System.getenv("STIM_ANDROID_PCH")
|
|
12
18
|
def ccache = System.getenv("STIM_ANDROID_CCACHE")
|
package/dist/metro-ato6ef3J.mjs
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
-
import { w as process_identity_exports } from "./config-DMP7L7H6.mjs";
|
|
3
|
-
import { r as readWorkspaceState } from "./workspace-state-WVwWw_hB.mjs";
|
|
4
|
-
import { t as isMetroRunning } from "./ports-BZprTJRa.mjs";
|
|
5
|
-
import { readlinkSync, realpathSync } from "fs";
|
|
6
|
-
import { sep } from "path";
|
|
7
|
-
//#region src/metro.ts
|
|
8
|
-
/**
|
|
9
|
-
* True when some process with this pid exists right now. It cannot tell a recycled pid from the
|
|
10
|
-
* process that first held it, so it is only valid for a pid this run spawned and still holds a
|
|
11
|
-
* handle on. For a pid read from a record on disk, decide with `inspectProcessIdentity` on that
|
|
12
|
-
* record's process token, or take an ownership claim (`ownership-claim.ts`): this answer would keep
|
|
13
|
-
* a dead holder's record looking live until some unrelated process exits.
|
|
14
|
-
*/
|
|
15
|
-
function pidExists(pid) {
|
|
16
|
-
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
17
|
-
try {
|
|
18
|
-
process.kill(pid, 0);
|
|
19
|
-
return true;
|
|
20
|
-
} catch (error) {
|
|
21
|
-
return error.code !== "ESRCH";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function parseLsofPids(out) {
|
|
25
|
-
if (!out) return [];
|
|
26
|
-
return String(out).split("\n").map((s) => parseInt(s.trim(), 10)).filter((n) => Number.isFinite(n));
|
|
27
|
-
}
|
|
28
|
-
function parseLsofCwd(out) {
|
|
29
|
-
if (!out) return null;
|
|
30
|
-
const lines = String(out).split("\n");
|
|
31
|
-
const idx = lines.findIndex((l) => l === "fcwd");
|
|
32
|
-
if (idx === -1) return null;
|
|
33
|
-
const nLine = lines.slice(idx + 1).find((l) => l.startsWith("n"));
|
|
34
|
-
return nLine ? nLine.slice(1) : null;
|
|
35
|
-
}
|
|
36
|
-
function processCwd(pid) {
|
|
37
|
-
if (process.platform === "linux") try {
|
|
38
|
-
return readlinkSync(`/proc/${pid}/cwd`);
|
|
39
|
-
} catch {}
|
|
40
|
-
return parseLsofCwd(getExecutor().runQuiet(`lsof -a -p ${pid} -d cwd -Fn`));
|
|
41
|
-
}
|
|
42
|
-
function canonicalPath(path) {
|
|
43
|
-
try {
|
|
44
|
-
return realpathSync(path);
|
|
45
|
-
} catch {
|
|
46
|
-
return path;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function isInsideProject(cwd, projectPath) {
|
|
50
|
-
if (!cwd || !projectPath) return false;
|
|
51
|
-
const a = canonicalPath(cwd);
|
|
52
|
-
const b = canonicalPath(projectPath);
|
|
53
|
-
return a === b || a.startsWith(b.endsWith(sep) ? b : b + sep);
|
|
54
|
-
}
|
|
55
|
-
const NOT_OURS_UNRESPONSIVE = "unresponsive";
|
|
56
|
-
const NOT_OURS_UNREADABLE_CWD = "unreadable-cwd";
|
|
57
|
-
const NOT_OURS_FOREIGN_CWD = "foreign-cwd";
|
|
58
|
-
async function resolveProjectMetro(port, projectPath, { probe = isMetroRunning } = {}) {
|
|
59
|
-
const pid = parseLsofPids(getExecutor().runQuiet(`lsof -nP -iTCP:${port} -sTCP:LISTEN -t`))[0];
|
|
60
|
-
if (pid === void 0) return { missing: true };
|
|
61
|
-
if (!await probe(port)) return {
|
|
62
|
-
notOurs: `pid ${pid} on port ${port} does not answer Metro's /status`,
|
|
63
|
-
kind: NOT_OURS_UNRESPONSIVE,
|
|
64
|
-
pid
|
|
65
|
-
};
|
|
66
|
-
const cwd = processCwd(pid);
|
|
67
|
-
if (!cwd) return {
|
|
68
|
-
notOurs: `pid ${pid} on port ${port}: working directory could not be read`,
|
|
69
|
-
kind: NOT_OURS_UNREADABLE_CWD,
|
|
70
|
-
pid
|
|
71
|
-
};
|
|
72
|
-
if (!isInsideProject(cwd, projectPath)) return {
|
|
73
|
-
notOurs: `pid ${pid} on port ${port} runs from ${cwd}, outside ${projectPath}`,
|
|
74
|
-
kind: NOT_OURS_FOREIGN_CWD,
|
|
75
|
-
pid
|
|
76
|
-
};
|
|
77
|
-
const supervisor = readWorkspaceState(canonicalPath(projectPath))?.supervisor;
|
|
78
|
-
const owned = supervisor?.port === port && (0, process_identity_exports.inspectProcessIdentity)(supervisor) === "same";
|
|
79
|
-
return { metro: {
|
|
80
|
-
pid,
|
|
81
|
-
leader: owned ? supervisor.pid : pid,
|
|
82
|
-
cwd,
|
|
83
|
-
...owned ? { processToken: supervisor.processToken } : {}
|
|
84
|
-
} };
|
|
85
|
-
}
|
|
86
|
-
function killMetroTree(leader, processToken) {
|
|
87
|
-
if (!leader || leader === process.pid || (0, process_identity_exports.inspectProcessIdentity)({
|
|
88
|
-
pid: leader,
|
|
89
|
-
processToken
|
|
90
|
-
}) !== "same") return false;
|
|
91
|
-
try {
|
|
92
|
-
process.kill(-leader, "SIGTERM");
|
|
93
|
-
return true;
|
|
94
|
-
} catch {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
//#endregion
|
|
99
|
-
export { resolveProjectMetro as i, killMetroTree as n, pidExists as r, NOT_OURS_FOREIGN_CWD as t };
|
package/dist/ports-BZprTJRa.mjs
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { A as isOnMountedVolume, g as loadConfig, j as listMountedVolumes, n as allMetroPorts, r as claimMetroPort, v as removeProject } from "./config-DMP7L7H6.mjs";
|
|
2
|
-
import { existsSync } from "fs";
|
|
3
|
-
import { request } from "http";
|
|
4
|
-
import { connect } from "net";
|
|
5
|
-
//#region src/ports.ts
|
|
6
|
-
function isMetroRunning(port) {
|
|
7
|
-
return new Promise((resolve) => {
|
|
8
|
-
const req = request({
|
|
9
|
-
hostname: "localhost",
|
|
10
|
-
port,
|
|
11
|
-
path: "/status",
|
|
12
|
-
timeout: 2e3
|
|
13
|
-
}, (res) => {
|
|
14
|
-
let data = "";
|
|
15
|
-
res.on("data", (chunk) => {
|
|
16
|
-
data += chunk;
|
|
17
|
-
});
|
|
18
|
-
res.on("end", () => resolve(data.includes("packager-status:running")));
|
|
19
|
-
});
|
|
20
|
-
req.on("error", () => resolve(false));
|
|
21
|
-
req.on("timeout", () => {
|
|
22
|
-
req.destroy();
|
|
23
|
-
resolve(false);
|
|
24
|
-
});
|
|
25
|
-
req.end();
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
function isPortFree(port, { timeoutMs = 400 } = {}) {
|
|
29
|
-
return new Promise((resolve) => {
|
|
30
|
-
const sock = connect({
|
|
31
|
-
port,
|
|
32
|
-
host: "127.0.0.1"
|
|
33
|
-
});
|
|
34
|
-
const done = (free) => {
|
|
35
|
-
sock.removeAllListeners();
|
|
36
|
-
sock.destroy();
|
|
37
|
-
resolve(free);
|
|
38
|
-
};
|
|
39
|
-
sock.setTimeout(timeoutMs);
|
|
40
|
-
sock.once("connect", () => done(false));
|
|
41
|
-
sock.once("error", () => done(true));
|
|
42
|
-
sock.once("timeout", () => done(false));
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
const FIRST_PORT = 8082;
|
|
46
|
-
async function computeNextPort(isFree = isPortFree) {
|
|
47
|
-
const taken = new Set(allMetroPorts());
|
|
48
|
-
for (let port = FIRST_PORT; port < 8282; port++) {
|
|
49
|
-
if (taken.has(port)) continue;
|
|
50
|
-
if (await isFree(port)) return port;
|
|
51
|
-
}
|
|
52
|
-
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>\`).`);
|
|
53
|
-
}
|
|
54
|
-
async function findReclaimablePort(excludeProjectPath, probe = isMetroRunning, { isMounted = isOnMountedVolume, mountedVolumes } = {}) {
|
|
55
|
-
const cfg = loadConfig();
|
|
56
|
-
if (!cfg?.projects) return null;
|
|
57
|
-
const mounted = mountedVolumes || listMountedVolumes();
|
|
58
|
-
const candidates = [];
|
|
59
|
-
for (const [path, proj] of Object.entries(cfg.projects)) {
|
|
60
|
-
if (path === excludeProjectPath) continue;
|
|
61
|
-
if (Object.keys(proj.ports ?? {}).length) continue;
|
|
62
|
-
if (existsSync(path)) continue;
|
|
63
|
-
if (!isMounted(path, mounted)) continue;
|
|
64
|
-
if (typeof proj.metroPort === "number") candidates.push({
|
|
65
|
-
port: proj.metroPort,
|
|
66
|
-
ownerPath: path
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
for (const c of candidates) if (!await probe(c.port)) return c;
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
async function allocatePort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
|
|
73
|
-
const reclaim = await findReclaimablePort(projectPath, probe);
|
|
74
|
-
if (reclaim && await isFree(reclaim.port)) {
|
|
75
|
-
removeProject(reclaim.ownerPath);
|
|
76
|
-
return reclaim.port;
|
|
77
|
-
}
|
|
78
|
-
return computeNextPort(isFree);
|
|
79
|
-
}
|
|
80
|
-
const RESERVE_ATTEMPTS = 5;
|
|
81
|
-
async function reserveMetroPort(projectPath, probe = isMetroRunning, isFree = isPortFree) {
|
|
82
|
-
for (let attempt = 0; attempt < RESERVE_ATTEMPTS; attempt++) {
|
|
83
|
-
const port = await allocatePort(projectPath, probe, isFree);
|
|
84
|
-
const claimed = claimMetroPort(projectPath, port);
|
|
85
|
-
if (claimed !== null) return claimed;
|
|
86
|
-
}
|
|
87
|
-
throw new Error(`Could not reserve a Metro port after ${RESERVE_ATTEMPTS} attempts: another Stim run claimed each one first. Retry.`);
|
|
88
|
-
}
|
|
89
|
-
//#endregion
|
|
90
|
-
export { isPortFree as n, reserveMetroPort as r, isMetroRunning as t };
|