stim 0.0.0 → 1.0.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/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/android-cas-compiler.d.mts +1 -0
- package/dist/android-cas-compiler.mjs +71 -0
- package/dist/android-jc8MnUyL.mjs +514 -0
- package/dist/cache-manifest-4tH64LQ9.mjs +66 -0
- package/dist/cache-manifest.d.mts +27 -0
- package/dist/cache-manifest.mjs +2 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +27182 -0
- package/dist/collector-run.d.mts +119 -0
- package/dist/collector-run.mjs +539 -0
- package/dist/config-7kmtuhO2.mjs +350 -0
- package/dist/errors-B6W7FkxQ.mjs +15 -0
- package/dist/exec-bsN9MJXb.mjs +63 -0
- package/dist/metro-store-COLl1pOk.mjs +1249 -0
- package/dist/ndjson-DcAtEx_K.mjs +131 -0
- package/dist/ndjson-DvHSsNU9.d.mts +16 -0
- package/dist/process-output-C7IlIzd7.mjs +40 -0
- package/dist/project-Dit1CckW.mjs +234 -0
- package/dist/server-bare-BmTVVmBB.mjs +351 -0
- package/dist/server-expo-BZJm--wA.mjs +2 -0
- package/dist/server-expo-CHJe1r6j.mjs +311 -0
- package/dist/state-2nush3MK.mjs +269 -0
- package/dist/supervisor-run.d.mts +85 -0
- package/dist/supervisor-run.mjs +210 -0
- package/package.json +61 -8
- package/shim/android-cas-pch.cmake +35 -0
- package/shim/android-cas.gradle +29 -0
- package/shim/android-cas.toolchain.cmake +12 -0
- package/shim/android-optimizations.gradle +30 -0
- package/shim/bundle-response.cjs +46 -0
- package/shim/bundle-response.d.cts +5 -0
- package/shim/expo-metro-config.cjs +188 -0
- package/shim/metro-cache-generation.cjs +20 -0
- package/shim/metro-cache-generation.d.cts +5 -0
- package/skill/SKILL.md +14 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { t as getExecutor } from "./exec-bsN9MJXb.mjs";
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
3
|
+
import { dirname, isAbsolute, join, normalize } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { withDirLock } from "@stim-cli/core";
|
|
6
|
+
import { capture, check, decode } from "unique-pid";
|
|
7
|
+
//#region src/fs-util.ts
|
|
8
|
+
function volumeRootFor(path) {
|
|
9
|
+
const m = normalize(String(path)).match(/^\/volumes\/([^/]+)/i);
|
|
10
|
+
return m ? `/Volumes/${m[1]}` : "/";
|
|
11
|
+
}
|
|
12
|
+
function resolveWorkspaceRealish(workspacePath) {
|
|
13
|
+
const raw = String(workspacePath);
|
|
14
|
+
if (!raw.startsWith("/")) return null;
|
|
15
|
+
let current = raw;
|
|
16
|
+
const MAX_HOPS = 40;
|
|
17
|
+
for (let hops = 0; hops < MAX_HOPS; hops++) {
|
|
18
|
+
const normalized = normalize(current);
|
|
19
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
20
|
+
let prefix = "";
|
|
21
|
+
let followedSymlink = false;
|
|
22
|
+
for (const segment of segments) {
|
|
23
|
+
prefix += `/${segment}`;
|
|
24
|
+
let st;
|
|
25
|
+
try {
|
|
26
|
+
st = lstatSync(prefix);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
if (err && err.code === "ENOENT") return normalized;
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (st.isSymbolicLink()) {
|
|
32
|
+
let target;
|
|
33
|
+
try {
|
|
34
|
+
target = readlinkSync(prefix);
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
current = `${target.startsWith("/") ? target : normalize(join(dirname(prefix), target))}${normalized.slice(prefix.length)}`;
|
|
39
|
+
followedSymlink = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!followedSymlink) return normalized;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
function isRealMount(entryDev, rootDev) {
|
|
48
|
+
return entryDev != null && rootDev != null && entryDev !== rootDev;
|
|
49
|
+
}
|
|
50
|
+
function listMountedVolumes({ statFn = statSync } = {}) {
|
|
51
|
+
const roots = ["/"];
|
|
52
|
+
let rootDev;
|
|
53
|
+
try {
|
|
54
|
+
rootDev = statFn("/").dev;
|
|
55
|
+
} catch {
|
|
56
|
+
return roots;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
for (const name of readdirSync("/Volumes")) {
|
|
60
|
+
const path = join("/Volumes", name);
|
|
61
|
+
let entryDev;
|
|
62
|
+
try {
|
|
63
|
+
entryDev = statFn(path).dev;
|
|
64
|
+
} catch {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (isRealMount(entryDev, rootDev)) roots.push(path);
|
|
68
|
+
}
|
|
69
|
+
} catch {}
|
|
70
|
+
return roots;
|
|
71
|
+
}
|
|
72
|
+
function resolveVolumeRoot(path) {
|
|
73
|
+
const realish = resolveWorkspaceRealish(path);
|
|
74
|
+
return realish === null ? null : volumeRootFor(realish);
|
|
75
|
+
}
|
|
76
|
+
function isOnMountedVolume(path, mountedVolumes) {
|
|
77
|
+
const mounted = new Set(mountedVolumes || listMountedVolumes());
|
|
78
|
+
const volume = resolveVolumeRoot(path);
|
|
79
|
+
if (volume === null) return false;
|
|
80
|
+
return mounted.has(volume);
|
|
81
|
+
}
|
|
82
|
+
function directorySize(dir, { timeoutMs } = {}) {
|
|
83
|
+
let out;
|
|
84
|
+
try {
|
|
85
|
+
out = getExecutor().runFile("du", ["-sk", dir], { timeoutMs });
|
|
86
|
+
} catch {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
if (!out) return 0;
|
|
90
|
+
const kb = parseInt(out.split(/\s+/)[0] ?? "", 10);
|
|
91
|
+
return isNaN(kb) ? 0 : kb * 1024;
|
|
92
|
+
}
|
|
93
|
+
function formatBytes(bytes) {
|
|
94
|
+
if (bytes >= 1024 ** 3) return `${(bytes / 1024 ** 3).toFixed(1)}G`;
|
|
95
|
+
if (bytes >= 1024 ** 2) return `${Math.round(bytes / 1024 ** 2)}M`;
|
|
96
|
+
return `${Math.round(bytes / 1024)}K`;
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/process-identity.ts
|
|
100
|
+
function sameProcessRecord(a, b) {
|
|
101
|
+
if (!a || !b) return !a && !b;
|
|
102
|
+
return a.pid === b.pid && a.processToken === b.processToken;
|
|
103
|
+
}
|
|
104
|
+
function captureProcessToken(pid) {
|
|
105
|
+
const result = capture(pid);
|
|
106
|
+
return result.ok ? result.value : null;
|
|
107
|
+
}
|
|
108
|
+
function inspectProcessIdentity(record) {
|
|
109
|
+
if (!record || typeof record.processToken !== "string") return "unknown";
|
|
110
|
+
const decoded = decode(record.processToken);
|
|
111
|
+
if (!decoded.ok || decoded.value.pid !== record.pid) return "unknown";
|
|
112
|
+
const result = check(record.processToken);
|
|
113
|
+
return result.ok ? result.value : "unknown";
|
|
114
|
+
}
|
|
115
|
+
async function waitForProcessExit(record, timeoutMs) {
|
|
116
|
+
const deadline = Date.now() + timeoutMs;
|
|
117
|
+
while (Date.now() <= deadline) {
|
|
118
|
+
const identity = inspectProcessIdentity(record);
|
|
119
|
+
if (identity === "different" || identity === "gone") return true;
|
|
120
|
+
if (Date.now() >= deadline) return false;
|
|
121
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/config.ts
|
|
127
|
+
function getConfigDir() {
|
|
128
|
+
return process.env.STIM_HOME || join(homedir(), ".stim");
|
|
129
|
+
}
|
|
130
|
+
function getConfigPath() {
|
|
131
|
+
return join(getConfigDir(), "config.json");
|
|
132
|
+
}
|
|
133
|
+
function ensureDir() {
|
|
134
|
+
const dir = getConfigDir();
|
|
135
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
const LOCK_DIR_NAME = "config.lock";
|
|
138
|
+
function lockPath() {
|
|
139
|
+
return join(getConfigDir(), LOCK_DIR_NAME);
|
|
140
|
+
}
|
|
141
|
+
function withConfigLock(fn) {
|
|
142
|
+
return withDirLock(lockPath(), fn, { ensureParent: ensureDir });
|
|
143
|
+
}
|
|
144
|
+
function loadConfig() {
|
|
145
|
+
const p = getConfigPath();
|
|
146
|
+
if (!existsSync(p)) return null;
|
|
147
|
+
const raw = readFileSync(p, "utf-8");
|
|
148
|
+
try {
|
|
149
|
+
return JSON.parse(raw);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
const corrupt = /* @__PURE__ */ new Error(`Stim config at ${p} is not valid JSON: ${err.message}\nIt holds the records of the simulators and emulators Stim owns, so it is never reset automatically.
|
|
152
|
+
Repair the file, or move it aside to start over: mv "${p}" "${p}.broken"`);
|
|
153
|
+
corrupt.code = "STIM_CONFIG_CORRUPT";
|
|
154
|
+
throw corrupt;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function saveConfig(config) {
|
|
158
|
+
ensureDir();
|
|
159
|
+
const target = getConfigPath();
|
|
160
|
+
const tmp = `${target}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
161
|
+
writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n");
|
|
162
|
+
try {
|
|
163
|
+
renameSync(tmp, target);
|
|
164
|
+
} catch (err) {
|
|
165
|
+
try {
|
|
166
|
+
unlinkSync(tmp);
|
|
167
|
+
} catch {}
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const CONFIG_VERSION = 2;
|
|
172
|
+
function ensureConfig() {
|
|
173
|
+
return withConfigLock(() => {
|
|
174
|
+
const existing = loadConfig();
|
|
175
|
+
if (existing) {
|
|
176
|
+
let changed = false;
|
|
177
|
+
if (!existing.repos) {
|
|
178
|
+
existing.repos = {};
|
|
179
|
+
changed = true;
|
|
180
|
+
}
|
|
181
|
+
if (existing.version !== CONFIG_VERSION) {
|
|
182
|
+
existing.version = CONFIG_VERSION;
|
|
183
|
+
changed = true;
|
|
184
|
+
}
|
|
185
|
+
if (changed) saveConfig(existing);
|
|
186
|
+
return existing;
|
|
187
|
+
}
|
|
188
|
+
const fresh = {
|
|
189
|
+
version: CONFIG_VERSION,
|
|
190
|
+
projects: {},
|
|
191
|
+
repos: {}
|
|
192
|
+
};
|
|
193
|
+
saveConfig(fresh);
|
|
194
|
+
return fresh;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function getProject(projectPath) {
|
|
198
|
+
return loadConfig()?.projects?.[projectPath] || null;
|
|
199
|
+
}
|
|
200
|
+
function requireAbsoluteProjectPath(projectPath) {
|
|
201
|
+
if (isAbsolute(projectPath)) return;
|
|
202
|
+
throw new Error(`Project key "${projectPath}" is not an absolute path. The registry is keyed by the realpath of a workspace root, so a relative key can never resolve.`);
|
|
203
|
+
}
|
|
204
|
+
function upsertProject(projectPath, fields) {
|
|
205
|
+
requireAbsoluteProjectPath(projectPath);
|
|
206
|
+
return withConfigLock(() => {
|
|
207
|
+
const cfg = ensureConfig();
|
|
208
|
+
const existing = cfg.projects[projectPath] || {
|
|
209
|
+
metroPort: null,
|
|
210
|
+
platforms: {}
|
|
211
|
+
};
|
|
212
|
+
cfg.projects[projectPath] = {
|
|
213
|
+
...existing,
|
|
214
|
+
...fields
|
|
215
|
+
};
|
|
216
|
+
saveConfig(cfg);
|
|
217
|
+
return cfg.projects[projectPath];
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function removeProject(projectPath) {
|
|
221
|
+
withConfigLock(() => {
|
|
222
|
+
const cfg = loadConfig();
|
|
223
|
+
if (!cfg?.projects?.[projectPath]) return;
|
|
224
|
+
delete cfg.projects[projectPath];
|
|
225
|
+
saveConfig(cfg);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function claimMetroPort(projectPath, port) {
|
|
229
|
+
return withConfigLock(() => {
|
|
230
|
+
const cfg = ensureConfig();
|
|
231
|
+
if (!cfg.projects[projectPath]) throw new Error(`Project not registered: ${projectPath}`);
|
|
232
|
+
for (const [path, proj] of Object.entries(cfg.projects)) if (path !== projectPath && proj?.metroPort === port) return null;
|
|
233
|
+
cfg.projects[projectPath].metroPort = port;
|
|
234
|
+
saveConfig(cfg);
|
|
235
|
+
return port;
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function setDevice(projectPath, platform, deviceFields) {
|
|
239
|
+
requireAbsoluteProjectPath(projectPath);
|
|
240
|
+
withConfigLock(() => {
|
|
241
|
+
const cfg = ensureConfig();
|
|
242
|
+
if (!cfg.projects[projectPath]) throw new Error(`Project not registered: ${projectPath}`);
|
|
243
|
+
cfg.projects[projectPath].platforms = cfg.projects[projectPath].platforms || {};
|
|
244
|
+
cfg.projects[projectPath].platforms[platform] = deviceFields;
|
|
245
|
+
saveConfig(cfg);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
function releaseAndroidConsolePort(projectPath, consolePort) {
|
|
249
|
+
return withConfigLock(() => {
|
|
250
|
+
const cfg = loadConfig();
|
|
251
|
+
const android = cfg?.projects?.[projectPath]?.platforms?.android;
|
|
252
|
+
if (!cfg || !android || android.consolePort !== consolePort) return false;
|
|
253
|
+
delete android.consolePort;
|
|
254
|
+
saveConfig(cfg);
|
|
255
|
+
return true;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function clearDevice(projectPath, platform) {
|
|
259
|
+
withConfigLock(() => {
|
|
260
|
+
const cfg = loadConfig();
|
|
261
|
+
if (!cfg?.projects?.[projectPath]?.platforms) return;
|
|
262
|
+
delete cfg.projects[projectPath].platforms[platform];
|
|
263
|
+
saveConfig(cfg);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
function setSupervisor(projectPath, { pid, port, startedAt, processToken }) {
|
|
267
|
+
requireAbsoluteProjectPath(projectPath);
|
|
268
|
+
return withConfigLock(() => {
|
|
269
|
+
const cfg = ensureConfig();
|
|
270
|
+
if (!cfg.projects[projectPath]) cfg.projects[projectPath] = {
|
|
271
|
+
metroPort: null,
|
|
272
|
+
platforms: {}
|
|
273
|
+
};
|
|
274
|
+
cfg.projects[projectPath].supervisor = {
|
|
275
|
+
pid,
|
|
276
|
+
port,
|
|
277
|
+
startedAt,
|
|
278
|
+
processToken
|
|
279
|
+
};
|
|
280
|
+
saveConfig(cfg);
|
|
281
|
+
return cfg.projects[projectPath].supervisor;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
function clearSupervisor(projectPath, expected) {
|
|
285
|
+
return withConfigLock(() => {
|
|
286
|
+
const cfg = loadConfig();
|
|
287
|
+
const current = cfg?.projects?.[projectPath]?.supervisor;
|
|
288
|
+
if (!current) return true;
|
|
289
|
+
if (expected !== void 0 && !sameProcessRecord(current, expected)) return false;
|
|
290
|
+
delete cfg.projects[projectPath].supervisor;
|
|
291
|
+
saveConfig(cfg);
|
|
292
|
+
return true;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function getConcurrencyLimits({ env = process.env } = {}) {
|
|
296
|
+
const c = loadConfig()?.concurrency || {};
|
|
297
|
+
return {
|
|
298
|
+
maxBuilds: resolveLimit(env.STIM_MAX_BUILDS, c.maxBuilds),
|
|
299
|
+
maxDevices: resolveLimit(env.STIM_MAX_DEVICES, c.maxDevices)
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function resolveLimit(envVal, cfgVal) {
|
|
303
|
+
const raw = envVal !== void 0 && envVal !== null && envVal !== "" ? Number(envVal) : typeof cfgVal === "number" ? cfgVal : NaN;
|
|
304
|
+
if (!Number.isFinite(raw) || raw <= 0) return 0;
|
|
305
|
+
return Math.floor(raw);
|
|
306
|
+
}
|
|
307
|
+
function getProjectSettings(projectPath) {
|
|
308
|
+
return getProject(projectPath)?.settings || {};
|
|
309
|
+
}
|
|
310
|
+
function getRepoSettings(gitCommonDir) {
|
|
311
|
+
return loadConfig()?.repos?.[gitCommonDir]?.settings || {};
|
|
312
|
+
}
|
|
313
|
+
function isPathPrefix(prefix, path) {
|
|
314
|
+
if (prefix === path) return true;
|
|
315
|
+
const withSlash = prefix.endsWith("/") ? prefix : `${prefix}/`;
|
|
316
|
+
return path.startsWith(withSlash);
|
|
317
|
+
}
|
|
318
|
+
function findEnclosingWorktreeRoot(projectPath) {
|
|
319
|
+
const cfg = loadConfig();
|
|
320
|
+
if (!cfg?.projects) return null;
|
|
321
|
+
let best = null;
|
|
322
|
+
for (const [path, proj] of Object.entries(cfg.projects)) {
|
|
323
|
+
if (!proj?.worktreeRoot) continue;
|
|
324
|
+
if (!isPathPrefix(path, projectPath)) continue;
|
|
325
|
+
if (!best || path.length > best.length) best = path;
|
|
326
|
+
}
|
|
327
|
+
return best;
|
|
328
|
+
}
|
|
329
|
+
function allMetroPorts() {
|
|
330
|
+
const cfg = loadConfig();
|
|
331
|
+
if (!cfg?.projects) return [];
|
|
332
|
+
return Object.values(cfg.projects).map((p) => p.metroPort).filter((p) => typeof p === "number");
|
|
333
|
+
}
|
|
334
|
+
function allConsolePortsAndSerials({ isMounted = isOnMountedVolume } = {}) {
|
|
335
|
+
const cfg = loadConfig();
|
|
336
|
+
const result = {
|
|
337
|
+
androidConsolePorts: [],
|
|
338
|
+
androidPhysicalSerials: []
|
|
339
|
+
};
|
|
340
|
+
if (!cfg) return result;
|
|
341
|
+
for (const [path, proj] of Object.entries(cfg.projects || {})) {
|
|
342
|
+
if (!existsSync(path) && isMounted(path)) continue;
|
|
343
|
+
const android = proj.platforms?.android;
|
|
344
|
+
if (typeof android?.consolePort === "number") result.androidConsolePorts.push(android.consolePort);
|
|
345
|
+
if (android?.serial && !android.avdName) result.androidPhysicalSerials.push(android.serial);
|
|
346
|
+
}
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
//#endregion
|
|
350
|
+
export { listMountedVolumes as A, inspectProcessIdentity as C, directorySize as D, withDirLock as E, formatBytes as O, captureProcessToken as S, waitForProcessExit as T, saveConfig as _, clearSupervisor as a, upsertProject as b, getConcurrencyLimits as c, getProjectSettings as d, getRepoSettings as f, removeProject as g, releaseAndroidConsolePort as h, clearDevice as i, volumeRootFor as j, isOnMountedVolume as k, getConfigDir as l, loadConfig as m, allMetroPorts as n, ensureConfig as o, isPathPrefix as p, claimMetroPort as r, findEnclosingWorktreeRoot as s, allConsolePortsAndSerials as t, getProject as u, setDevice as v, sameProcessRecord as w, withConfigLock as x, setSupervisor as y };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/supervisor/errors.ts
|
|
2
|
+
function supervisorError(code, message, remedy) {
|
|
3
|
+
const err = new Error(message);
|
|
4
|
+
err.code = code;
|
|
5
|
+
if (remedy) err.remedy = remedy;
|
|
6
|
+
return err;
|
|
7
|
+
}
|
|
8
|
+
function describeError(err) {
|
|
9
|
+
if (!err) return "unknown error";
|
|
10
|
+
const e = err;
|
|
11
|
+
const message = e.message || String(err);
|
|
12
|
+
return `${e.code ? `${e.code}: ` : ""}${message}${e.remedy ? ` Remedy: ${e.remedy}` : ""}`;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { supervisorError as n, describeError as t };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { execFileSync, execSync, spawn } from "child_process";
|
|
2
|
+
//#region src/exec.ts
|
|
3
|
+
const MAX_BUFFER = 67108864;
|
|
4
|
+
let active = {
|
|
5
|
+
run(cmd, { timeoutMs, cwd } = {}) {
|
|
6
|
+
const opts = {
|
|
7
|
+
encoding: "utf-8",
|
|
8
|
+
stdio: [
|
|
9
|
+
"pipe",
|
|
10
|
+
"pipe",
|
|
11
|
+
"pipe"
|
|
12
|
+
],
|
|
13
|
+
maxBuffer: MAX_BUFFER
|
|
14
|
+
};
|
|
15
|
+
if (timeoutMs) opts.timeout = timeoutMs;
|
|
16
|
+
if (cwd) opts.cwd = cwd;
|
|
17
|
+
return String(execSync(cmd, opts)).trim();
|
|
18
|
+
},
|
|
19
|
+
runFile(file, args = [], { timeoutMs, cwd, env, omitEnv } = {}) {
|
|
20
|
+
const opts = {
|
|
21
|
+
encoding: "utf-8",
|
|
22
|
+
stdio: [
|
|
23
|
+
"pipe",
|
|
24
|
+
"pipe",
|
|
25
|
+
"pipe"
|
|
26
|
+
],
|
|
27
|
+
maxBuffer: MAX_BUFFER
|
|
28
|
+
};
|
|
29
|
+
if (timeoutMs) opts.timeout = timeoutMs;
|
|
30
|
+
if (cwd) opts.cwd = cwd;
|
|
31
|
+
if (env || omitEnv?.length) {
|
|
32
|
+
const childEnv = {
|
|
33
|
+
...process.env,
|
|
34
|
+
...env
|
|
35
|
+
};
|
|
36
|
+
for (const key of omitEnv ?? []) delete childEnv[key];
|
|
37
|
+
opts.env = childEnv;
|
|
38
|
+
}
|
|
39
|
+
return String(execFileSync(file, args, opts)).trim();
|
|
40
|
+
},
|
|
41
|
+
runQuiet(cmd, opts) {
|
|
42
|
+
try {
|
|
43
|
+
return this.run(cmd, opts);
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
runFileQuiet(file, args, opts) {
|
|
49
|
+
try {
|
|
50
|
+
return this.runFile(file, args, opts);
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
spawn(cmd, args = [], opts = {}) {
|
|
56
|
+
return spawn(cmd, args, opts);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function getExecutor() {
|
|
60
|
+
return active;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { getExecutor as t };
|