stim 1.18.0 → 1.19.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/{activity-BxnkDZDi.mjs → activity-BSw0x84R.mjs} +3 -3
- package/dist/{agent-device-usage-output-CZ66YA4T.mjs → agent-device-usage-output-Bjxu_VmX.mjs} +5 -5
- package/dist/{android-BvwuMUJ9.mjs → android-BXc6BmEi.mjs} +507 -347
- package/dist/{android-DC3ZLuw0.mjs → android-BdQC6zs1.mjs} +6 -6
- package/dist/{android-BKA16DTf.mjs → android-BvV1gGdN.mjs} +3 -3
- package/dist/{android-CBwCNicO.mjs → android-DE5g_5gv.mjs} +2 -2
- package/dist/{android-cas-BBFyr9CA.mjs → android-cas-C87geAg-.mjs} +3 -3
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/{api-D0VtFhqk.mjs → api-DIf_s86s.mjs} +5 -7
- package/dist/api-run.mjs +8 -8
- package/dist/api.d.mts +8 -2
- package/dist/api.mjs +1 -1
- package/dist/{app-install-D-PUENug.mjs → app-install-CFI4ptUL.mjs} +33 -13
- package/dist/{web-DTU0uiWj.mjs → browser-web-CDhqQxhv.mjs} +134 -172
- package/dist/{budget-DIhL2BKx.mjs → budget-Dz-kyXLv.mjs} +15 -202
- package/dist/{build-facts-DcLv2HSd.d.mts → build-facts-B87vo-Im.d.mts} +32 -1
- package/dist/{cache-manifest-BI7ozfmH.mjs → cache-manifest-DiwKSyv9.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-DJnmnS5q.mjs → chrome-BTUOo8p2.mjs} +1 -1
- package/dist/{cli-DNJMwADx.mjs → cli-C9Zvgbdc.mjs} +20 -20
- package/dist/cli.mjs +1 -1
- package/dist/{client-DzhapoMB.mjs → client-CuUo8Nvg.mjs} +248 -23
- package/dist/collector-run.d.mts +6 -0
- package/dist/collector-run.mjs +6 -6
- package/dist/{command-output-CBtzyIgF.mjs → command-output-Bt8ygHE7.mjs} +1 -1
- package/dist/{config-CgYMahz7.mjs → config-DC5jEtd1.mjs} +6 -2
- package/dist/{created-devices-DKbeWsiK.mjs → created-devices-D0s26Hx5.mjs} +2 -2
- package/dist/{dependency-state-xKoOypBm.mjs → dependency-state-DKaS1OM9.mjs} +2 -2
- package/dist/{detached-entry-BLfcgc4O.mjs → detached-entry-pB_IPfV4.mjs} +1 -1
- package/dist/{dev-client-CLnIH02x.mjs → dev-client-CDY_zAmD.mjs} +2 -2
- package/dist/{device-D7oCHGWi.mjs → device-BHw0WlHl.mjs} +8 -8
- package/dist/{device-capacity-EUOOYr8T.mjs → device-capacity-BoN1jQkO.mjs} +23 -12
- package/dist/device-host-worker.mjs +14 -14
- package/dist/{gradle-C6GKnunQ.mjs → device-ios-B2ruohE-.mjs} +659 -105
- package/dist/{device-lease-Co-EwifR.mjs → device-lease-Cu7PtQpZ.mjs} +5 -5
- package/dist/{device-lease-run-w1xhCqIW.mjs → device-lease-run-D38SnZtt.mjs} +4 -4
- package/dist/{device-pool-F5WvRPtD.mjs → device-pool-CP4rAsHx.mjs} +3 -3
- package/dist/{device-remote-tHbd1fKy.mjs → device-remote-B5GiUifk.mjs} +15 -16
- package/dist/{doctor-B9ckYUoU.mjs → doctor-PRJH94v6.mjs} +53 -29
- package/dist/doctor-watchman-wZhyTdp4.mjs +259 -0
- package/dist/{error-diagnostics-BRXOqDzG.mjs → error-diagnostics--v5HGOU0.mjs} +10 -10
- package/dist/{exec-DWhCZDGm.mjs → exec-i8GDPwq9.mjs} +32 -8
- package/dist/{gc-Dh6mXOKC.mjs → gc-CyHMcKcn.mjs} +19 -19
- package/dist/{guide-tm6igAaa.mjs → guide-dRaQJJk9.mjs} +409 -472
- package/dist/{hosted-android-Bv285t_t.mjs → hosted-android-Bs7fytj7.mjs} +3 -3
- package/dist/{hosted-client-C-rMtppF.mjs → hosted-client-C5G4h_OJ.mjs} +1 -1
- package/dist/{hosted-ios-D5mNmtj4.mjs → hosted-ios-Bik035eL.mjs} +1 -1
- package/dist/{hosted-logs-DN8Iz4mJ.mjs → hosted-logs-CI9Az9a4.mjs} +7 -7
- package/dist/{hosted-macos-B-wEjlwG.mjs → hosted-macos-DABm3ScO.mjs} +4 -4
- package/dist/{hosted-native-DeeOG8xZ.mjs → hosted-native-DbV2aUVw.mjs} +12 -8
- package/dist/{idle-shutdown-jjtxAn5C.mjs → idle-shutdown-DYiMBn34.mjs} +7 -7
- package/dist/{ios-B3SbOWdw.mjs → ios-BWbHFZVz.mjs} +5 -5
- package/dist/{ios-Bvbf0SGY.mjs → ios-DCrkgXEM.mjs} +1 -1
- package/dist/{ios-DNpdygiv.mjs → ios-DYUr9LGc.mjs} +7 -6
- package/dist/ios-ITYno9rO.mjs +2241 -0
- package/dist/{ios-device-vOaByMhw.mjs → ios-device-CJ_ZOGky.mjs} +3 -3
- package/dist/{ios-device-Bn7jWDB1.mjs → ios-device-CrW8aMb5.mjs} +1 -1
- package/dist/{ios-state-B2pES6Vs.mjs → ios-state-By0XuLTQ.mjs} +1 -1
- package/dist/{launch-verify-hskNcWkZ.mjs → launch-verify-DCveSE6d.mjs} +4 -4
- package/dist/{logs-1Ylc1Ri6.mjs → logs-Bao1HOMy.mjs} +10 -10
- package/dist/{macos-DF6C7wUU.mjs → macos-ciyBdTY5.mjs} +108 -130
- package/dist/macos-nIOATJmR.mjs +2 -0
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +7 -7
- package/dist/manifest-CX0d1IkZ.mjs +148 -0
- package/dist/{metro-Dh8T4Ytd.mjs → metro-DI2AjPF_.mjs} +194 -41
- package/dist/{metro-gateway-CEUw9RMH.mjs → metro-gateway-BxpJLIwh.mjs} +2 -2
- package/dist/{plan-placement-CtxkCO54.mjs → miss-reason-CjQCOKqq.mjs} +180 -296
- package/dist/{named-ports-BFIz1Shz.mjs → named-ports-B4iWH4E-.mjs} +12 -5
- package/dist/{native-runtime-D_603JiC.mjs → native-runtime-BGxsbfny.mjs} +6 -6
- package/dist/offload-worker.d.mts +1 -29
- package/dist/offload-worker.mjs +9 -8
- package/dist/{ownership-D6Se7gg_.mjs → ownership-EB0ySdGK.mjs} +2 -2
- package/dist/{placement-log-BcPhRwR4.mjs → placement-log-Dxe34rJm.mjs} +7 -3
- package/dist/plan-placement-BVY59qa8.mjs +290 -0
- package/dist/{ports-BLTtfBq-.mjs → ports-ByPc0_97.mjs} +3 -3
- package/dist/{prebuild-juCyreaR.mjs → prebuild-BKJ3wJI1.mjs} +4 -4
- package/dist/{preview-D5V07Nrl.mjs → preview-BbSottmO.mjs} +16 -17
- package/dist/project-B5eQ1USs.mjs +612 -0
- package/dist/{pull-request-u4vp7zmK.mjs → pull-request-IHNdMnG8.mjs} +1 -1
- package/dist/pull-requests.mjs +1 -1
- package/dist/react-native-ios-BMh2i25I.mjs +2 -0
- package/dist/react-native-ios-CSpIxexZ.mjs +2083 -0
- package/dist/{recordings-BAWltsVt.mjs → recordings-Bv8McZN4.mjs} +1 -1
- package/dist/{reload-DvyWzVjJ.mjs → reload-rAPdqeRR.mjs} +13 -14
- package/dist/{remote-cache-BGg7aGIZ.mjs → remote-cache-N3hAEaAL.mjs} +4 -4
- package/dist/{run-vhtDgpHO.mjs → run-lAkle1-c.mjs} +2 -2
- package/dist/{server-bare-B9E7S1aR.mjs → server-bare-CkIjnegq.mjs} +3 -3
- package/dist/server-expo-DGsdUGX2.mjs +2 -0
- package/dist/{server-expo-DoBHhuzK.mjs → server-expo-n3tUi3Em.mjs} +3 -3
- package/dist/{settings-Byp2AQ_E.mjs → settings-2am7TSxp.mjs} +5 -6
- package/dist/{settings-CQyMElEP.mjs → settings-CxgngUZk.mjs} +767 -15
- package/dist/settings.schema.json +72 -6
- package/dist/{simslim-EHBQ2R4G.mjs → simslim-D9LkJxIO.mjs} +5 -5
- package/dist/{slot-launch-C1nqh4mu.mjs → slot-launch-HV_KtPIw.mjs} +6 -6
- package/dist/{stage-g_WqWeaE.mjs → stage-BgaZJVZY.mjs} +1 -1
- package/dist/{start-NTc1Ifqs.mjs → start-D8NbxNBb.mjs} +1 -1
- package/dist/{start-D-IeOyoK.mjs → start-DtxthMfI.mjs} +29 -22
- package/dist/{state-BG-aAk4k.mjs → state-D2KtnALG.mjs} +2 -2
- package/dist/{state-xnqpx3AD.mjs → state-Tp4uImDS.mjs} +4 -2
- package/dist/{stats-DY6ekz8F.mjs → stats-8EJ5ivFT.mjs} +6 -6
- package/dist/{status-DCjZeE-K.mjs → status-BOyp0CUl.mjs} +25 -27
- package/dist/{stim-desktop-Behws_fi.mjs → stim-desktop-C65jk_q2.mjs} +1 -1
- package/dist/{stim-installations-aR_WsNOA.mjs → stim-installations-DbmUjsLv.mjs} +1 -1
- package/dist/stop-D0UIpZuh.mjs +72 -0
- package/dist/{stop-DIJtD2GL.mjs → stop-DRipAg-e.mjs} +2 -2
- package/dist/{stop-BTjs3xbc.mjs → stop-zDlknFoz.mjs} +78 -31
- package/dist/supervisor-run.mjs +14 -14
- package/dist/{support-BaayeYCf.mjs → support-CNqYkV0y.mjs} +7 -7
- package/dist/{support-Bwvbrq5-.mjs → support-DsNaFg6D.mjs} +5 -5
- package/dist/swiftpm-macos-aJzPAj1t.mjs +119 -0
- package/dist/{toolchain-Bnyd_K3A.mjs → toolchain-C3xSoyUl.mjs} +27 -270
- package/dist/{trigger-CJMhwXe8.mjs → trigger-CAhmG-4s.mjs} +3 -3
- package/dist/trigger-CoalsMTV.mjs +2 -0
- package/dist/{warm-progress-CjB-MP6H.mjs → warm-progress-pFLB2e6P.mjs} +133 -36
- package/dist/{status-Dcr0Qexd.mjs → watchman-BL1IdOtq.mjs} +75 -7
- package/dist/web-DFpFeZu5.mjs +2 -0
- package/dist/web-DbbrPUn0.mjs +127 -0
- package/dist/web-run.mjs +7 -7
- package/dist/{workspace-state-C59dqG-Z.mjs → workspace-state-CR9X86xJ.mjs} +1 -1
- package/dist/{workspaces-xXItD8Dj.mjs → workspaces-Djb2gfi9.mjs} +102 -18
- package/dist/{worktree-BSYlofkq.mjs → worktree-BL4M1rhB.mjs} +45 -42
- package/dist/{worktree-BJfnU6r5.mjs → worktree-DV-A0LNL.mjs} +1 -1
- package/package.json +5 -5
- package/shim/bundle-response.cjs +2 -1
- package/shim/bundle-response.d.cts +1 -1
- package/shim/expo-metro-config.cjs +1 -1
- package/dist/device-ios-6MewKKnc.mjs +0 -412
- package/dist/guide-status-Bhf9SJXc.mjs +0 -125
- package/dist/ios-CPdkoKaa.mjs +0 -3953
- package/dist/macos-Ck7OBMNY.mjs +0 -2
- package/dist/native-run-B06FkmZx.mjs +0 -158
- package/dist/project-C3ChRLZy.mjs +0 -212
- package/dist/projects-DD7dBTCj.mjs +0 -92
- package/dist/server-expo-Db9HBUoa.mjs +0 -2
- package/dist/stop-BGhKMOuH.mjs +0 -48
- package/dist/trigger-DWAq9hkw.mjs +0 -2
- package/dist/web-B6_bFDn7.mjs +0 -2
- package/dist/worktree-CsKYexJM.mjs +0 -753
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
|
|
2
|
+
import { _ as loadConfig$1, d as getConfigDir, f as getConfigPath$1, h as getRepoSettings, m as getProjectSettings } from "./config-DC5jEtd1.mjs";
|
|
3
|
+
import { accessSync, chmodSync, constants, lstatSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
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";
|
|
5
6
|
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";
|
|
6
|
-
import {
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
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";
|
|
9
|
+
import { buildCacheRoot } from "@stim-cli/core";
|
|
7
10
|
import { createHash } from "node:crypto";
|
|
8
11
|
//#region src/optimizations.ts
|
|
9
12
|
function compilerCacheFallbackMessage({ fallback, compilerCache, file = null }) {
|
|
@@ -102,6 +105,750 @@ function resolveMetroSharedCache(settings) {
|
|
|
102
105
|
return optimizationBoolean(settings, "metroSharedCache");
|
|
103
106
|
}
|
|
104
107
|
//#endregion
|
|
108
|
+
//#region src/temporary.ts
|
|
109
|
+
function existingPath(path) {
|
|
110
|
+
const missing = [];
|
|
111
|
+
let current = resolve(path);
|
|
112
|
+
while (!lstatSync(current, { throwIfNoEntry: false })) {
|
|
113
|
+
missing.unshift(basename(current));
|
|
114
|
+
current = dirname(current);
|
|
115
|
+
}
|
|
116
|
+
const existing = realpathSync(current);
|
|
117
|
+
return {
|
|
118
|
+
existing,
|
|
119
|
+
resolved: join(existing, ...missing)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function filesystemDevice(path) {
|
|
123
|
+
return statSync(existingPath(path).existing).dev;
|
|
124
|
+
}
|
|
125
|
+
function outsideWorkingTree(path) {
|
|
126
|
+
let current = path;
|
|
127
|
+
while (true) {
|
|
128
|
+
if (lstatSync(join(current, ".git"), { throwIfNoEntry: false })) return false;
|
|
129
|
+
const parent = dirname(current);
|
|
130
|
+
if (parent === current) return true;
|
|
131
|
+
current = parent;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function inside(parent, path) {
|
|
135
|
+
const rel = relative(parent, path);
|
|
136
|
+
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
137
|
+
}
|
|
138
|
+
function temporaryRoot(near) {
|
|
139
|
+
const reference = existingPath(near);
|
|
140
|
+
const insideReference = (path) => inside(reference.resolved, path);
|
|
141
|
+
const override = process.env.STIM_TMPDIR ?? loadConfig$1()?.tempDir;
|
|
142
|
+
if (override !== void 0) {
|
|
143
|
+
if (typeof override !== "string" || !isAbsolute(override)) throw new Error("STIM_TMPDIR / tempDir must be an absolute directory path.");
|
|
144
|
+
const { existing, resolved } = existingPath(override);
|
|
145
|
+
if (!statSync(existing).isDirectory() || !outsideWorkingTree(existing)) throw new Error(`Temporary directory ${override} must be outside Git working trees.`);
|
|
146
|
+
if (insideReference(resolved)) throw new Error(`Temporary directory ${override} must not be inside ${near}.`);
|
|
147
|
+
accessSync(existing, constants.W_OK);
|
|
148
|
+
return resolved;
|
|
149
|
+
}
|
|
150
|
+
const { existing } = reference;
|
|
151
|
+
const cache = existingPath(buildCacheRoot()).resolved;
|
|
152
|
+
const device = statSync(existing).dev;
|
|
153
|
+
const candidates = [tmpdir()];
|
|
154
|
+
let current = existing === reference.resolved || !statSync(existing).isDirectory() ? dirname(existing) : existing;
|
|
155
|
+
while (statSync(current).dev === device) {
|
|
156
|
+
candidates.push(current);
|
|
157
|
+
const parent = dirname(current);
|
|
158
|
+
if (parent === current) break;
|
|
159
|
+
current = parent;
|
|
160
|
+
}
|
|
161
|
+
for (const candidate of candidates) try {
|
|
162
|
+
const path = realpathSync(candidate);
|
|
163
|
+
if (statSync(path).dev !== device || insideReference(path) || inside(cache, path) || !outsideWorkingTree(path)) continue;
|
|
164
|
+
accessSync(path, constants.W_OK);
|
|
165
|
+
return path;
|
|
166
|
+
} catch {}
|
|
167
|
+
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.`);
|
|
168
|
+
}
|
|
169
|
+
function makeTemporaryDirectory(near, prefix) {
|
|
170
|
+
const root = temporaryRoot(near);
|
|
171
|
+
mkdirSync(root, {
|
|
172
|
+
recursive: true,
|
|
173
|
+
mode: 448
|
|
174
|
+
});
|
|
175
|
+
return mkdtempSync(join(root, prefix));
|
|
176
|
+
}
|
|
177
|
+
function removeTemporaryEntry(path) {
|
|
178
|
+
const stat = lstatSync(path, { throwIfNoEntry: false });
|
|
179
|
+
if (stat?.isDirectory()) {
|
|
180
|
+
chmodSync(path, stat.mode | 448);
|
|
181
|
+
for (const entry of readdirSync(path, { withFileTypes: true })) if (entry.isDirectory()) removeTemporaryEntry(join(path, entry.name));
|
|
182
|
+
}
|
|
183
|
+
rmSync(path, {
|
|
184
|
+
recursive: true,
|
|
185
|
+
force: true
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/workspace/worktree.ts
|
|
190
|
+
const CARRY_SKIP_BASENAMES = /* @__PURE__ */ new Set([
|
|
191
|
+
".DerivedData",
|
|
192
|
+
".DS_Store",
|
|
193
|
+
".idea",
|
|
194
|
+
".jj"
|
|
195
|
+
]);
|
|
196
|
+
function isCarrySkipped(rel) {
|
|
197
|
+
return /(^|\/)android\/build\/generated\/autolinking(\/|$)/.test(rel) || String(rel).split("/").some((seg) => CARRY_SKIP_BASENAMES.has(seg));
|
|
198
|
+
}
|
|
199
|
+
function nativePath(path) {
|
|
200
|
+
return sep$1 === "/" ? path : path.replaceAll("/", sep$1);
|
|
201
|
+
}
|
|
202
|
+
function gitCommonDir(cwd) {
|
|
203
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
204
|
+
"-C",
|
|
205
|
+
cwd,
|
|
206
|
+
"rev-parse",
|
|
207
|
+
"--path-format=absolute",
|
|
208
|
+
"--git-common-dir"
|
|
209
|
+
]);
|
|
210
|
+
return out ? nativePath(out.trim()) : null;
|
|
211
|
+
}
|
|
212
|
+
function repoRoot(cwd) {
|
|
213
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
214
|
+
"-C",
|
|
215
|
+
cwd,
|
|
216
|
+
"rev-parse",
|
|
217
|
+
"--show-toplevel"
|
|
218
|
+
]);
|
|
219
|
+
return out ? nativePath(out.trim()) : null;
|
|
220
|
+
}
|
|
221
|
+
function locallyKnownUpstream(projectRoot) {
|
|
222
|
+
try {
|
|
223
|
+
const name = getExecutor().runFile("git", [
|
|
224
|
+
"-C",
|
|
225
|
+
projectRoot,
|
|
226
|
+
"rev-parse",
|
|
227
|
+
"--abbrev-ref",
|
|
228
|
+
"--symbolic-full-name",
|
|
229
|
+
"@{upstream}"
|
|
230
|
+
], { timeoutMs: 5e3 });
|
|
231
|
+
const counts = getExecutor().runFile("git", [
|
|
232
|
+
"-C",
|
|
233
|
+
projectRoot,
|
|
234
|
+
"rev-list",
|
|
235
|
+
"--left-right",
|
|
236
|
+
"--count",
|
|
237
|
+
"HEAD...@{upstream}"
|
|
238
|
+
], { timeoutMs: 5e3 }).trim().split(/\s+/).map(Number);
|
|
239
|
+
const ahead = counts[0] ?? NaN;
|
|
240
|
+
const behind = counts[1] ?? NaN;
|
|
241
|
+
if (!name || !Number.isInteger(ahead) || !Number.isInteger(behind)) return null;
|
|
242
|
+
return {
|
|
243
|
+
name,
|
|
244
|
+
ahead,
|
|
245
|
+
behind
|
|
246
|
+
};
|
|
247
|
+
} catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function warmWorktreePaths(cwd) {
|
|
252
|
+
const currentRoot = repoRoot(cwd);
|
|
253
|
+
if (!currentRoot) throw new Error("Not a git repository.");
|
|
254
|
+
const target = realpathSync$1(currentRoot);
|
|
255
|
+
const source = resolveSourceCheckout(target);
|
|
256
|
+
if ("refusal" in source) throw new Error(source.refusal);
|
|
257
|
+
if (!source.entries.find((entry) => canonicalPath(entry.path) === target)) throw new Error("Could not identify the linked worktree and its source checkout.");
|
|
258
|
+
const root = realpathSync$1(source.path);
|
|
259
|
+
if (root === target) throw new Error("Run stim worktree warm from a linked worktree, not the source checkout.");
|
|
260
|
+
const sourceRoot = repoRoot(root);
|
|
261
|
+
const sourceCommon = gitCommonDir(root);
|
|
262
|
+
const targetCommon = gitCommonDir(target);
|
|
263
|
+
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.");
|
|
264
|
+
return {
|
|
265
|
+
root,
|
|
266
|
+
target,
|
|
267
|
+
common: realpathSync$1(targetCommon)
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function matchesInclude(path, patterns) {
|
|
271
|
+
for (const pattern of patterns || []) {
|
|
272
|
+
const rooted = pattern.startsWith("/");
|
|
273
|
+
const escaped = (rooted ? pattern.slice(1) : pattern).replace(/[.+^${}()|[\]\\?]/g, "\\$&").replace(/\*\*\//g, "::GLOBSTAR::").replace(/\*/g, "[^/]+").replace(/::GLOBSTAR::/g, "(?:.*/)?").replace(/\\\?/g, "[^/]");
|
|
274
|
+
if (new RegExp(`${rooted ? "^" : "(^|/)"}${escaped}$`).test(path)) return true;
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
function readWorktreeExclude(root) {
|
|
279
|
+
return readPatternFile(join$1(root, ".worktreeexclude"));
|
|
280
|
+
}
|
|
281
|
+
function readPatternFile(p) {
|
|
282
|
+
if (!existsSync$1(p)) return null;
|
|
283
|
+
return readFileSync$1(p, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
284
|
+
}
|
|
285
|
+
function canonicalPath(path) {
|
|
286
|
+
try {
|
|
287
|
+
return realpathSync$1(path);
|
|
288
|
+
} catch {
|
|
289
|
+
return resolve$1(path);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function nestedWorktreePaths(root) {
|
|
293
|
+
const source = canonicalPath(root);
|
|
294
|
+
const paths = /* @__PURE__ */ new Set();
|
|
295
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
296
|
+
"-C",
|
|
297
|
+
root,
|
|
298
|
+
"worktree",
|
|
299
|
+
"list",
|
|
300
|
+
"--porcelain"
|
|
301
|
+
]);
|
|
302
|
+
if (out === null) throw new Error("Could not list Git worktrees. Refusing to carry ignored files.");
|
|
303
|
+
for (const entry of parseWorktrees(out)) {
|
|
304
|
+
const rel = relative$1(source, canonicalPath(entry.path));
|
|
305
|
+
if (!rel || rel === ".." || rel.startsWith(`..${sep$1}`) || isAbsolute$1(rel)) continue;
|
|
306
|
+
paths.add(rel.split(sep$1).join("/"));
|
|
307
|
+
}
|
|
308
|
+
return [...paths];
|
|
309
|
+
}
|
|
310
|
+
function overlapsNestedWorktree(rel, nestedPaths) {
|
|
311
|
+
const path = String(rel).replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
312
|
+
return nestedPaths.some((nested) => path === nested || path.startsWith(`${nested}/`) || nested.startsWith(`${path}/`));
|
|
313
|
+
}
|
|
314
|
+
function listTrackedPaths(dir) {
|
|
315
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
316
|
+
"-C",
|
|
317
|
+
dir,
|
|
318
|
+
"ls-files",
|
|
319
|
+
"-z"
|
|
320
|
+
]);
|
|
321
|
+
if (out === null) return null;
|
|
322
|
+
return out.split("\0").filter(Boolean);
|
|
323
|
+
}
|
|
324
|
+
function trackedGuard(dir) {
|
|
325
|
+
const paths = listTrackedPaths(dir);
|
|
326
|
+
if (paths === null) return {
|
|
327
|
+
known: false,
|
|
328
|
+
covers: () => false
|
|
329
|
+
};
|
|
330
|
+
const entries = new Set(paths);
|
|
331
|
+
const covered = new Set(paths);
|
|
332
|
+
for (const p of paths) for (let i = p.indexOf("/"); i !== -1; i = p.indexOf("/", i + 1)) covered.add(p.slice(0, i));
|
|
333
|
+
return {
|
|
334
|
+
known: true,
|
|
335
|
+
covers: (rel) => {
|
|
336
|
+
if (covered.has(rel)) return true;
|
|
337
|
+
for (let i = rel.indexOf("/"); i !== -1; i = rel.indexOf("/", i + 1)) if (entries.has(rel.slice(0, i))) return true;
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function listGitignoredEntries(root) {
|
|
343
|
+
const args = [
|
|
344
|
+
"-C",
|
|
345
|
+
root,
|
|
346
|
+
"ls-files",
|
|
347
|
+
"--others",
|
|
348
|
+
"--ignored",
|
|
349
|
+
"--exclude-standard",
|
|
350
|
+
"--directory",
|
|
351
|
+
"--no-empty-directory",
|
|
352
|
+
"-z"
|
|
353
|
+
];
|
|
354
|
+
const out = getExecutor().runFile("git", args);
|
|
355
|
+
if (!out) return [];
|
|
356
|
+
return out.split("\0").filter(Boolean).map((e) => e.endsWith("/") ? e.slice(0, -1) : e);
|
|
357
|
+
}
|
|
358
|
+
function listCarryableIgnoredEntries(root, patterns) {
|
|
359
|
+
const nested = nestedWorktreePaths(root);
|
|
360
|
+
return listGitignoredEntries(root).filter((rel) => !isCarrySkipped(rel) && !overlapsNestedWorktree(rel, nested) && !matchesInclude(rel, patterns));
|
|
361
|
+
}
|
|
362
|
+
function missingDestinationReason(target, rel) {
|
|
363
|
+
const parts = rel.split("/");
|
|
364
|
+
for (let i = 1; i < parts.length; i++) {
|
|
365
|
+
const parent = parts.slice(0, i).join("/");
|
|
366
|
+
const stat = lstatSync$1(join$1(target, parent), { throwIfNoEntry: false });
|
|
367
|
+
if (stat?.isSymbolicLink()) return `symlink ancestor: ${parent}`;
|
|
368
|
+
if (stat && !stat.isDirectory()) return `non-directory ancestor: ${parent}`;
|
|
369
|
+
}
|
|
370
|
+
return lstatSync$1(join$1(target, rel), { throwIfNoEntry: false }) ? "exists" : null;
|
|
371
|
+
}
|
|
372
|
+
function removeCopiedExclusions(path, rel) {
|
|
373
|
+
const entries = readdirSync$1(path, { withFileTypes: true });
|
|
374
|
+
const excluded = entries.filter((entry) => isCarrySkipped(`${rel}/${entry.name}`));
|
|
375
|
+
if (excluded.length > 0) {
|
|
376
|
+
const stat = lstatSync$1(path);
|
|
377
|
+
chmodSync$1(path, stat.mode | 448);
|
|
378
|
+
try {
|
|
379
|
+
for (const entry of excluded) removeTemporaryEntry(join$1(path, entry.name));
|
|
380
|
+
} finally {
|
|
381
|
+
utimesSync$1(path, stat.atime, stat.mtime);
|
|
382
|
+
chmodSync$1(path, stat.mode);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
for (const entry of entries) {
|
|
386
|
+
const child = `${rel}/${entry.name}`;
|
|
387
|
+
if (entry.isDirectory() && !isCarrySkipped(child)) removeCopiedExclusions(join$1(path, entry.name), child);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function cloneIgnoredEntries({ root, target, patterns }) {
|
|
391
|
+
const copied = [];
|
|
392
|
+
const skipped = [];
|
|
393
|
+
const failed = [];
|
|
394
|
+
let cloned = true;
|
|
395
|
+
const guard = trackedGuard(target);
|
|
396
|
+
if (!guard.known) throw new Error("Could not list the destination's tracked files.");
|
|
397
|
+
const nested = nestedWorktreePaths(target);
|
|
398
|
+
for (const rel of listCarryableIgnoredEntries(root, patterns)) {
|
|
399
|
+
const from = join$1(root, rel);
|
|
400
|
+
const to = join$1(target, rel);
|
|
401
|
+
try {
|
|
402
|
+
const reason = guard.covers(rel) ? "tracked" : overlapsNestedWorktree(rel, nested) ? "nested worktree" : missingDestinationReason(target, rel);
|
|
403
|
+
if (reason) {
|
|
404
|
+
skipped.push({
|
|
405
|
+
file: rel,
|
|
406
|
+
reason
|
|
407
|
+
});
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
mkdirSync$1(dirname$1(to), { recursive: true });
|
|
411
|
+
try {
|
|
412
|
+
getExecutor().runFile("cp", [
|
|
413
|
+
"-Rc",
|
|
414
|
+
from,
|
|
415
|
+
to
|
|
416
|
+
]);
|
|
417
|
+
} catch {
|
|
418
|
+
removeTemporaryEntry(to);
|
|
419
|
+
getExecutor().runFile("cp", [
|
|
420
|
+
"-R",
|
|
421
|
+
from,
|
|
422
|
+
to
|
|
423
|
+
]);
|
|
424
|
+
cloned = false;
|
|
425
|
+
}
|
|
426
|
+
if (lstatSync$1(to).isDirectory()) removeCopiedExclusions(to, rel);
|
|
427
|
+
copied.push(rel);
|
|
428
|
+
} catch (e) {
|
|
429
|
+
failed.push({
|
|
430
|
+
file: rel,
|
|
431
|
+
error: String(e?.message || e)
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
copied,
|
|
437
|
+
skipped,
|
|
438
|
+
failed,
|
|
439
|
+
cloned
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function podsOutOfSync(target, copiedEntries, { read = readFileSync$1 } = {}) {
|
|
443
|
+
const problems = [];
|
|
444
|
+
for (const rel of copiedEntries || []) {
|
|
445
|
+
if (rel !== "Pods" && !rel.endsWith("/Pods")) continue;
|
|
446
|
+
const iosDir = rel === "Pods" ? "" : rel.slice(0, -5);
|
|
447
|
+
const manifest = join$1(target, rel, "Manifest.lock");
|
|
448
|
+
const podfileLock = join$1(target, iosDir, "Podfile.lock");
|
|
449
|
+
if (!existsSync$1(manifest)) continue;
|
|
450
|
+
if (!existsSync$1(podfileLock)) {
|
|
451
|
+
problems.push({
|
|
452
|
+
dir: iosDir || ".",
|
|
453
|
+
reason: "missing"
|
|
454
|
+
});
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
if (read(manifest, "utf-8") !== read(podfileLock, "utf-8")) problems.push({
|
|
459
|
+
dir: iosDir || ".",
|
|
460
|
+
reason: "mismatch"
|
|
461
|
+
});
|
|
462
|
+
} catch {}
|
|
463
|
+
}
|
|
464
|
+
return problems;
|
|
465
|
+
}
|
|
466
|
+
const LOCKFILE_NAMES = [
|
|
467
|
+
"pnpm-lock.yaml",
|
|
468
|
+
"yarn.lock",
|
|
469
|
+
"bun.lock",
|
|
470
|
+
"bun.lockb",
|
|
471
|
+
"package-lock.json"
|
|
472
|
+
];
|
|
473
|
+
function npmInstallMatches(wanted, installed) {
|
|
474
|
+
if (!wanted.packages || !installed.packages) return null;
|
|
475
|
+
for (const [key, entry] of Object.entries(wanted.packages)) {
|
|
476
|
+
if (!key.includes("node_modules/") || entry.link) continue;
|
|
477
|
+
const present = installed.packages[key];
|
|
478
|
+
if (!present && (entry.optional || entry.devOptional)) continue;
|
|
479
|
+
if (present?.version !== entry.version) return false;
|
|
480
|
+
}
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
function lastYamlDocument(text) {
|
|
484
|
+
return (text.split(/^---$/m).at(-1) ?? "").trim();
|
|
485
|
+
}
|
|
486
|
+
function yarnClassicInstallMatches(lockfile, integrity) {
|
|
487
|
+
if (!integrity.lockfileEntries) return null;
|
|
488
|
+
const wanted = [...lockfile.matchAll(/^ {2}resolved "([^"]+)"$/gm)].flatMap((m) => m[1] ?? []);
|
|
489
|
+
if (wanted.length === 0) return null;
|
|
490
|
+
const installed = new Set(Object.values(integrity.lockfileEntries));
|
|
491
|
+
return wanted.every((url) => installed.has(url));
|
|
492
|
+
}
|
|
493
|
+
function yarnBerryInstallMatches(lockfile, state) {
|
|
494
|
+
const installed = new Set([...state.matchAll(/^"?([^\s"#][^"]*?)"?:$/gm)].flatMap((m) => m[1]?.replace(/@virtual:[^#]+#/, "@") ?? []));
|
|
495
|
+
let entries = 0;
|
|
496
|
+
for (const entry of lockfile.split(/\n(?=\S)/)) {
|
|
497
|
+
const resolution = /^ {2}resolution: "([^"]+)"$/m.exec(entry)?.[1];
|
|
498
|
+
if (!resolution) continue;
|
|
499
|
+
entries += 1;
|
|
500
|
+
if (installed.has(resolution) || /^ {2}conditions: /m.test(entry)) continue;
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
return entries === 0 ? null : true;
|
|
504
|
+
}
|
|
505
|
+
function installedMatchesLockfile(dir, lockfile, { read = readFileSync$1 } = {}) {
|
|
506
|
+
const text = (rel) => {
|
|
507
|
+
try {
|
|
508
|
+
return read(join$1(dir, rel), "utf-8").replaceAll("\r\n", "\n");
|
|
509
|
+
} catch {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
const wanted = text(lockfile);
|
|
514
|
+
if (wanted === null) return null;
|
|
515
|
+
try {
|
|
516
|
+
if (lockfile === "package-lock.json") {
|
|
517
|
+
const installed = text("node_modules/.package-lock.json");
|
|
518
|
+
return installed === null ? null : npmInstallMatches(JSON.parse(wanted), JSON.parse(installed));
|
|
519
|
+
}
|
|
520
|
+
if (lockfile === "pnpm-lock.yaml") {
|
|
521
|
+
const installed = text("node_modules/.pnpm/lock.yaml");
|
|
522
|
+
return installed === null ? null : lastYamlDocument(wanted) === lastYamlDocument(installed);
|
|
523
|
+
}
|
|
524
|
+
if (lockfile === "yarn.lock") {
|
|
525
|
+
if (/^__metadata:$/m.test(wanted)) {
|
|
526
|
+
const berry = text("node_modules/.yarn-state.yml");
|
|
527
|
+
return berry === null ? null : yarnBerryInstallMatches(wanted, berry);
|
|
528
|
+
}
|
|
529
|
+
const classic = text("node_modules/.yarn-integrity");
|
|
530
|
+
return classic === null ? null : yarnClassicInstallMatches(wanted, JSON.parse(classic));
|
|
531
|
+
}
|
|
532
|
+
} catch {}
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
function depsOutOfSync(root, target, copiedEntries, { read = readFileSync$1 } = {}) {
|
|
536
|
+
const problems = [];
|
|
537
|
+
for (const rel of copiedEntries || []) {
|
|
538
|
+
if (rel !== "node_modules" && !rel.endsWith("/node_modules")) continue;
|
|
539
|
+
const dir = rel === "node_modules" ? "" : rel.slice(0, -13);
|
|
540
|
+
const name = LOCKFILE_NAMES.find((candidate) => existsSync$1(join$1(target, dir, candidate)));
|
|
541
|
+
if (!name) continue;
|
|
542
|
+
const installed = installedMatchesLockfile(join$1(target, dir), name, { read });
|
|
543
|
+
if (installed === false) {
|
|
544
|
+
problems.push({
|
|
545
|
+
dir: dir || ".",
|
|
546
|
+
lockfile: name,
|
|
547
|
+
reason: "installed"
|
|
548
|
+
});
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
const source = join$1(root, dir, name);
|
|
552
|
+
if (installed === true || !existsSync$1(source)) continue;
|
|
553
|
+
try {
|
|
554
|
+
if (read(source, "utf-8") !== read(join$1(target, dir, name), "utf-8")) problems.push({
|
|
555
|
+
dir: dir || ".",
|
|
556
|
+
lockfile: name,
|
|
557
|
+
reason: "lockfile"
|
|
558
|
+
});
|
|
559
|
+
} catch {}
|
|
560
|
+
}
|
|
561
|
+
return problems;
|
|
562
|
+
}
|
|
563
|
+
const FINGERPRINT_INPUT_FILES = [
|
|
564
|
+
"app.json",
|
|
565
|
+
"app.config.ts",
|
|
566
|
+
"app.config.js",
|
|
567
|
+
"app.config.mjs",
|
|
568
|
+
"package.json"
|
|
569
|
+
];
|
|
570
|
+
function dirtyFingerprintFiles(root) {
|
|
571
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
572
|
+
"-C",
|
|
573
|
+
root,
|
|
574
|
+
"status",
|
|
575
|
+
"--porcelain",
|
|
576
|
+
"--",
|
|
577
|
+
...FINGERPRINT_INPUT_FILES
|
|
578
|
+
]);
|
|
579
|
+
if (out === null || out.trim() === "") return [];
|
|
580
|
+
return out.split("\n").map((line) => normalizePorcelainLine(line.trimEnd())).filter((line) => line !== "").map((line) => line.slice(3).trim()).filter((path) => path !== "");
|
|
581
|
+
}
|
|
582
|
+
function hasUncommittedWork(dir) {
|
|
583
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
584
|
+
"--no-optional-locks",
|
|
585
|
+
"-C",
|
|
586
|
+
dir,
|
|
587
|
+
"status",
|
|
588
|
+
"--porcelain"
|
|
589
|
+
]);
|
|
590
|
+
if (out === null) return null;
|
|
591
|
+
return out.trim().length > 0;
|
|
592
|
+
}
|
|
593
|
+
function dirtyPaths(dir, { limit = 10 } = {}) {
|
|
594
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
595
|
+
"--no-optional-locks",
|
|
596
|
+
"-C",
|
|
597
|
+
dir,
|
|
598
|
+
"status",
|
|
599
|
+
"--porcelain"
|
|
600
|
+
]);
|
|
601
|
+
if (out === null) return [];
|
|
602
|
+
return out.split("\n").map((l) => normalizePorcelainLine(l.trimEnd())).filter(Boolean).slice(0, limit);
|
|
603
|
+
}
|
|
604
|
+
function normalizePorcelainLine(line) {
|
|
605
|
+
if (line === "" || line[2] === " ") return line;
|
|
606
|
+
return ` ${line}`;
|
|
607
|
+
}
|
|
608
|
+
function restoreFile(dir, file) {
|
|
609
|
+
return getExecutor().runFileQuiet("git", [
|
|
610
|
+
"-C",
|
|
611
|
+
dir,
|
|
612
|
+
"checkout",
|
|
613
|
+
"--",
|
|
614
|
+
file
|
|
615
|
+
]) !== null;
|
|
616
|
+
}
|
|
617
|
+
function isPodInstallChurn(paths) {
|
|
618
|
+
if (!paths || paths.length === 0) return false;
|
|
619
|
+
return paths.every((line) => /(?:^|\/)(?:Podfile\.lock|project\.pbxproj)$/.test(line.slice(3).trim()));
|
|
620
|
+
}
|
|
621
|
+
const SAFE_BRANCH_NAME = /^[A-Za-z0-9@._/-]+$/;
|
|
622
|
+
function unpushedCommits(dir) {
|
|
623
|
+
const exec = getExecutor();
|
|
624
|
+
const branch = exec.runFileQuiet("git", [
|
|
625
|
+
"-C",
|
|
626
|
+
dir,
|
|
627
|
+
"symbolic-ref",
|
|
628
|
+
"--quiet",
|
|
629
|
+
"--short",
|
|
630
|
+
"HEAD"
|
|
631
|
+
]);
|
|
632
|
+
const own = branch === null ? "" : branch.trim();
|
|
633
|
+
const protection = own && SAFE_BRANCH_NAME.test(own) ? [
|
|
634
|
+
"--remotes",
|
|
635
|
+
`--exclude=${own}`,
|
|
636
|
+
"--branches"
|
|
637
|
+
] : ["--remotes"];
|
|
638
|
+
if (!own && exec.runFileQuiet("git", [
|
|
639
|
+
"-C",
|
|
640
|
+
dir,
|
|
641
|
+
"rev-parse",
|
|
642
|
+
"--abbrev-ref",
|
|
643
|
+
"HEAD"
|
|
644
|
+
]) === "HEAD") protection.push("--branches");
|
|
645
|
+
const out = exec.runFileQuiet("git", [
|
|
646
|
+
"-C",
|
|
647
|
+
dir,
|
|
648
|
+
"log",
|
|
649
|
+
"--oneline",
|
|
650
|
+
"HEAD",
|
|
651
|
+
"--not",
|
|
652
|
+
...protection
|
|
653
|
+
]);
|
|
654
|
+
if (out === null) return null;
|
|
655
|
+
return out.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
656
|
+
}
|
|
657
|
+
function hasRemote(dir) {
|
|
658
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
659
|
+
"-C",
|
|
660
|
+
dir,
|
|
661
|
+
"remote"
|
|
662
|
+
]);
|
|
663
|
+
return Boolean(out && out.trim().length > 0);
|
|
664
|
+
}
|
|
665
|
+
function branchExists(cwd, branch) {
|
|
666
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
667
|
+
"-C",
|
|
668
|
+
cwd,
|
|
669
|
+
"rev-parse",
|
|
670
|
+
"--verify",
|
|
671
|
+
"--quiet",
|
|
672
|
+
`refs/heads/${branch}`
|
|
673
|
+
]);
|
|
674
|
+
return Boolean(out);
|
|
675
|
+
}
|
|
676
|
+
function resolveFullRef(cwd, ref) {
|
|
677
|
+
try {
|
|
678
|
+
const out = getExecutor().runFile("git", [
|
|
679
|
+
"-C",
|
|
680
|
+
cwd,
|
|
681
|
+
"rev-parse",
|
|
682
|
+
"--verify",
|
|
683
|
+
"--quiet",
|
|
684
|
+
"--end-of-options",
|
|
685
|
+
`${ref}^{commit}`
|
|
686
|
+
]);
|
|
687
|
+
return out && out.trim() ? out.trim() : null;
|
|
688
|
+
} catch {
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
function removeWorktree(path, { from, force = false, platform = process.platform }) {
|
|
693
|
+
const args = [
|
|
694
|
+
...platform === "win32" ? ["-c", "core.longpaths=true"] : [],
|
|
695
|
+
"-C",
|
|
696
|
+
from,
|
|
697
|
+
"worktree",
|
|
698
|
+
"remove",
|
|
699
|
+
...force ? ["--force"] : [],
|
|
700
|
+
"--",
|
|
701
|
+
path
|
|
702
|
+
];
|
|
703
|
+
getExecutor().runFile("git", args);
|
|
704
|
+
}
|
|
705
|
+
function deleteBranch(cwd, branch, expectedSha) {
|
|
706
|
+
if (!SAFE_BRANCH_NAME.test(branch) || branch.startsWith("-")) throw new Error(`Refusing branch ${JSON.stringify(branch)}: it is not a safe local branch name.`);
|
|
707
|
+
getExecutor().runFile("git", [
|
|
708
|
+
"-C",
|
|
709
|
+
cwd,
|
|
710
|
+
"update-ref",
|
|
711
|
+
"-d",
|
|
712
|
+
`refs/heads/${branch}`,
|
|
713
|
+
expectedSha
|
|
714
|
+
]);
|
|
715
|
+
}
|
|
716
|
+
function parseWorktrees(out) {
|
|
717
|
+
const entries = [];
|
|
718
|
+
let current = {};
|
|
719
|
+
for (const line of out.split("\n")) if (line.startsWith("worktree ")) {
|
|
720
|
+
if (current.path) entries.push(current);
|
|
721
|
+
current = { path: nativePath(line.slice(9)) };
|
|
722
|
+
} else if (line.startsWith("branch ")) current.branch = line.slice(7).replace("refs/heads/", "");
|
|
723
|
+
else if (line === "prunable" || line.startsWith("prunable ")) current.prunable = true;
|
|
724
|
+
else if (line === "locked" || line.startsWith("locked ")) current.locked = true;
|
|
725
|
+
else if (line === "bare") current.bare = true;
|
|
726
|
+
if (current.path) entries.push(current);
|
|
727
|
+
return entries;
|
|
728
|
+
}
|
|
729
|
+
function hasPopulatedSubmodules(worktree) {
|
|
730
|
+
const modules = getExecutor().runFileQuiet("git", [
|
|
731
|
+
"-C",
|
|
732
|
+
worktree,
|
|
733
|
+
"rev-parse",
|
|
734
|
+
"--git-path",
|
|
735
|
+
"modules"
|
|
736
|
+
]);
|
|
737
|
+
if (modules && existsSync$1(resolve$1(worktree, nativePath(modules)))) return true;
|
|
738
|
+
return (getExecutor().runFileQuiet("git", [
|
|
739
|
+
"-C",
|
|
740
|
+
worktree,
|
|
741
|
+
"ls-files",
|
|
742
|
+
"--stage",
|
|
743
|
+
"-z"
|
|
744
|
+
]) ?? "").split("\0").some((line) => line.startsWith("160000 ") && existsSync$1(join$1(worktree, line.slice(line.indexOf(" ") + 1), ".git")));
|
|
745
|
+
}
|
|
746
|
+
function listWorktrees(cwd) {
|
|
747
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
748
|
+
"-C",
|
|
749
|
+
cwd,
|
|
750
|
+
"worktree",
|
|
751
|
+
"list",
|
|
752
|
+
"--porcelain"
|
|
753
|
+
]);
|
|
754
|
+
return out ? parseWorktrees(out) : [];
|
|
755
|
+
}
|
|
756
|
+
function branchOf(head) {
|
|
757
|
+
return head.startsWith("ref: refs/heads/") ? head.slice(16) : void 0;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* The linked worktrees recorded under a git common dir, read from `worktrees/<id>/gitdir` and `HEAD` without running
|
|
761
|
+
* git or touching the worktree directories, sorted by path. The worktree with the branch the common dir's own `HEAD`
|
|
762
|
+
* names (the main checkout's branch, or a bare repository's source checkout) is left out.
|
|
763
|
+
*/
|
|
764
|
+
function linkedWorktreesOnDisk(commonDir) {
|
|
765
|
+
const admin = join$1(commonDir, "worktrees");
|
|
766
|
+
let ids;
|
|
767
|
+
let sourceBranch;
|
|
768
|
+
try {
|
|
769
|
+
ids = readdirSync$1(admin);
|
|
770
|
+
sourceBranch = branchOf(readFileSync$1(join$1(commonDir, "HEAD"), "utf-8").trim());
|
|
771
|
+
} catch {
|
|
772
|
+
return [];
|
|
773
|
+
}
|
|
774
|
+
return ids.flatMap((id) => {
|
|
775
|
+
try {
|
|
776
|
+
const gitdir = readFileSync$1(join$1(admin, id, "gitdir"), "utf-8").trim();
|
|
777
|
+
const branch = branchOf(readFileSync$1(join$1(admin, id, "HEAD"), "utf-8").trim());
|
|
778
|
+
if (branch && branch === sourceBranch) return [];
|
|
779
|
+
const path = dirname$1(resolve$1(admin, id, nativePath(gitdir)));
|
|
780
|
+
return [branch ? {
|
|
781
|
+
path,
|
|
782
|
+
branch
|
|
783
|
+
} : { path }];
|
|
784
|
+
} catch {
|
|
785
|
+
return [];
|
|
786
|
+
}
|
|
787
|
+
}).toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* The git common dir of the repository containing `start`, read from `.git` and `commondir` files without running
|
|
791
|
+
* git. Returns null when `start` is missing, unreadable, or not inside a repository.
|
|
792
|
+
*/
|
|
793
|
+
function gitCommonDirOnDisk(start) {
|
|
794
|
+
try {
|
|
795
|
+
for (let dir = realpathSync$1.native(start);; dir = dirname$1(dir)) {
|
|
796
|
+
const dotGit = join$1(dir, ".git");
|
|
797
|
+
if (existsSync$1(dotGit)) {
|
|
798
|
+
if (statSync$1(dotGit).isDirectory()) return realpathSync$1.native(dotGit);
|
|
799
|
+
const gitdir = /^gitdir: (.+)$/m.exec(readFileSync$1(dotGit, "utf-8"))?.[1]?.trim();
|
|
800
|
+
if (!gitdir) return null;
|
|
801
|
+
const linked = resolve$1(dir, gitdir);
|
|
802
|
+
const commondir = join$1(linked, "commondir");
|
|
803
|
+
return realpathSync$1.native(existsSync$1(commondir) ? resolve$1(linked, readFileSync$1(commondir, "utf-8").trim()) : linked);
|
|
804
|
+
}
|
|
805
|
+
if (dirname$1(dir) === dir) return null;
|
|
806
|
+
}
|
|
807
|
+
} catch {
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function selectSourceCheckout(entries, bareHead) {
|
|
812
|
+
const first = entries[0];
|
|
813
|
+
if (!first) return { refusal: "Git lists no worktrees for this repository." };
|
|
814
|
+
if (!first.bare) return { path: first.path };
|
|
815
|
+
const bare = first.path;
|
|
816
|
+
const checkouts = entries.filter((entry) => !entry.bare && !entry.prunable);
|
|
817
|
+
const listed = checkouts.map((entry) => `${entry.branch ?? "detached"} (${entry.path})`).join(", ") || "none";
|
|
818
|
+
const pointHead = ` git -C ${bare} symbolic-ref HEAD refs/heads/<branch>`;
|
|
819
|
+
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}` };
|
|
820
|
+
const matches = checkouts.filter((entry) => entry.branch === bareHead);
|
|
821
|
+
const [only] = matches;
|
|
822
|
+
if (only && matches.length === 1) return { path: only.path };
|
|
823
|
+
if (!only) {
|
|
824
|
+
const stale = entries.find((entry) => entry.prunable && entry.branch === bareHead);
|
|
825
|
+
const beside = checkouts[0] ? dirname$1(checkouts[0].path) : bare;
|
|
826
|
+
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}` };
|
|
827
|
+
}
|
|
828
|
+
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}` };
|
|
829
|
+
}
|
|
830
|
+
function bareHeadBranch(bare) {
|
|
831
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
832
|
+
"-C",
|
|
833
|
+
bare,
|
|
834
|
+
"symbolic-ref",
|
|
835
|
+
"--quiet",
|
|
836
|
+
"HEAD"
|
|
837
|
+
])?.trim();
|
|
838
|
+
return out?.startsWith("refs/heads/") ? out.slice(11) : null;
|
|
839
|
+
}
|
|
840
|
+
function sourceCheckoutOf(entries) {
|
|
841
|
+
const first = entries[0];
|
|
842
|
+
return selectSourceCheckout(entries, first?.bare ? bareHeadBranch(first.path) : null);
|
|
843
|
+
}
|
|
844
|
+
function resolveSourceCheckout(cwd) {
|
|
845
|
+
const entries = listWorktrees(cwd);
|
|
846
|
+
return {
|
|
847
|
+
entries,
|
|
848
|
+
...sourceCheckoutOf(entries)
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
//#endregion
|
|
105
852
|
//#region src/engine/metro-reach.ts
|
|
106
853
|
const PUBLIC_METRO_ENV = "STIM_METRO_PUBLIC_URL";
|
|
107
854
|
const MANAGED_PROVIDERS = ["ngrok", "cloudflared"];
|
|
@@ -347,15 +1094,15 @@ function androidAvdConfigSetting(settings, settingsRoot) {
|
|
|
347
1094
|
if ("avdConfigFile" in android) {
|
|
348
1095
|
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.");
|
|
349
1096
|
try {
|
|
350
|
-
const root = realpathSync(settingsRoot);
|
|
1097
|
+
const root = realpathSync$1(settingsRoot);
|
|
351
1098
|
const candidate = resolve$1(root, android.avdConfigFile);
|
|
352
1099
|
if (pathEscapesRoot(root, candidate)) throw new Error("path escapes the settings root");
|
|
353
|
-
const path = realpathSync(candidate);
|
|
1100
|
+
const path = realpathSync$1(candidate);
|
|
354
1101
|
if (pathEscapesRoot(root, path)) throw new Error("symlink target escapes the settings root");
|
|
355
|
-
const stat = statSync(path);
|
|
1102
|
+
const stat = statSync$1(path);
|
|
356
1103
|
if (!stat.isFile()) throw new Error("path is not a regular file");
|
|
357
1104
|
if (stat.size > 65536) throw new Error("file exceeds 64 KiB");
|
|
358
|
-
const contents = readFileSync(path, "utf8");
|
|
1105
|
+
const contents = readFileSync$1(path, "utf8");
|
|
359
1106
|
if (Buffer.byteLength(contents) > 65536) throw new Error("file exceeds 64 KiB");
|
|
360
1107
|
entries = parseAndroidAvdConfigIni(contents);
|
|
361
1108
|
} catch (error) {
|
|
@@ -384,12 +1131,12 @@ function iosSimSlimProfileSetting(settings, settingsRoot) {
|
|
|
384
1131
|
const value = settings.ios.simslimProfile;
|
|
385
1132
|
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.");
|
|
386
1133
|
try {
|
|
387
|
-
const root = realpathSync(settingsRoot);
|
|
1134
|
+
const root = realpathSync$1(settingsRoot);
|
|
388
1135
|
const candidate = resolve$1(root, value);
|
|
389
1136
|
if (pathEscapesRoot(root, candidate)) throw new Error("path escapes the settings root");
|
|
390
|
-
const path = realpathSync(candidate);
|
|
1137
|
+
const path = realpathSync$1(candidate);
|
|
391
1138
|
if (pathEscapesRoot(root, path)) throw new Error("symlink target escapes the settings root");
|
|
392
|
-
const stat = statSync(path);
|
|
1139
|
+
const stat = statSync$1(path);
|
|
393
1140
|
if (!stat.isFile()) throw new Error("path is not a regular file");
|
|
394
1141
|
if (stat.size > 65536) throw new Error("file exceeds 64 KiB");
|
|
395
1142
|
return path;
|
|
@@ -451,9 +1198,9 @@ function unknownSettingKeys(settings, prefix = "") {
|
|
|
451
1198
|
function readCommittedSettings(directory) {
|
|
452
1199
|
if (!directory) return {};
|
|
453
1200
|
const p = join$1(directory, ".stim.json");
|
|
454
|
-
if (!existsSync(p)) return {};
|
|
1201
|
+
if (!existsSync$1(p)) return {};
|
|
455
1202
|
try {
|
|
456
|
-
const parsed = JSON.parse(readFileSync(p, "utf-8"));
|
|
1203
|
+
const parsed = JSON.parse(readFileSync$1(p, "utf-8"));
|
|
457
1204
|
return isPlainObject(parsed) ? parsed : {};
|
|
458
1205
|
} catch {
|
|
459
1206
|
return {};
|
|
@@ -588,9 +1335,11 @@ function cacheProviderSettingError(settings) {
|
|
|
588
1335
|
if ("options" in block && !isPlainObject(block.options)) return `Invalid cache.options setting ${JSON.stringify(block.options)}. Expected an object.`;
|
|
589
1336
|
return null;
|
|
590
1337
|
}
|
|
1338
|
+
/** The remote target `remote` names, or null for `local`, which runs on this Mac whatever a lower layer sets. */
|
|
591
1339
|
function parseIosRemote(remote) {
|
|
592
1340
|
const selected = remote.trim();
|
|
593
1341
|
const reserved = selected.toLowerCase();
|
|
1342
|
+
if (reserved === "local") return null;
|
|
594
1343
|
return REMOTE_DEVICE_BACKENDS.includes(reserved) ? {
|
|
595
1344
|
kind: "backend",
|
|
596
1345
|
backend: reserved
|
|
@@ -603,6 +1352,9 @@ function remoteIosSetting(settings) {
|
|
|
603
1352
|
const ios = settings.ios;
|
|
604
1353
|
return isPlainObject(ios) && typeof ios.remote === "string" ? parseIosRemote(ios.remote) : null;
|
|
605
1354
|
}
|
|
1355
|
+
function remoteEasFallbackSetting(settings) {
|
|
1356
|
+
return settingValueAt(settings, "remote.easFallback") === true;
|
|
1357
|
+
}
|
|
606
1358
|
function remoteAndroidSetting(settings) {
|
|
607
1359
|
const android = settings.android;
|
|
608
1360
|
return isPlainObject(android) && typeof android.remote === "string" ? parseIosRemote(android.remote) : null;
|
|
@@ -698,4 +1450,4 @@ function webSettings(settings) {
|
|
|
698
1450
|
};
|
|
699
1451
|
}
|
|
700
1452
|
//#endregion
|
|
701
|
-
export { publicUrlSetting as A,
|
|
1453
|
+
export { depsOutOfSync as $, publicUrlSetting as A, settingShapeErrors as B, metroWarmupUrlSetting as C, removeTemporaryEntry as Ct, projectMaintenancePinned as D, optimizationBuildProfile as Dt, projectDeviceReclaimIdleMinutes as E, compilerCacheFallbackMessage as Et, resolveCacheProviderConfig as F, webSettings as G, settingsLayers as H, resolveSettings as I, planMetroReach as J, PUBLIC_METRO_ENV as K, settingFile as L, remoteAndroidSetting as M, remoteEasFallbackSetting as N, projectMetroSharedCache as O, resolveOptimizations as Ot, remoteIosSetting as P, deleteBranch as Q, settingOrigin as R, metroTunnelSettingError as S, makeTemporaryDirectory as St, parseIosRemote as T, artifactCachePolicy as Tt, tunnelModeSetting as U, settingValueAt as V, unknownSettingKeys as W, branchExists as X, resolveTailscaleBinary as Y, cloneIgnoredEntries as Z, iosSigningIdentitySha1SettingError as _, restoreFile as _t, androidAvdConfigSetting as a, hasRemote as at, metroIdleStopMinutesSetting as b, warmWorktreePaths as bt, androidDataPartitionSizeGbSetting as c, linkedWorktreesOnDisk as ct, deviceIdleShutdownMinutesSetting as d, podsOutOfSync as dt, dirtyFingerprintFiles as et, iosLanHostSetting as f, readWorktreeExclude as ft, iosSigningIdentitySha1Setting as g, resolveSourceCheckout as gt, iosSigningIdentitySettingError as h, resolveFullRef as ht, SETTING_SHAPE_REMEDY as i, hasPopulatedSubmodules as it, readCommittedSettings as j, projectOptimizations as k, androidDataPartitionSizeGbSettingError as l, listWorktrees as lt, iosSigningIdentitySetting as m, repoRoot as mt, PATH_SETTINGS as n, gitCommonDir as nt, androidAvdConfigSettingError as o, hasUncommittedWork as ot, iosLanHostSettingError as p, removeWorktree as pt, detectProviders as q, SETTINGS_WITH_RESOLVE_TIME_FALLBACK as r, gitCommonDirOnDisk as rt, androidDataPartitionSizeBytes as s, isPodInstallChurn as st, ANDROID_AVD_CONFIG_HELP as t, dirtyPaths as tt, cacheProviderSettingError as u, locallyKnownUpstream as ut, iosSimSlimProfileSetting as v, sourceCheckoutOf as vt, ngrokUrlSetting as w, temporaryRoot as wt, metroPortSetting as x, filesystemDevice as xt, mergeSettingsLayers as y, unpushedCommits as yt, settingOriginScope as z };
|