stim 1.17.2 → 1.18.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 +50 -1
- package/dist/{activity-IRvJY_ru.mjs → activity-BxnkDZDi.mjs} +4 -4
- package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-CZ66YA4T.mjs} +8 -8
- package/dist/android-BKA16DTf.mjs +4 -0
- package/dist/{android-B9yaNKwa.mjs → android-BvwuMUJ9.mjs} +192 -98
- package/dist/{android-DE4v59Mb.mjs → android-CBwCNicO.mjs} +2 -2
- package/dist/{android-B_YNZy61.mjs → android-DC3ZLuw0.mjs} +9 -9
- package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-BBFyr9CA.mjs} +5 -5
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/api-D0VtFhqk.mjs +152 -0
- package/dist/api-run.d.mts +1 -0
- package/dist/api-run.mjs +145 -0
- package/dist/api.d.mts +212 -0
- package/dist/api.mjs +2 -0
- package/dist/{app-install-VPkI5WEg.mjs → app-install-D-PUENug.mjs} +3 -3
- package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
- package/dist/{budget-DfcgFdQB.mjs → budget-DIhL2BKx.mjs} +1578 -58
- package/dist/build-facts-DcLv2HSd.d.mts +111 -0
- package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-BI7ozfmH.mjs} +3 -3
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BrcVWQyD.mjs → chrome-DJnmnS5q.mjs} +1 -1
- package/dist/cli-DNJMwADx.mjs +68 -0
- package/dist/cli.mjs +1 -1
- package/dist/{client-SvBdgObD.mjs → client-DzhapoMB.mjs} +139 -32
- package/dist/collector-run.mjs +9 -9
- package/dist/{command-output-CDAIFD-8.mjs → command-output-CBtzyIgF.mjs} +2 -2
- package/dist/{config-D0DbpZtM.mjs → config-CgYMahz7.mjs} +5 -5
- package/dist/{created-devices-CpvzBL25.mjs → created-devices-DKbeWsiK.mjs} +2 -2
- package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-xKoOypBm.mjs} +2 -2
- package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-BLfcgc4O.mjs} +1 -1
- package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CLnIH02x.mjs} +2 -2
- package/dist/{device-C8IOoJPv.mjs → device-D7oCHGWi.mjs} +8 -8
- package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-EUOOYr8T.mjs} +13 -13
- package/dist/device-host-worker.mjs +19 -19
- package/dist/{device-ios-C2Um-XUR.mjs → device-ios-6MewKKnc.mjs} +13 -13
- package/dist/{device-lease-DpoS36_u.mjs → device-lease-Co-EwifR.mjs} +9 -9
- package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-w1xhCqIW.mjs} +4 -4
- package/dist/{device-pool-3o7s5lo4.mjs → device-pool-F5WvRPtD.mjs} +3 -3
- package/dist/{device-remote-hFSkpfQu.mjs → device-remote-tHbd1fKy.mjs} +15 -15
- package/dist/{doctor-CFv1fCrF.mjs → doctor-B9ckYUoU.mjs} +33 -30
- package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics-BRXOqDzG.mjs} +11 -11
- package/dist/{exec-CaErVv7t.mjs → exec-DWhCZDGm.mjs} +65 -2
- package/dist/{gc-DEmkg3Xh.mjs → gc-Dh6mXOKC.mjs} +23 -469
- package/dist/{gradle-dJpre3Yn.mjs → gradle-C6GKnunQ.mjs} +6 -6
- package/dist/{guide-status-Du-ZQ3FD.mjs → guide-status-Bhf9SJXc.mjs} +3 -3
- package/dist/{guide-CSmhMTf8.mjs → guide-tm6igAaa.mjs} +290 -53
- package/dist/{hosted-android-E1Heoqnv.mjs → hosted-android-Bv285t_t.mjs} +3 -3
- package/dist/{hosted-client-Bp4knSwu.mjs → hosted-client-C-rMtppF.mjs} +2 -2
- package/dist/{hosted-ios-BnbeYOUD.mjs → hosted-ios-D5mNmtj4.mjs} +1 -1
- package/dist/{hosted-logs-OcgIBvCT.mjs → hosted-logs-DN8Iz4mJ.mjs} +9 -9
- package/dist/{hosted-macos-DcxKjtfH.mjs → hosted-macos-B-wEjlwG.mjs} +5 -5
- package/dist/{hosted-native-1Ebbig4w.mjs → hosted-native-DeeOG8xZ.mjs} +6 -6
- package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-jjtxAn5C.mjs} +7 -8
- package/dist/{ios-DSfnPfse.mjs → ios-B3SbOWdw.mjs} +19 -7
- package/dist/{ios-Cr1h5gnO.mjs → ios-Bvbf0SGY.mjs} +1 -1
- package/dist/{ios-BJ4XUSe1.mjs → ios-CPdkoKaa.mjs} +181 -95
- package/dist/ios-DNpdygiv.mjs +7 -0
- package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-Bn7jWDB1.mjs} +1 -1
- package/dist/{ios-device-pAN4t54-.mjs → ios-device-vOaByMhw.mjs} +3 -3
- package/dist/{ios-state-8L3lgum6.mjs → ios-state-B2pES6Vs.mjs} +1 -1
- package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-hskNcWkZ.mjs} +5 -5
- package/dist/{logs-BAaS7gS2.mjs → logs-1Ylc1Ri6.mjs} +13 -13
- package/dist/macos-Ck7OBMNY.mjs +2 -0
- package/dist/{macos-BIRQS90f.mjs → macos-DF6C7wUU.mjs} +118 -34
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +349 -47
- package/dist/{metro-g8NqiJp-.mjs → metro-Dh8T4Ytd.mjs} +5 -5
- package/dist/{metro-gateway-C-ijGI2F.mjs → metro-gateway-CEUw9RMH.mjs} +4 -4
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
- package/dist/{named-ports-C8m8EQs2.mjs → named-ports-BFIz1Shz.mjs} +61 -6
- package/dist/{native-run-D1Q56rAD.mjs → native-run-B06FkmZx.mjs} +4 -4
- package/dist/{native-runtime-BEK171xQ.mjs → native-runtime-D_603JiC.mjs} +6 -6
- package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
- package/dist/offload-worker.d.mts +3 -17
- package/dist/offload-worker.mjs +10 -11
- package/dist/{ownership-DyRQru0q.mjs → ownership-D6Se7gg_.mjs} +3 -3
- package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
- package/dist/placement-log-BcPhRwR4.mjs +84 -0
- package/dist/{build-plan-BDsSi09V.mjs → plan-placement-CtxkCO54.mjs} +122 -48
- package/dist/{ports-4nYqxPWC.mjs → ports-BLTtfBq-.mjs} +3 -3
- package/dist/{prebuild-D7RUMkwN.mjs → prebuild-juCyreaR.mjs} +5 -5
- package/dist/preview-D5V07Nrl.mjs +987 -0
- package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
- package/dist/{project-D47Rh0ly.mjs → project-C3ChRLZy.mjs} +4 -68
- package/dist/projects-DD7dBTCj.mjs +92 -0
- package/dist/{pull-request-pxnDCQzn.mjs → pull-request-u4vp7zmK.mjs} +1 -1
- package/dist/pull-requests.mjs +1 -1
- package/dist/{recordings-BNgsD1Tr.mjs → recordings-BAWltsVt.mjs} +1 -1
- package/dist/{reload-DcRxj3qG.mjs → reload-DvyWzVjJ.mjs} +15 -15
- package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-BGg7aGIZ.mjs} +5 -5
- package/dist/{run-CkE9y0nc.mjs → run-vhtDgpHO.mjs} +5 -5
- package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-B9E7S1aR.mjs} +4 -5
- package/dist/server-expo-Db9HBUoa.mjs +2 -0
- package/dist/{server-expo-CHTyXD0G.mjs → server-expo-DoBHhuzK.mjs} +5 -6
- package/dist/{settings-SR9OmW9h.mjs → settings-Byp2AQ_E.mjs} +6 -5
- package/dist/{settings-Cfd-0r2d.mjs → settings-CQyMElEP.mjs} +24 -763
- package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
- package/dist/settings.schema.json +126 -3
- package/dist/{simslim-BWNrc3M3.mjs → simslim-EHBQ2R4G.mjs} +7 -7
- package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-C1nqh4mu.mjs} +6 -6
- package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
- package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
- package/dist/{stage-CzaiwFsI.mjs → stage-g_WqWeaE.mjs} +14 -3
- package/dist/{start-DUz3mYdb.mjs → start-D-IeOyoK.mjs} +19 -19
- package/dist/{start-AlfCPRVU.mjs → start-NTc1Ifqs.mjs} +1 -1
- package/dist/{state-PeQiJuIu.mjs → state-BG-aAk4k.mjs} +4 -4
- package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
- package/dist/{state-DyaNTgfR.mjs → state-xnqpx3AD.mjs} +2 -2
- package/dist/{stats-Bq4fJOdB.mjs → stats-DY6ekz8F.mjs} +5 -5
- package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
- package/dist/{status-BA2pehwf.mjs → status-DCjZeE-K.mjs} +33 -34
- package/dist/{status-CzS4bPO9.mjs → status-Dcr0Qexd.mjs} +5 -5
- package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-Behws_fi.mjs} +1 -1
- package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-aR_WsNOA.mjs} +1 -1
- package/dist/{stop-BIiM49EY.mjs → stop-BGhKMOuH.mjs} +5 -5
- package/dist/{stop-DQIasgKp.mjs → stop-BTjs3xbc.mjs} +17 -18
- package/dist/{stop-zF1y04f2.mjs → stop-DIJtD2GL.mjs} +2 -2
- package/dist/supervisor-run.d.mts +24 -22
- package/dist/supervisor-run.mjs +21 -22
- package/dist/{support-CsJNVfOc.mjs → support-BaayeYCf.mjs} +7 -7
- package/dist/{support-WvD__tSV.mjs → support-Bwvbrq5-.mjs} +5 -5
- package/dist/{toolchain-DNyR3NPq.mjs → toolchain-Bnyd_K3A.mjs} +471 -22
- package/dist/{trigger-Bemu8ytw.mjs → trigger-CJMhwXe8.mjs} +5 -5
- package/dist/trigger-DWAq9hkw.mjs +2 -0
- package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-CjB-MP6H.mjs} +7 -7
- package/dist/web-B6_bFDn7.mjs +2 -0
- package/dist/{web-Dl0KSRHf.mjs → web-DTU0uiWj.mjs} +22 -22
- package/dist/web-run.mjs +10 -10
- package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-C59dqG-Z.mjs} +3 -3
- package/dist/{ownership-BMLqyBgJ.mjs → workspaces-xXItD8Dj.mjs} +577 -23
- package/dist/{worktree-j3IcFAI5.mjs → worktree-BJfnU6r5.mjs} +1 -1
- package/dist/{worktree-Be_7NLKy.mjs → worktree-BSYlofkq.mjs} +234 -35
- package/dist/worktree-CsKYexJM.mjs +753 -0
- package/package.json +10 -5
- package/dist/build-progress-D0WkjCPd.mjs +0 -1036
- package/dist/build-slots-41v_d2x9.mjs +0 -142
- package/dist/cli-CYXb29oE.mjs +0 -51
- package/dist/deps-iKw0eD4P.mjs +0 -438
- package/dist/errors-B6W7FkxQ.mjs +0 -15
- package/dist/idle-BXG__1BA.mjs +0 -176
- package/dist/in-use-CvGOUn-x.mjs +0 -130
- package/dist/preview-B-g9d7kV.mjs +0 -318
- package/dist/process-identity-BmRm1qV0.mjs +0 -12
- package/dist/server-expo-CeakhI8P.mjs +0 -2
- package/dist/state-BKa8wNN5.mjs +0 -148
- package/dist/stop-cause-rhmGfwvt.mjs +0 -97
- package/dist/trigger-CXts6qt1.mjs +0 -2
- package/dist/watchman-APYjXSQa.mjs +0 -48
- package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
- package/dist/workspaces-Bcib97-6.mjs +0 -306
|
@@ -1,757 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
|
-
import { chmodSync as chmodSync$1, existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, statSync as statSync$1, utimesSync as utimesSync$1 } from "fs";
|
|
7
|
-
import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
|
|
8
|
-
import { buildCacheRoot } from "@stim-cli/core";
|
|
1
|
+
import { _ as loadConfig$1, f as getConfigPath$1, h as getRepoSettings, m as getProjectSettings } from "./config-CgYMahz7.mjs";
|
|
2
|
+
import { s as gitCommonDir, y as repoRoot } from "./worktree-CsKYexJM.mjs";
|
|
3
|
+
import { isAbsolute } from "node:path";
|
|
4
|
+
import { isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
|
|
9
5
|
import { ANDROID_COMPILER_CACHE_CHOICES, ANDROID_PCH_CHOICES, REMOTE_DEVICE_BACKENDS, SETTINGS, SETTING_GROUPS, TUNNEL_MODES, WEB_VIEWPORTS, acceptsShape, coerceSettingText, expectedShape, isLayeredSetting, settingDefinition, settingValueError } from "@stim-cli/core/state";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
function existingPath(path) {
|
|
13
|
-
const missing = [];
|
|
14
|
-
let current = resolve(path);
|
|
15
|
-
while (!lstatSync(current, { throwIfNoEntry: false })) {
|
|
16
|
-
missing.unshift(basename(current));
|
|
17
|
-
current = dirname(current);
|
|
18
|
-
}
|
|
19
|
-
const existing = realpathSync(current);
|
|
20
|
-
return {
|
|
21
|
-
existing,
|
|
22
|
-
resolved: join(existing, ...missing)
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function filesystemDevice(path) {
|
|
26
|
-
return statSync(existingPath(path).existing).dev;
|
|
27
|
-
}
|
|
28
|
-
function outsideWorkingTree(path) {
|
|
29
|
-
let current = path;
|
|
30
|
-
while (true) {
|
|
31
|
-
if (lstatSync(join(current, ".git"), { throwIfNoEntry: false })) return false;
|
|
32
|
-
const parent = dirname(current);
|
|
33
|
-
if (parent === current) return true;
|
|
34
|
-
current = parent;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function inside(parent, path) {
|
|
38
|
-
const rel = relative(parent, path);
|
|
39
|
-
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
40
|
-
}
|
|
41
|
-
function temporaryRoot(near) {
|
|
42
|
-
const reference = existingPath(near);
|
|
43
|
-
const insideReference = (path) => inside(reference.resolved, path);
|
|
44
|
-
const override = process.env.STIM_TMPDIR ?? loadConfig$1()?.tempDir;
|
|
45
|
-
if (override !== void 0) {
|
|
46
|
-
if (typeof override !== "string" || !isAbsolute(override)) throw new Error("STIM_TMPDIR / tempDir must be an absolute directory path.");
|
|
47
|
-
const { existing, resolved } = existingPath(override);
|
|
48
|
-
if (!statSync(existing).isDirectory() || !outsideWorkingTree(existing)) throw new Error(`Temporary directory ${override} must be outside Git working trees.`);
|
|
49
|
-
if (insideReference(resolved)) throw new Error(`Temporary directory ${override} must not be inside ${near}.`);
|
|
50
|
-
accessSync(existing, constants.W_OK);
|
|
51
|
-
return resolved;
|
|
52
|
-
}
|
|
53
|
-
const { existing } = reference;
|
|
54
|
-
const cache = existingPath(buildCacheRoot()).resolved;
|
|
55
|
-
const device = statSync(existing).dev;
|
|
56
|
-
const candidates = [tmpdir()];
|
|
57
|
-
let current = existing === reference.resolved || !statSync(existing).isDirectory() ? dirname(existing) : existing;
|
|
58
|
-
while (statSync(current).dev === device) {
|
|
59
|
-
candidates.push(current);
|
|
60
|
-
const parent = dirname(current);
|
|
61
|
-
if (parent === current) break;
|
|
62
|
-
current = parent;
|
|
63
|
-
}
|
|
64
|
-
for (const candidate of candidates) try {
|
|
65
|
-
const path = realpathSync(candidate);
|
|
66
|
-
if (statSync(path).dev !== device || insideReference(path) || inside(cache, path) || !outsideWorkingTree(path)) continue;
|
|
67
|
-
accessSync(path, constants.W_OK);
|
|
68
|
-
return path;
|
|
69
|
-
} catch {}
|
|
70
|
-
throw new Error(`No writable temporary directory on the filesystem containing ${near}. Set STIM_TMPDIR or tempDir in ${join(getConfigDir(), "config.json")} to a directory outside Git working trees.`);
|
|
71
|
-
}
|
|
72
|
-
function makeTemporaryDirectory(near, prefix) {
|
|
73
|
-
const root = temporaryRoot(near);
|
|
74
|
-
mkdirSync(root, {
|
|
75
|
-
recursive: true,
|
|
76
|
-
mode: 448
|
|
77
|
-
});
|
|
78
|
-
return mkdtempSync(join(root, prefix));
|
|
79
|
-
}
|
|
80
|
-
function removeTemporaryEntry(path) {
|
|
81
|
-
const stat = lstatSync(path, { throwIfNoEntry: false });
|
|
82
|
-
if (stat?.isDirectory()) {
|
|
83
|
-
chmodSync(path, stat.mode | 448);
|
|
84
|
-
for (const entry of readdirSync(path, { withFileTypes: true })) if (entry.isDirectory()) removeTemporaryEntry(join(path, entry.name));
|
|
85
|
-
}
|
|
86
|
-
rmSync(path, {
|
|
87
|
-
recursive: true,
|
|
88
|
-
force: true
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
//#endregion
|
|
92
|
-
//#region src/workspace/worktree.ts
|
|
93
|
-
const CARRY_SKIP_BASENAMES = /* @__PURE__ */ new Set([
|
|
94
|
-
".DerivedData",
|
|
95
|
-
".DS_Store",
|
|
96
|
-
".idea",
|
|
97
|
-
".jj"
|
|
98
|
-
]);
|
|
99
|
-
function isCarrySkipped(rel) {
|
|
100
|
-
return /(^|\/)android\/build\/generated\/autolinking(\/|$)/.test(rel) || String(rel).split("/").some((seg) => CARRY_SKIP_BASENAMES.has(seg));
|
|
101
|
-
}
|
|
102
|
-
function nativePath(path) {
|
|
103
|
-
return sep$1 === "/" ? path : path.replaceAll("/", sep$1);
|
|
104
|
-
}
|
|
105
|
-
function gitCommonDir(cwd) {
|
|
106
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
107
|
-
"-C",
|
|
108
|
-
cwd,
|
|
109
|
-
"rev-parse",
|
|
110
|
-
"--path-format=absolute",
|
|
111
|
-
"--git-common-dir"
|
|
112
|
-
]);
|
|
113
|
-
return out ? nativePath(out.trim()) : null;
|
|
114
|
-
}
|
|
115
|
-
function repoRoot(cwd) {
|
|
116
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
117
|
-
"-C",
|
|
118
|
-
cwd,
|
|
119
|
-
"rev-parse",
|
|
120
|
-
"--show-toplevel"
|
|
121
|
-
]);
|
|
122
|
-
return out ? nativePath(out.trim()) : null;
|
|
123
|
-
}
|
|
124
|
-
function locallyKnownUpstream(projectRoot) {
|
|
125
|
-
try {
|
|
126
|
-
const name = getExecutor().runFile("git", [
|
|
127
|
-
"-C",
|
|
128
|
-
projectRoot,
|
|
129
|
-
"rev-parse",
|
|
130
|
-
"--abbrev-ref",
|
|
131
|
-
"--symbolic-full-name",
|
|
132
|
-
"@{upstream}"
|
|
133
|
-
], { timeoutMs: 5e3 });
|
|
134
|
-
const counts = getExecutor().runFile("git", [
|
|
135
|
-
"-C",
|
|
136
|
-
projectRoot,
|
|
137
|
-
"rev-list",
|
|
138
|
-
"--left-right",
|
|
139
|
-
"--count",
|
|
140
|
-
"HEAD...@{upstream}"
|
|
141
|
-
], { timeoutMs: 5e3 }).trim().split(/\s+/).map(Number);
|
|
142
|
-
const ahead = counts[0] ?? NaN;
|
|
143
|
-
const behind = counts[1] ?? NaN;
|
|
144
|
-
if (!name || !Number.isInteger(ahead) || !Number.isInteger(behind)) return null;
|
|
145
|
-
return {
|
|
146
|
-
name,
|
|
147
|
-
ahead,
|
|
148
|
-
behind
|
|
149
|
-
};
|
|
150
|
-
} catch {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
function warmWorktreePaths(cwd) {
|
|
155
|
-
const currentRoot = repoRoot(cwd);
|
|
156
|
-
if (!currentRoot) throw new Error("Not a git repository.");
|
|
157
|
-
const target = realpathSync$1(currentRoot);
|
|
158
|
-
const source = resolveSourceCheckout(target);
|
|
159
|
-
if ("refusal" in source) throw new Error(source.refusal);
|
|
160
|
-
if (!source.entries.find((entry) => canonicalPath(entry.path) === target)) throw new Error("Could not identify the linked worktree and its source checkout.");
|
|
161
|
-
const root = realpathSync$1(source.path);
|
|
162
|
-
if (root === target) throw new Error("Run stim worktree warm from a linked worktree, not the source checkout.");
|
|
163
|
-
const sourceRoot = repoRoot(root);
|
|
164
|
-
const sourceCommon = gitCommonDir(root);
|
|
165
|
-
const targetCommon = gitCommonDir(target);
|
|
166
|
-
if (!sourceRoot || realpathSync$1(sourceRoot) !== root || !sourceCommon || !targetCommon || realpathSync$1(sourceCommon) !== realpathSync$1(targetCommon)) throw new Error("Could not verify that the source checkout belongs to this linked worktree.");
|
|
167
|
-
return {
|
|
168
|
-
root,
|
|
169
|
-
target,
|
|
170
|
-
common: realpathSync$1(targetCommon)
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
function matchesInclude(path, patterns) {
|
|
174
|
-
for (const pattern of patterns || []) {
|
|
175
|
-
const rooted = pattern.startsWith("/");
|
|
176
|
-
const escaped = (rooted ? pattern.slice(1) : pattern).replace(/[.+^${}()|[\]\\?]/g, "\\$&").replace(/\*\*\//g, "::GLOBSTAR::").replace(/\*/g, "[^/]+").replace(/::GLOBSTAR::/g, "(?:.*/)?").replace(/\\\?/g, "[^/]");
|
|
177
|
-
if (new RegExp(`${rooted ? "^" : "(^|/)"}${escaped}$`).test(path)) return true;
|
|
178
|
-
}
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
function readWorktreeExclude(root) {
|
|
182
|
-
return readPatternFile(join$1(root, ".worktreeexclude"));
|
|
183
|
-
}
|
|
184
|
-
function readPatternFile(p) {
|
|
185
|
-
if (!existsSync$1(p)) return null;
|
|
186
|
-
return readFileSync$1(p, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
187
|
-
}
|
|
188
|
-
function canonicalPath(path) {
|
|
189
|
-
try {
|
|
190
|
-
return realpathSync$1(path);
|
|
191
|
-
} catch {
|
|
192
|
-
return resolve$1(path);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
function nestedWorktreePaths(root) {
|
|
196
|
-
const source = canonicalPath(root);
|
|
197
|
-
const paths = /* @__PURE__ */ new Set();
|
|
198
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
199
|
-
"-C",
|
|
200
|
-
root,
|
|
201
|
-
"worktree",
|
|
202
|
-
"list",
|
|
203
|
-
"--porcelain"
|
|
204
|
-
]);
|
|
205
|
-
if (out === null) throw new Error("Could not list Git worktrees. Refusing to carry ignored files.");
|
|
206
|
-
for (const entry of parseWorktrees(out)) {
|
|
207
|
-
const rel = relative$1(source, canonicalPath(entry.path));
|
|
208
|
-
if (!rel || rel === ".." || rel.startsWith(`..${sep$1}`) || isAbsolute$1(rel)) continue;
|
|
209
|
-
paths.add(rel.split(sep$1).join("/"));
|
|
210
|
-
}
|
|
211
|
-
return [...paths];
|
|
212
|
-
}
|
|
213
|
-
function overlapsNestedWorktree(rel, nestedPaths) {
|
|
214
|
-
const path = String(rel).replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
215
|
-
return nestedPaths.some((nested) => path === nested || path.startsWith(`${nested}/`) || nested.startsWith(`${path}/`));
|
|
216
|
-
}
|
|
217
|
-
function listTrackedPaths(dir) {
|
|
218
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
219
|
-
"-C",
|
|
220
|
-
dir,
|
|
221
|
-
"ls-files",
|
|
222
|
-
"-z"
|
|
223
|
-
]);
|
|
224
|
-
if (out === null) return null;
|
|
225
|
-
return out.split("\0").filter(Boolean);
|
|
226
|
-
}
|
|
227
|
-
function trackedGuard(dir) {
|
|
228
|
-
const paths = listTrackedPaths(dir);
|
|
229
|
-
if (paths === null) return {
|
|
230
|
-
known: false,
|
|
231
|
-
covers: () => false
|
|
232
|
-
};
|
|
233
|
-
const entries = new Set(paths);
|
|
234
|
-
const covered = new Set(paths);
|
|
235
|
-
for (const p of paths) for (let i = p.indexOf("/"); i !== -1; i = p.indexOf("/", i + 1)) covered.add(p.slice(0, i));
|
|
236
|
-
return {
|
|
237
|
-
known: true,
|
|
238
|
-
covers: (rel) => {
|
|
239
|
-
if (covered.has(rel)) return true;
|
|
240
|
-
for (let i = rel.indexOf("/"); i !== -1; i = rel.indexOf("/", i + 1)) if (entries.has(rel.slice(0, i))) return true;
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
function listGitignoredEntries(root) {
|
|
246
|
-
const args = [
|
|
247
|
-
"-C",
|
|
248
|
-
root,
|
|
249
|
-
"ls-files",
|
|
250
|
-
"--others",
|
|
251
|
-
"--ignored",
|
|
252
|
-
"--exclude-standard",
|
|
253
|
-
"--directory",
|
|
254
|
-
"--no-empty-directory",
|
|
255
|
-
"-z"
|
|
256
|
-
];
|
|
257
|
-
const out = getExecutor().runFile("git", args);
|
|
258
|
-
if (!out) return [];
|
|
259
|
-
return out.split("\0").filter(Boolean).map((e) => e.endsWith("/") ? e.slice(0, -1) : e);
|
|
260
|
-
}
|
|
261
|
-
function listCarryableIgnoredEntries(root, patterns) {
|
|
262
|
-
const nested = nestedWorktreePaths(root);
|
|
263
|
-
return listGitignoredEntries(root).filter((rel) => !isCarrySkipped(rel) && !overlapsNestedWorktree(rel, nested) && !matchesInclude(rel, patterns));
|
|
264
|
-
}
|
|
265
|
-
function missingDestinationReason(target, rel) {
|
|
266
|
-
const parts = rel.split("/");
|
|
267
|
-
for (let i = 1; i < parts.length; i++) {
|
|
268
|
-
const parent = parts.slice(0, i).join("/");
|
|
269
|
-
const stat = lstatSync$1(join$1(target, parent), { throwIfNoEntry: false });
|
|
270
|
-
if (stat?.isSymbolicLink()) return `symlink ancestor: ${parent}`;
|
|
271
|
-
if (stat && !stat.isDirectory()) return `non-directory ancestor: ${parent}`;
|
|
272
|
-
}
|
|
273
|
-
return lstatSync$1(join$1(target, rel), { throwIfNoEntry: false }) ? "exists" : null;
|
|
274
|
-
}
|
|
275
|
-
function removeCopiedExclusions(path, rel) {
|
|
276
|
-
const entries = readdirSync$1(path, { withFileTypes: true });
|
|
277
|
-
const excluded = entries.filter((entry) => isCarrySkipped(`${rel}/${entry.name}`));
|
|
278
|
-
if (excluded.length > 0) {
|
|
279
|
-
const stat = lstatSync$1(path);
|
|
280
|
-
chmodSync$1(path, stat.mode | 448);
|
|
281
|
-
try {
|
|
282
|
-
for (const entry of excluded) removeTemporaryEntry(join$1(path, entry.name));
|
|
283
|
-
} finally {
|
|
284
|
-
utimesSync$1(path, stat.atime, stat.mtime);
|
|
285
|
-
chmodSync$1(path, stat.mode);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
for (const entry of entries) {
|
|
289
|
-
const child = `${rel}/${entry.name}`;
|
|
290
|
-
if (entry.isDirectory() && !isCarrySkipped(child)) removeCopiedExclusions(join$1(path, entry.name), child);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
function cloneIgnoredEntries({ root, target, patterns }) {
|
|
294
|
-
const copied = [];
|
|
295
|
-
const skipped = [];
|
|
296
|
-
const failed = [];
|
|
297
|
-
let cloned = true;
|
|
298
|
-
const guard = trackedGuard(target);
|
|
299
|
-
if (!guard.known) throw new Error("Could not list the destination's tracked files.");
|
|
300
|
-
const nested = nestedWorktreePaths(target);
|
|
301
|
-
for (const rel of listCarryableIgnoredEntries(root, patterns)) {
|
|
302
|
-
const from = join$1(root, rel);
|
|
303
|
-
const to = join$1(target, rel);
|
|
304
|
-
try {
|
|
305
|
-
const reason = guard.covers(rel) ? "tracked" : overlapsNestedWorktree(rel, nested) ? "nested worktree" : missingDestinationReason(target, rel);
|
|
306
|
-
if (reason) {
|
|
307
|
-
skipped.push({
|
|
308
|
-
file: rel,
|
|
309
|
-
reason
|
|
310
|
-
});
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
mkdirSync$1(dirname$1(to), { recursive: true });
|
|
314
|
-
try {
|
|
315
|
-
getExecutor().runFile("cp", [
|
|
316
|
-
"-Rc",
|
|
317
|
-
from,
|
|
318
|
-
to
|
|
319
|
-
]);
|
|
320
|
-
} catch {
|
|
321
|
-
removeTemporaryEntry(to);
|
|
322
|
-
getExecutor().runFile("cp", [
|
|
323
|
-
"-R",
|
|
324
|
-
from,
|
|
325
|
-
to
|
|
326
|
-
]);
|
|
327
|
-
cloned = false;
|
|
328
|
-
}
|
|
329
|
-
if (lstatSync$1(to).isDirectory()) removeCopiedExclusions(to, rel);
|
|
330
|
-
copied.push(rel);
|
|
331
|
-
} catch (e) {
|
|
332
|
-
failed.push({
|
|
333
|
-
file: rel,
|
|
334
|
-
error: String(e?.message || e)
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return {
|
|
339
|
-
copied,
|
|
340
|
-
skipped,
|
|
341
|
-
failed,
|
|
342
|
-
cloned
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
function podsOutOfSync(target, copiedEntries, { read = readFileSync$1 } = {}) {
|
|
346
|
-
const problems = [];
|
|
347
|
-
for (const rel of copiedEntries || []) {
|
|
348
|
-
if (rel !== "Pods" && !rel.endsWith("/Pods")) continue;
|
|
349
|
-
const iosDir = rel === "Pods" ? "" : rel.slice(0, -5);
|
|
350
|
-
const manifest = join$1(target, rel, "Manifest.lock");
|
|
351
|
-
const podfileLock = join$1(target, iosDir, "Podfile.lock");
|
|
352
|
-
if (!existsSync$1(manifest)) continue;
|
|
353
|
-
if (!existsSync$1(podfileLock)) {
|
|
354
|
-
problems.push({
|
|
355
|
-
dir: iosDir || ".",
|
|
356
|
-
reason: "missing"
|
|
357
|
-
});
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
try {
|
|
361
|
-
if (read(manifest, "utf-8") !== read(podfileLock, "utf-8")) problems.push({
|
|
362
|
-
dir: iosDir || ".",
|
|
363
|
-
reason: "mismatch"
|
|
364
|
-
});
|
|
365
|
-
} catch {}
|
|
366
|
-
}
|
|
367
|
-
return problems;
|
|
368
|
-
}
|
|
369
|
-
const LOCKFILE_NAMES = [
|
|
370
|
-
"pnpm-lock.yaml",
|
|
371
|
-
"yarn.lock",
|
|
372
|
-
"bun.lock",
|
|
373
|
-
"bun.lockb",
|
|
374
|
-
"package-lock.json"
|
|
375
|
-
];
|
|
376
|
-
function npmInstallMatches(wanted, installed) {
|
|
377
|
-
if (!wanted.packages || !installed.packages) return null;
|
|
378
|
-
for (const [key, entry] of Object.entries(wanted.packages)) {
|
|
379
|
-
if (!key.includes("node_modules/") || entry.link) continue;
|
|
380
|
-
const present = installed.packages[key];
|
|
381
|
-
if (!present && (entry.optional || entry.devOptional)) continue;
|
|
382
|
-
if (present?.version !== entry.version) return false;
|
|
383
|
-
}
|
|
384
|
-
return true;
|
|
385
|
-
}
|
|
386
|
-
function lastYamlDocument(text) {
|
|
387
|
-
return (text.split(/^---$/m).at(-1) ?? "").trim();
|
|
388
|
-
}
|
|
389
|
-
function yarnClassicInstallMatches(lockfile, integrity) {
|
|
390
|
-
if (!integrity.lockfileEntries) return null;
|
|
391
|
-
const wanted = [...lockfile.matchAll(/^ {2}resolved "([^"]+)"$/gm)].flatMap((m) => m[1] ?? []);
|
|
392
|
-
if (wanted.length === 0) return null;
|
|
393
|
-
const installed = new Set(Object.values(integrity.lockfileEntries));
|
|
394
|
-
return wanted.every((url) => installed.has(url));
|
|
395
|
-
}
|
|
396
|
-
function yarnBerryInstallMatches(lockfile, state) {
|
|
397
|
-
const installed = new Set([...state.matchAll(/^"?([^\s"#][^"]*?)"?:$/gm)].flatMap((m) => m[1]?.replace(/@virtual:[^#]+#/, "@") ?? []));
|
|
398
|
-
let entries = 0;
|
|
399
|
-
for (const entry of lockfile.split(/\n(?=\S)/)) {
|
|
400
|
-
const resolution = /^ {2}resolution: "([^"]+)"$/m.exec(entry)?.[1];
|
|
401
|
-
if (!resolution) continue;
|
|
402
|
-
entries += 1;
|
|
403
|
-
if (installed.has(resolution) || /^ {2}conditions: /m.test(entry)) continue;
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
return entries === 0 ? null : true;
|
|
407
|
-
}
|
|
408
|
-
function installedMatchesLockfile(dir, lockfile, { read = readFileSync$1 } = {}) {
|
|
409
|
-
const text = (rel) => {
|
|
410
|
-
try {
|
|
411
|
-
return read(join$1(dir, rel), "utf-8").replaceAll("\r\n", "\n");
|
|
412
|
-
} catch {
|
|
413
|
-
return null;
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
const wanted = text(lockfile);
|
|
417
|
-
if (wanted === null) return null;
|
|
418
|
-
try {
|
|
419
|
-
if (lockfile === "package-lock.json") {
|
|
420
|
-
const installed = text("node_modules/.package-lock.json");
|
|
421
|
-
return installed === null ? null : npmInstallMatches(JSON.parse(wanted), JSON.parse(installed));
|
|
422
|
-
}
|
|
423
|
-
if (lockfile === "pnpm-lock.yaml") {
|
|
424
|
-
const installed = text("node_modules/.pnpm/lock.yaml");
|
|
425
|
-
return installed === null ? null : lastYamlDocument(wanted) === lastYamlDocument(installed);
|
|
426
|
-
}
|
|
427
|
-
if (lockfile === "yarn.lock") {
|
|
428
|
-
if (/^__metadata:$/m.test(wanted)) {
|
|
429
|
-
const berry = text("node_modules/.yarn-state.yml");
|
|
430
|
-
return berry === null ? null : yarnBerryInstallMatches(wanted, berry);
|
|
431
|
-
}
|
|
432
|
-
const classic = text("node_modules/.yarn-integrity");
|
|
433
|
-
return classic === null ? null : yarnClassicInstallMatches(wanted, JSON.parse(classic));
|
|
434
|
-
}
|
|
435
|
-
} catch {}
|
|
436
|
-
return null;
|
|
437
|
-
}
|
|
438
|
-
function depsOutOfSync(root, target, copiedEntries, { read = readFileSync$1 } = {}) {
|
|
439
|
-
const problems = [];
|
|
440
|
-
for (const rel of copiedEntries || []) {
|
|
441
|
-
if (rel !== "node_modules" && !rel.endsWith("/node_modules")) continue;
|
|
442
|
-
const dir = rel === "node_modules" ? "" : rel.slice(0, -13);
|
|
443
|
-
const name = LOCKFILE_NAMES.find((candidate) => existsSync$1(join$1(target, dir, candidate)));
|
|
444
|
-
if (!name) continue;
|
|
445
|
-
const installed = installedMatchesLockfile(join$1(target, dir), name, { read });
|
|
446
|
-
if (installed === false) {
|
|
447
|
-
problems.push({
|
|
448
|
-
dir: dir || ".",
|
|
449
|
-
lockfile: name,
|
|
450
|
-
reason: "installed"
|
|
451
|
-
});
|
|
452
|
-
continue;
|
|
453
|
-
}
|
|
454
|
-
const source = join$1(root, dir, name);
|
|
455
|
-
if (installed === true || !existsSync$1(source)) continue;
|
|
456
|
-
try {
|
|
457
|
-
if (read(source, "utf-8") !== read(join$1(target, dir, name), "utf-8")) problems.push({
|
|
458
|
-
dir: dir || ".",
|
|
459
|
-
lockfile: name,
|
|
460
|
-
reason: "lockfile"
|
|
461
|
-
});
|
|
462
|
-
} catch {}
|
|
463
|
-
}
|
|
464
|
-
return problems;
|
|
465
|
-
}
|
|
466
|
-
const FINGERPRINT_INPUT_FILES = [
|
|
467
|
-
"app.json",
|
|
468
|
-
"app.config.ts",
|
|
469
|
-
"app.config.js",
|
|
470
|
-
"app.config.mjs",
|
|
471
|
-
"package.json"
|
|
472
|
-
];
|
|
473
|
-
function dirtyFingerprintFiles(root) {
|
|
474
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
475
|
-
"-C",
|
|
476
|
-
root,
|
|
477
|
-
"status",
|
|
478
|
-
"--porcelain",
|
|
479
|
-
"--",
|
|
480
|
-
...FINGERPRINT_INPUT_FILES
|
|
481
|
-
]);
|
|
482
|
-
if (out === null || out.trim() === "") return [];
|
|
483
|
-
return out.split("\n").map((line) => normalizePorcelainLine(line.trimEnd())).filter((line) => line !== "").map((line) => line.slice(3).trim()).filter((path) => path !== "");
|
|
484
|
-
}
|
|
485
|
-
function hasUncommittedWork(dir) {
|
|
486
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
487
|
-
"--no-optional-locks",
|
|
488
|
-
"-C",
|
|
489
|
-
dir,
|
|
490
|
-
"status",
|
|
491
|
-
"--porcelain"
|
|
492
|
-
]);
|
|
493
|
-
if (out === null) return null;
|
|
494
|
-
return out.trim().length > 0;
|
|
495
|
-
}
|
|
496
|
-
function dirtyPaths(dir, { limit = 10 } = {}) {
|
|
497
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
498
|
-
"--no-optional-locks",
|
|
499
|
-
"-C",
|
|
500
|
-
dir,
|
|
501
|
-
"status",
|
|
502
|
-
"--porcelain"
|
|
503
|
-
]);
|
|
504
|
-
if (out === null) return [];
|
|
505
|
-
return out.split("\n").map((l) => normalizePorcelainLine(l.trimEnd())).filter(Boolean).slice(0, limit);
|
|
506
|
-
}
|
|
507
|
-
function normalizePorcelainLine(line) {
|
|
508
|
-
if (line === "" || line[2] === " ") return line;
|
|
509
|
-
return ` ${line}`;
|
|
510
|
-
}
|
|
511
|
-
function restoreFile(dir, file) {
|
|
512
|
-
return getExecutor().runFileQuiet("git", [
|
|
513
|
-
"-C",
|
|
514
|
-
dir,
|
|
515
|
-
"checkout",
|
|
516
|
-
"--",
|
|
517
|
-
file
|
|
518
|
-
]) !== null;
|
|
519
|
-
}
|
|
520
|
-
function isPodInstallChurn(paths) {
|
|
521
|
-
if (!paths || paths.length === 0) return false;
|
|
522
|
-
return paths.every((line) => /(?:^|\/)(?:Podfile\.lock|project\.pbxproj)$/.test(line.slice(3).trim()));
|
|
523
|
-
}
|
|
524
|
-
const SAFE_BRANCH_NAME = /^[A-Za-z0-9@._/-]+$/;
|
|
525
|
-
function unpushedCommits(dir) {
|
|
526
|
-
const exec = getExecutor();
|
|
527
|
-
const branch = exec.runFileQuiet("git", [
|
|
528
|
-
"-C",
|
|
529
|
-
dir,
|
|
530
|
-
"symbolic-ref",
|
|
531
|
-
"--quiet",
|
|
532
|
-
"--short",
|
|
533
|
-
"HEAD"
|
|
534
|
-
]);
|
|
535
|
-
const own = branch === null ? "" : branch.trim();
|
|
536
|
-
const protection = own && SAFE_BRANCH_NAME.test(own) ? [
|
|
537
|
-
"--remotes",
|
|
538
|
-
`--exclude=${own}`,
|
|
539
|
-
"--branches"
|
|
540
|
-
] : ["--remotes"];
|
|
541
|
-
if (!own && exec.runFileQuiet("git", [
|
|
542
|
-
"-C",
|
|
543
|
-
dir,
|
|
544
|
-
"rev-parse",
|
|
545
|
-
"--abbrev-ref",
|
|
546
|
-
"HEAD"
|
|
547
|
-
]) === "HEAD") protection.push("--branches");
|
|
548
|
-
const out = exec.runFileQuiet("git", [
|
|
549
|
-
"-C",
|
|
550
|
-
dir,
|
|
551
|
-
"log",
|
|
552
|
-
"--oneline",
|
|
553
|
-
"HEAD",
|
|
554
|
-
"--not",
|
|
555
|
-
...protection
|
|
556
|
-
]);
|
|
557
|
-
if (out === null) return null;
|
|
558
|
-
return out.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
559
|
-
}
|
|
560
|
-
function hasRemote(dir) {
|
|
561
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
562
|
-
"-C",
|
|
563
|
-
dir,
|
|
564
|
-
"remote"
|
|
565
|
-
]);
|
|
566
|
-
return Boolean(out && out.trim().length > 0);
|
|
567
|
-
}
|
|
568
|
-
function branchExists(cwd, branch) {
|
|
569
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
570
|
-
"-C",
|
|
571
|
-
cwd,
|
|
572
|
-
"rev-parse",
|
|
573
|
-
"--verify",
|
|
574
|
-
"--quiet",
|
|
575
|
-
`refs/heads/${branch}`
|
|
576
|
-
]);
|
|
577
|
-
return Boolean(out);
|
|
578
|
-
}
|
|
579
|
-
function resolveFullRef(cwd, ref) {
|
|
580
|
-
try {
|
|
581
|
-
const out = getExecutor().runFile("git", [
|
|
582
|
-
"-C",
|
|
583
|
-
cwd,
|
|
584
|
-
"rev-parse",
|
|
585
|
-
"--verify",
|
|
586
|
-
"--quiet",
|
|
587
|
-
"--end-of-options",
|
|
588
|
-
`${ref}^{commit}`
|
|
589
|
-
]);
|
|
590
|
-
return out && out.trim() ? out.trim() : null;
|
|
591
|
-
} catch {
|
|
592
|
-
return null;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
function removeWorktree(path, { from, force = false, platform = process.platform }) {
|
|
596
|
-
const args = [
|
|
597
|
-
...platform === "win32" ? ["-c", "core.longpaths=true"] : [],
|
|
598
|
-
"-C",
|
|
599
|
-
from,
|
|
600
|
-
"worktree",
|
|
601
|
-
"remove",
|
|
602
|
-
...force ? ["--force"] : [],
|
|
603
|
-
"--",
|
|
604
|
-
path
|
|
605
|
-
];
|
|
606
|
-
getExecutor().runFile("git", args);
|
|
607
|
-
}
|
|
608
|
-
function deleteBranch(cwd, branch, expectedSha) {
|
|
609
|
-
if (!SAFE_BRANCH_NAME.test(branch) || branch.startsWith("-")) throw new Error(`Refusing branch ${JSON.stringify(branch)}: it is not a safe local branch name.`);
|
|
610
|
-
getExecutor().runFile("git", [
|
|
611
|
-
"-C",
|
|
612
|
-
cwd,
|
|
613
|
-
"update-ref",
|
|
614
|
-
"-d",
|
|
615
|
-
`refs/heads/${branch}`,
|
|
616
|
-
expectedSha
|
|
617
|
-
]);
|
|
618
|
-
}
|
|
619
|
-
function parseWorktrees(out) {
|
|
620
|
-
const entries = [];
|
|
621
|
-
let current = {};
|
|
622
|
-
for (const line of out.split("\n")) if (line.startsWith("worktree ")) {
|
|
623
|
-
if (current.path) entries.push(current);
|
|
624
|
-
current = { path: nativePath(line.slice(9)) };
|
|
625
|
-
} else if (line.startsWith("branch ")) current.branch = line.slice(7).replace("refs/heads/", "");
|
|
626
|
-
else if (line === "prunable" || line.startsWith("prunable ")) current.prunable = true;
|
|
627
|
-
else if (line === "locked" || line.startsWith("locked ")) current.locked = true;
|
|
628
|
-
else if (line === "bare") current.bare = true;
|
|
629
|
-
if (current.path) entries.push(current);
|
|
630
|
-
return entries;
|
|
631
|
-
}
|
|
632
|
-
function hasPopulatedSubmodules(worktree) {
|
|
633
|
-
const modules = getExecutor().runFileQuiet("git", [
|
|
634
|
-
"-C",
|
|
635
|
-
worktree,
|
|
636
|
-
"rev-parse",
|
|
637
|
-
"--git-path",
|
|
638
|
-
"modules"
|
|
639
|
-
]);
|
|
640
|
-
if (modules && existsSync$1(resolve$1(worktree, nativePath(modules)))) return true;
|
|
641
|
-
return (getExecutor().runFileQuiet("git", [
|
|
642
|
-
"-C",
|
|
643
|
-
worktree,
|
|
644
|
-
"ls-files",
|
|
645
|
-
"--stage",
|
|
646
|
-
"-z"
|
|
647
|
-
]) ?? "").split("\0").some((line) => line.startsWith("160000 ") && existsSync$1(join$1(worktree, line.slice(line.indexOf(" ") + 1), ".git")));
|
|
648
|
-
}
|
|
649
|
-
function listWorktrees(cwd) {
|
|
650
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
651
|
-
"-C",
|
|
652
|
-
cwd,
|
|
653
|
-
"worktree",
|
|
654
|
-
"list",
|
|
655
|
-
"--porcelain"
|
|
656
|
-
]);
|
|
657
|
-
return out ? parseWorktrees(out) : [];
|
|
658
|
-
}
|
|
659
|
-
function branchOf(head) {
|
|
660
|
-
return head.startsWith("ref: refs/heads/") ? head.slice(16) : void 0;
|
|
661
|
-
}
|
|
662
|
-
/**
|
|
663
|
-
* The linked worktrees recorded under a git common dir, read from `worktrees/<id>/gitdir` and `HEAD` without running
|
|
664
|
-
* git or touching the worktree directories, sorted by path. The worktree with the branch the common dir's own `HEAD`
|
|
665
|
-
* names (the main checkout's branch, or a bare repository's source checkout) is left out.
|
|
666
|
-
*/
|
|
667
|
-
function linkedWorktreesOnDisk(commonDir) {
|
|
668
|
-
const admin = join$1(commonDir, "worktrees");
|
|
669
|
-
let ids;
|
|
670
|
-
let sourceBranch;
|
|
671
|
-
try {
|
|
672
|
-
ids = readdirSync$1(admin);
|
|
673
|
-
sourceBranch = branchOf(readFileSync$1(join$1(commonDir, "HEAD"), "utf-8").trim());
|
|
674
|
-
} catch {
|
|
675
|
-
return [];
|
|
676
|
-
}
|
|
677
|
-
return ids.flatMap((id) => {
|
|
678
|
-
try {
|
|
679
|
-
const gitdir = readFileSync$1(join$1(admin, id, "gitdir"), "utf-8").trim();
|
|
680
|
-
const branch = branchOf(readFileSync$1(join$1(admin, id, "HEAD"), "utf-8").trim());
|
|
681
|
-
if (branch && branch === sourceBranch) return [];
|
|
682
|
-
const path = dirname$1(resolve$1(admin, id, nativePath(gitdir)));
|
|
683
|
-
return [branch ? {
|
|
684
|
-
path,
|
|
685
|
-
branch
|
|
686
|
-
} : { path }];
|
|
687
|
-
} catch {
|
|
688
|
-
return [];
|
|
689
|
-
}
|
|
690
|
-
}).toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
|
-
* The git common dir of the repository containing `start`, read from `.git` and `commondir` files without running
|
|
694
|
-
* git. Returns null when `start` is missing, unreadable, or not inside a repository.
|
|
695
|
-
*/
|
|
696
|
-
function gitCommonDirOnDisk(start) {
|
|
697
|
-
try {
|
|
698
|
-
for (let dir = realpathSync$1.native(start);; dir = dirname$1(dir)) {
|
|
699
|
-
const dotGit = join$1(dir, ".git");
|
|
700
|
-
if (existsSync$1(dotGit)) {
|
|
701
|
-
if (statSync$1(dotGit).isDirectory()) return realpathSync$1.native(dotGit);
|
|
702
|
-
const gitdir = /^gitdir: (.+)$/m.exec(readFileSync$1(dotGit, "utf-8"))?.[1]?.trim();
|
|
703
|
-
if (!gitdir) return null;
|
|
704
|
-
const linked = resolve$1(dir, gitdir);
|
|
705
|
-
const commondir = join$1(linked, "commondir");
|
|
706
|
-
return realpathSync$1.native(existsSync$1(commondir) ? resolve$1(linked, readFileSync$1(commondir, "utf-8").trim()) : linked);
|
|
707
|
-
}
|
|
708
|
-
if (dirname$1(dir) === dir) return null;
|
|
709
|
-
}
|
|
710
|
-
} catch {
|
|
711
|
-
return null;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
function selectSourceCheckout(entries, bareHead) {
|
|
715
|
-
const first = entries[0];
|
|
716
|
-
if (!first) return { refusal: "Git lists no worktrees for this repository." };
|
|
717
|
-
if (!first.bare) return { path: first.path };
|
|
718
|
-
const bare = first.path;
|
|
719
|
-
const checkouts = entries.filter((entry) => !entry.bare && !entry.prunable);
|
|
720
|
-
const listed = checkouts.map((entry) => `${entry.branch ?? "detached"} (${entry.path})`).join(", ") || "none";
|
|
721
|
-
const pointHead = ` git -C ${bare} symbolic-ref HEAD refs/heads/<branch>`;
|
|
722
|
-
if (!bareHead) return { refusal: `The bare repository at ${bare} has a detached HEAD, so Stim cannot tell which worktree is the source checkout. Point HEAD at the source branch, then retry:\n${pointHead}\nChecked-out branches: ${listed}` };
|
|
723
|
-
const matches = checkouts.filter((entry) => entry.branch === bareHead);
|
|
724
|
-
const [only] = matches;
|
|
725
|
-
if (only && matches.length === 1) return { path: only.path };
|
|
726
|
-
if (!only) {
|
|
727
|
-
const stale = entries.find((entry) => entry.prunable && entry.branch === bareHead);
|
|
728
|
-
const beside = checkouts[0] ? dirname$1(checkouts[0].path) : bare;
|
|
729
|
-
return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, but no worktree has ${bareHead} checked out, so Stim has no source checkout to copy from. ${stale ? `Its worktree at ${stale.path} is missing. Recreate it, then retry:\n git -C ${bare} worktree prune\n git -C ${bare} worktree add ${stale.path} ${bareHead}\n` : `Create one, then retry:\n git -C ${bare} worktree add ${join$1(beside, bareHead)} ${bareHead}\n`}Or point HEAD at a branch that is checked out (${listed}):\n${pointHead}` };
|
|
730
|
-
}
|
|
731
|
-
return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, which is checked out in more than one worktree: ${matches.map((entry) => entry.path).join(", ")}. Remove the extra worktrees, or point HEAD at a branch checked out once, then retry:\n${pointHead}` };
|
|
732
|
-
}
|
|
733
|
-
function bareHeadBranch(bare) {
|
|
734
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
735
|
-
"-C",
|
|
736
|
-
bare,
|
|
737
|
-
"symbolic-ref",
|
|
738
|
-
"--quiet",
|
|
739
|
-
"HEAD"
|
|
740
|
-
])?.trim();
|
|
741
|
-
return out?.startsWith("refs/heads/") ? out.slice(11) : null;
|
|
742
|
-
}
|
|
743
|
-
function sourceCheckoutOf(entries) {
|
|
744
|
-
const first = entries[0];
|
|
745
|
-
return selectSourceCheckout(entries, first?.bare ? bareHeadBranch(first.path) : null);
|
|
746
|
-
}
|
|
747
|
-
function resolveSourceCheckout(cwd) {
|
|
748
|
-
const entries = listWorktrees(cwd);
|
|
749
|
-
return {
|
|
750
|
-
entries,
|
|
751
|
-
...sourceCheckoutOf(entries)
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
//#endregion
|
|
6
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "fs";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
755
8
|
//#region src/optimizations.ts
|
|
756
9
|
function compilerCacheFallbackMessage({ fallback, compilerCache, file = null }) {
|
|
757
10
|
const origin = fallback.fromEnvironment ? "the environment" : file;
|
|
@@ -1094,15 +347,15 @@ function androidAvdConfigSetting(settings, settingsRoot) {
|
|
|
1094
347
|
if ("avdConfigFile" in android) {
|
|
1095
348
|
if (typeof android.avdConfigFile !== "string" || !android.avdConfigFile.trim() || android.avdConfigFile !== android.avdConfigFile.trim() || /[\r\n\0]/.test(android.avdConfigFile) || isAbsolute$1(android.avdConfigFile)) throw new Error("Invalid android.avdConfigFile setting. Expected a relative file path inside the app directory.");
|
|
1096
349
|
try {
|
|
1097
|
-
const root = realpathSync
|
|
350
|
+
const root = realpathSync(settingsRoot);
|
|
1098
351
|
const candidate = resolve$1(root, android.avdConfigFile);
|
|
1099
352
|
if (pathEscapesRoot(root, candidate)) throw new Error("path escapes the settings root");
|
|
1100
|
-
const path = realpathSync
|
|
353
|
+
const path = realpathSync(candidate);
|
|
1101
354
|
if (pathEscapesRoot(root, path)) throw new Error("symlink target escapes the settings root");
|
|
1102
|
-
const stat = statSync
|
|
355
|
+
const stat = statSync(path);
|
|
1103
356
|
if (!stat.isFile()) throw new Error("path is not a regular file");
|
|
1104
357
|
if (stat.size > 65536) throw new Error("file exceeds 64 KiB");
|
|
1105
|
-
const contents = readFileSync
|
|
358
|
+
const contents = readFileSync(path, "utf8");
|
|
1106
359
|
if (Buffer.byteLength(contents) > 65536) throw new Error("file exceeds 64 KiB");
|
|
1107
360
|
entries = parseAndroidAvdConfigIni(contents);
|
|
1108
361
|
} catch (error) {
|
|
@@ -1131,12 +384,12 @@ function iosSimSlimProfileSetting(settings, settingsRoot) {
|
|
|
1131
384
|
const value = settings.ios.simslimProfile;
|
|
1132
385
|
if (typeof value !== "string" || !value.trim() || value !== value.trim() || /[\r\n\0]/.test(value) || isAbsolute$1(value)) throw new Error("Invalid ios.simslimProfile setting. Expected a relative JSON file path inside the app directory.");
|
|
1133
386
|
try {
|
|
1134
|
-
const root = realpathSync
|
|
387
|
+
const root = realpathSync(settingsRoot);
|
|
1135
388
|
const candidate = resolve$1(root, value);
|
|
1136
389
|
if (pathEscapesRoot(root, candidate)) throw new Error("path escapes the settings root");
|
|
1137
|
-
const path = realpathSync
|
|
390
|
+
const path = realpathSync(candidate);
|
|
1138
391
|
if (pathEscapesRoot(root, path)) throw new Error("symlink target escapes the settings root");
|
|
1139
|
-
const stat = statSync
|
|
392
|
+
const stat = statSync(path);
|
|
1140
393
|
if (!stat.isFile()) throw new Error("path is not a regular file");
|
|
1141
394
|
if (stat.size > 65536) throw new Error("file exceeds 64 KiB");
|
|
1142
395
|
return path;
|
|
@@ -1198,9 +451,9 @@ function unknownSettingKeys(settings, prefix = "") {
|
|
|
1198
451
|
function readCommittedSettings(directory) {
|
|
1199
452
|
if (!directory) return {};
|
|
1200
453
|
const p = join$1(directory, ".stim.json");
|
|
1201
|
-
if (!existsSync
|
|
454
|
+
if (!existsSync(p)) return {};
|
|
1202
455
|
try {
|
|
1203
|
-
const parsed = JSON.parse(readFileSync
|
|
456
|
+
const parsed = JSON.parse(readFileSync(p, "utf-8"));
|
|
1204
457
|
return isPlainObject(parsed) ? parsed : {};
|
|
1205
458
|
} catch {
|
|
1206
459
|
return {};
|
|
@@ -1369,6 +622,14 @@ function deviceIdleShutdownMinutesSetting(settings) {
|
|
|
1369
622
|
function deviceReclaimIdleMinutesSetting(settings) {
|
|
1370
623
|
return minutesSetting(settings, "devices", "reclaimIdleMinutes");
|
|
1371
624
|
}
|
|
625
|
+
function projectMaintenancePinned(root) {
|
|
626
|
+
try {
|
|
627
|
+
const value = settingValueAt(settingsForProject(root), "maintenance.keep");
|
|
628
|
+
return value !== void 0 && value !== false;
|
|
629
|
+
} catch {
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
1372
633
|
function projectDeviceReclaimIdleMinutes(root) {
|
|
1373
634
|
return deviceReclaimIdleMinutesSetting(settingsForProject(root));
|
|
1374
635
|
}
|
|
@@ -1437,4 +698,4 @@ function webSettings(settings) {
|
|
|
1437
698
|
};
|
|
1438
699
|
}
|
|
1439
700
|
//#endregion
|
|
1440
|
-
export {
|
|
701
|
+
export { publicUrlSetting as A, settingValueAt as B, metroWarmupUrlSetting as C, projectMaintenancePinned as D, projectDeviceReclaimIdleMinutes as E, resolveSettings as F, PUBLIC_METRO_ENV as G, tunnelModeSetting as H, settingFile as I, resolveTailscaleBinary as J, detectProviders as K, settingOrigin as L, remoteAndroidSetting as M, remoteIosSetting as N, projectMetroSharedCache as O, resolveCacheProviderConfig as P, resolveOptimizations as Q, settingOriginScope as R, metroTunnelSettingError as S, parseIosRemote as T, unknownSettingKeys as U, settingsLayers as V, webSettings as W, compilerCacheFallbackMessage as X, artifactCachePolicy as Y, optimizationBuildProfile as Z, iosSigningIdentitySha1SettingError as _, androidAvdConfigSetting as a, metroIdleStopMinutesSetting as b, androidDataPartitionSizeGbSetting as c, deviceIdleShutdownMinutesSetting as d, iosLanHostSetting as f, iosSigningIdentitySha1Setting as g, iosSigningIdentitySettingError as h, SETTING_SHAPE_REMEDY as i, readCommittedSettings as j, projectOptimizations as k, androidDataPartitionSizeGbSettingError as l, iosSigningIdentitySetting as m, PATH_SETTINGS as n, androidAvdConfigSettingError as o, iosLanHostSettingError as p, planMetroReach as q, SETTINGS_WITH_RESOLVE_TIME_FALLBACK as r, androidDataPartitionSizeBytes as s, ANDROID_AVD_CONFIG_HELP as t, cacheProviderSettingError as u, iosSimSlimProfileSetting as v, ngrokUrlSetting as w, metroPortSetting as x, mergeSettingsLayers as y, settingShapeErrors as z };
|