@socketsecurity/lib 5.8.0 → 5.8.2

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/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.8.2](https://github.com/SocketDev/socket-lib/releases/tag/v5.8.2) - 2026-03-13
9
+
10
+ ### Fixed
11
+
12
+ - **http-request**: Download to temp file then atomically rename to prevent corruption
13
+ - Downloads now write to `{destPath}.download` temp file first
14
+ - On success, atomically renames to the destination path
15
+ - On failure, cleans up temp file and preserves any existing file at destination
16
+ - Prevents partial/corrupted files from CI caching causing extraction failures
17
+
18
+ ## [5.8.1](https://github.com/SocketDev/socket-lib/releases/tag/v5.8.1) - 2026-03-11
19
+
20
+ ### Performance
21
+
22
+ - **windows**: Add comprehensive caching for expensive PATH resolution operations
23
+ - `getBinPath()`, `getBinPathSync()`: Cache binary path lookups
24
+ - `findRealBin()`: Cache `all:true` lookups and use single `whichSync({ all: true })` call
25
+ - `getVoltaBinPath()`: Cache Volta binary resolution
26
+ - `spawn()`: Cache binary path resolution before spawning
27
+ - `getGitPath()`: Cache git binary path
28
+ - `getCachedRealpath()`: New helper caching `realpathSync()` calls for git operations
29
+ - `findGitRoot()`: Cache git root directory lookups
30
+ - `findPackageJson()`: Cache package.json path lookups
31
+ - `readPackageJson()`: Cache parsed package.json content
32
+ - `resolveBinaryPath()`: Cache binary path resolution with Windows extension handling
33
+ - `NPM_BIN_PATH`, `NPM_REAL_EXEC_PATH`: Share npm path resolution to avoid duplicate `which.sync()` calls
34
+ - `ProcessLockManager.isStale()`: Use single `statSync({ throwIfNoEntry: false })` instead of `existsSync()` + `statSync()`
35
+ - All caches validate entries with `existsSync()` and remove stale entries automatically
36
+
8
37
  ## [5.8.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.8.0) - 2026-03-10
9
38
 
10
39
  ### Added
package/dist/bin.js CHANGED
@@ -50,6 +50,9 @@ var import_which = __toESM(require("./external/which"));
50
50
  var import_fs = require("./fs");
51
51
  var import_normalize = require("./paths/normalize");
52
52
  var import_spawn = require("./spawn");
53
+ const binPathCache = /* @__PURE__ */ new Map();
54
+ const binPathAllCache = /* @__PURE__ */ new Map();
55
+ const voltaBinCache = /* @__PURE__ */ new Map();
53
56
  let _fs;
54
57
  // @__NO_SIDE_EFFECTS__
55
58
  function getFs() {
@@ -68,7 +71,25 @@ function getPath() {
68
71
  }
69
72
  // @__NO_SIDE_EFFECTS__
70
73
  async function execBin(binPath, args, options) {
71
- const resolvedPath = (0, import_normalize.isPath)(binPath) ? /* @__PURE__ */ resolveRealBinSync(binPath) : await whichReal(binPath);
74
+ let resolvedPath;
75
+ if ((0, import_normalize.isPath)(binPath)) {
76
+ resolvedPath = /* @__PURE__ */ resolveRealBinSync(binPath);
77
+ } else {
78
+ const cached = binPathCache.get(binPath);
79
+ if (cached) {
80
+ if ((/* @__PURE__ */ getFs()).existsSync(cached)) {
81
+ resolvedPath = cached;
82
+ } else {
83
+ binPathCache.delete(binPath);
84
+ }
85
+ }
86
+ if (!resolvedPath) {
87
+ resolvedPath = await whichReal(binPath);
88
+ if (typeof resolvedPath === "string") {
89
+ binPathCache.set(binPath, resolvedPath);
90
+ }
91
+ }
92
+ }
72
93
  if (!resolvedPath) {
73
94
  const error = new Error(
74
95
  `Binary not found: ${binPath}
@@ -93,27 +114,23 @@ To resolve:
93
114
  function findRealBin(binName, commonPaths = []) {
94
115
  const fs = /* @__PURE__ */ getFs();
95
116
  const path = /* @__PURE__ */ getPath();
96
- for (const binPath2 of commonPaths) {
97
- if (fs.existsSync(binPath2)) {
98
- return binPath2;
117
+ for (const binPath of commonPaths) {
118
+ if (fs.existsSync(binPath)) {
119
+ return binPath;
99
120
  }
100
121
  }
101
- const binPath = import_which.default.sync(binName, { nothrow: true });
102
- if (binPath) {
122
+ const allPaths = import_which.default.sync(binName, { all: true, nothrow: true }) || [];
123
+ const pathsArray = Array.isArray(allPaths) ? allPaths : typeof allPaths === "string" ? [allPaths] : [];
124
+ if (pathsArray.length === 0) {
125
+ return void 0;
126
+ }
127
+ for (const binPath of pathsArray) {
103
128
  const binDir = path.dirname(binPath);
104
- if (isShadowBinPath(binDir)) {
105
- const allPaths = import_which.default.sync(binName, { all: true, nothrow: true }) || [];
106
- const pathsArray = Array.isArray(allPaths) ? allPaths : typeof allPaths === "string" ? [allPaths] : [];
107
- for (const altPath of pathsArray) {
108
- const altDir = path.dirname(altPath);
109
- if (!isShadowBinPath(altDir)) {
110
- return altPath;
111
- }
112
- }
129
+ if (!isShadowBinPath(binDir)) {
130
+ return binPath;
113
131
  }
114
- return binPath;
115
132
  }
116
- return void 0;
133
+ return pathsArray[0];
117
134
  }
118
135
  function findRealNpm() {
119
136
  const fs = /* @__PURE__ */ getFs();
@@ -199,6 +216,14 @@ function resolveRealBinSync(binPath) {
199
216
  const voltaIndex = basename === "node" ? -1 : /(?<=\/)\.volta\//i.exec(binPath)?.index ?? -1;
200
217
  if (voltaIndex !== -1) {
201
218
  const voltaPath = binPath.slice(0, voltaIndex);
219
+ const voltaCacheKey = `${voltaPath}:${basename}`;
220
+ const cachedVolta = voltaBinCache.get(voltaCacheKey);
221
+ if (cachedVolta) {
222
+ if (fs.existsSync(cachedVolta)) {
223
+ return cachedVolta;
224
+ }
225
+ voltaBinCache.delete(voltaCacheKey);
226
+ }
202
227
  const voltaToolsPath = path.join(voltaPath, "tools");
203
228
  const voltaImagePath = path.join(voltaToolsPath, "image");
204
229
  const voltaUserPath = path.join(voltaToolsPath, "user");
@@ -247,11 +272,13 @@ function resolveRealBinSync(binPath) {
247
272
  }
248
273
  }
249
274
  if (voltaBinPath) {
275
+ let resolvedVoltaPath = voltaBinPath;
250
276
  try {
251
- return (0, import_normalize.normalizePath)(fs.realpathSync.native(voltaBinPath));
277
+ resolvedVoltaPath = (0, import_normalize.normalizePath)(fs.realpathSync.native(voltaBinPath));
252
278
  } catch {
253
279
  }
254
- return voltaBinPath;
280
+ voltaBinCache.set(voltaCacheKey, resolvedVoltaPath);
281
+ return resolvedVoltaPath;
255
282
  }
256
283
  }
257
284
  if (import_platform.WIN32) {
@@ -392,28 +419,78 @@ async function which(binName, options) {
392
419
  }
393
420
  }
394
421
  async function whichReal(binName, options) {
422
+ const fs = /* @__PURE__ */ getFs();
395
423
  const opts = { nothrow: true, ...options };
424
+ if (opts.all) {
425
+ const cachedAll = binPathAllCache.get(binName);
426
+ if (cachedAll && cachedAll.length > 0) {
427
+ if (fs.existsSync(cachedAll[0])) {
428
+ return cachedAll;
429
+ }
430
+ binPathAllCache.delete(binName);
431
+ }
432
+ } else {
433
+ const cached = binPathCache.get(binName);
434
+ if (cached) {
435
+ if (fs.existsSync(cached)) {
436
+ return cached;
437
+ }
438
+ binPathCache.delete(binName);
439
+ }
440
+ }
396
441
  const result = await (0, import_which.default)(binName, opts);
397
442
  if (opts?.all) {
398
443
  const paths = Array.isArray(result) ? result : typeof result === "string" ? [result] : void 0;
399
- return paths?.length ? paths.map((p) => /* @__PURE__ */ resolveRealBinSync(p)) : paths;
444
+ if (paths?.length) {
445
+ const resolved2 = paths.map((p) => /* @__PURE__ */ resolveRealBinSync(p));
446
+ binPathAllCache.set(binName, resolved2);
447
+ return resolved2;
448
+ }
449
+ return paths;
400
450
  }
401
451
  if (!result) {
402
452
  return void 0;
403
453
  }
404
- return /* @__PURE__ */ resolveRealBinSync(result);
454
+ const resolved = /* @__PURE__ */ resolveRealBinSync(result);
455
+ binPathCache.set(binName, resolved);
456
+ return resolved;
405
457
  }
406
458
  function whichRealSync(binName, options) {
459
+ const fs = /* @__PURE__ */ getFs();
407
460
  const opts = { nothrow: true, ...options };
461
+ if (opts.all) {
462
+ const cachedAll = binPathAllCache.get(binName);
463
+ if (cachedAll && cachedAll.length > 0) {
464
+ if (fs.existsSync(cachedAll[0])) {
465
+ return cachedAll;
466
+ }
467
+ binPathAllCache.delete(binName);
468
+ }
469
+ } else {
470
+ const cached = binPathCache.get(binName);
471
+ if (cached) {
472
+ if (fs.existsSync(cached)) {
473
+ return cached;
474
+ }
475
+ binPathCache.delete(binName);
476
+ }
477
+ }
408
478
  const result = whichSync(binName, opts);
409
479
  if (opts.all) {
410
480
  const paths = Array.isArray(result) ? result : typeof result === "string" ? [result] : void 0;
411
- return paths?.length ? paths.map((p) => /* @__PURE__ */ resolveRealBinSync(p)) : paths;
481
+ if (paths?.length) {
482
+ const resolved2 = paths.map((p) => /* @__PURE__ */ resolveRealBinSync(p));
483
+ binPathAllCache.set(binName, resolved2);
484
+ return resolved2;
485
+ }
486
+ return paths;
412
487
  }
413
488
  if (!result) {
414
489
  return void 0;
415
490
  }
416
- return /* @__PURE__ */ resolveRealBinSync(result);
491
+ const resolved = /* @__PURE__ */ resolveRealBinSync(result);
492
+ binPathCache.set(binName, resolved);
493
+ return resolved;
417
494
  }
418
495
  function whichSync(binName, options) {
419
496
  if ((0, import_normalize.isPath)(binName)) {
@@ -5,7 +5,6 @@ export declare const YARN = "yarn";
5
5
  export declare const BUN = "bun";
6
6
  export declare const VLT = "vlt";
7
7
  export declare const NPX = "npx";
8
- // NPM binary path - resolved at runtime using which.
9
8
  export declare const NPM_BIN_PATH: string;
10
9
  // NPM CLI entry point - resolved at runtime from npm bin location.
11
10
  // NOTE: This is kept for backward compatibility but NPM_BIN_PATH should be used instead
@@ -61,22 +61,22 @@ const YARN = "yarn";
61
61
  const BUN = "bun";
62
62
  const VLT = "vlt";
63
63
  const NPX = "npx";
64
- const NPM_BIN_PATH = /* @__PURE__ */ (() => {
64
+ const _npmBinPath = /* @__PURE__ */ (() => {
65
65
  try {
66
- return import_which.default.sync("npm", { nothrow: true }) || "npm";
66
+ return import_which.default.sync("npm", { nothrow: true }) || null;
67
67
  } catch {
68
- return "npm";
68
+ return null;
69
69
  }
70
70
  })();
71
+ const NPM_BIN_PATH = _npmBinPath || "npm";
71
72
  const NPM_REAL_EXEC_PATH = /* @__PURE__ */ (() => {
72
73
  try {
73
- const { existsSync } = require("fs");
74
- const path = require("path");
75
- const npmBin = import_which.default.sync("npm", { nothrow: true });
76
- if (!npmBin) {
74
+ if (!_npmBinPath) {
77
75
  return void 0;
78
76
  }
79
- const npmDir = path.dirname(npmBin);
77
+ const { existsSync } = require("fs");
78
+ const path = require("path");
79
+ const npmDir = path.dirname(_npmBinPath);
80
80
  const nodeModulesPath = path.join(
81
81
  npmDir,
82
82
  "..",
@@ -46,20 +46,50 @@ function getPath() {
46
46
  return _path;
47
47
  }
48
48
  const NODE_JS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs"]);
49
+ const packageJsonPathCache = /* @__PURE__ */ new Map();
50
+ const packageJsonContentCache = /* @__PURE__ */ new Map();
49
51
  function findPackageJson(filePath) {
50
52
  const fs = /* @__PURE__ */ getFs();
51
53
  const path = /* @__PURE__ */ getPath();
52
- let currentDir = path.dirname(path.resolve(filePath));
54
+ const startDir = path.dirname(path.resolve(filePath));
55
+ const cached = packageJsonPathCache.get(startDir);
56
+ if (cached !== void 0) {
57
+ if (cached === null) {
58
+ return void 0;
59
+ }
60
+ if (fs.existsSync(cached)) {
61
+ return cached;
62
+ }
63
+ packageJsonPathCache.delete(startDir);
64
+ }
65
+ let currentDir = startDir;
53
66
  const root = path.parse(currentDir).root;
54
67
  while (currentDir !== root) {
55
68
  const packageJsonPath = path.join(currentDir, "package.json");
56
69
  if (fs.existsSync(packageJsonPath)) {
70
+ packageJsonPathCache.set(startDir, packageJsonPath);
57
71
  return packageJsonPath;
58
72
  }
59
73
  currentDir = path.dirname(currentDir);
60
74
  }
75
+ packageJsonPathCache.set(startDir, null);
61
76
  return void 0;
62
77
  }
78
+ function readPackageJson(packageJsonPath) {
79
+ const fs = /* @__PURE__ */ getFs();
80
+ const cached = packageJsonContentCache.get(packageJsonPath);
81
+ if (cached !== void 0) {
82
+ return cached;
83
+ }
84
+ try {
85
+ const content = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
86
+ packageJsonContentCache.set(packageJsonPath, content);
87
+ return content;
88
+ } catch {
89
+ packageJsonContentCache.set(packageJsonPath, null);
90
+ return null;
91
+ }
92
+ }
63
93
  function detectDlxExecutableType(filePath) {
64
94
  const fs = /* @__PURE__ */ getFs();
65
95
  const path = /* @__PURE__ */ getPath();
@@ -88,20 +118,16 @@ function detectExecutableType(filePath) {
88
118
  return detectLocalExecutableType(filePath);
89
119
  }
90
120
  function detectLocalExecutableType(filePath) {
91
- const fs = /* @__PURE__ */ getFs();
92
121
  const packageJsonPath = findPackageJson(filePath);
93
122
  if (packageJsonPath !== void 0) {
94
- try {
95
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
96
- if (packageJson.bin) {
97
- return {
98
- type: "package",
99
- method: "package-json",
100
- packageJsonPath,
101
- inDlxCache: false
102
- };
103
- }
104
- } catch {
123
+ const packageJson = readPackageJson(packageJsonPath);
124
+ if (packageJson?.bin) {
125
+ return {
126
+ type: "package",
127
+ method: "package-json",
128
+ packageJsonPath,
129
+ inDlxCache: false
130
+ };
105
131
  }
106
132
  }
107
133
  if (isJsFilePath(filePath)) {
@@ -314,15 +314,24 @@ function parsePackageSpec(spec) {
314
314
  };
315
315
  }
316
316
  }
317
+ const binaryPathCache = /* @__PURE__ */ new Map();
317
318
  function resolveBinaryPath(basePath) {
318
319
  if (!import_platform.WIN32) {
319
320
  return basePath;
320
321
  }
321
322
  const fs = /* @__PURE__ */ getFs();
323
+ const cached = binaryPathCache.get(basePath);
324
+ if (cached) {
325
+ if (fs.existsSync(cached)) {
326
+ return cached;
327
+ }
328
+ binaryPathCache.delete(basePath);
329
+ }
322
330
  const extensions = [".cmd", ".bat", ".ps1", ".exe", ""];
323
331
  for (const ext of extensions) {
324
332
  const testPath = basePath + ext;
325
333
  if (fs.existsSync(testPath)) {
334
+ binaryPathCache.set(basePath, testPath);
326
335
  return testPath;
327
336
  }
328
337
  }
package/dist/git.js CHANGED
@@ -34,6 +34,7 @@ __export(git_exports, {
34
34
  isUnstagedSync: () => isUnstagedSync
35
35
  });
36
36
  module.exports = __toCommonJS(git_exports);
37
+ var import_bin = require("./bin");
37
38
  var import_debug = require("./debug");
38
39
  var import_globs = require("./globs");
39
40
  var import_normalize = require("./paths/normalize");
@@ -66,8 +67,28 @@ function evictLRUGitCache() {
66
67
  }
67
68
  }
68
69
  }
70
+ let _gitPath;
71
+ const realpathCache = /* @__PURE__ */ new Map();
72
+ const gitRootCache = /* @__PURE__ */ new Map();
73
+ function getCachedRealpath(pathname) {
74
+ const fs = /* @__PURE__ */ getFs();
75
+ const cached = realpathCache.get(pathname);
76
+ if (cached) {
77
+ if (fs.existsSync(cached)) {
78
+ return cached;
79
+ }
80
+ realpathCache.delete(pathname);
81
+ }
82
+ const resolved = fs.realpathSync(pathname);
83
+ realpathCache.set(pathname, resolved);
84
+ return resolved;
85
+ }
69
86
  function getGitPath() {
70
- return "git";
87
+ if (_gitPath === void 0) {
88
+ const resolved = (0, import_bin.whichSync)("git", { nothrow: true });
89
+ _gitPath = typeof resolved === "string" ? resolved : "git";
90
+ }
91
+ return _gitPath;
71
92
  }
72
93
  function getCwd() {
73
94
  return (/* @__PURE__ */ getFs()).realpathSync(process.cwd());
@@ -165,11 +186,19 @@ function innerDiffSync(args, options) {
165
186
  function findGitRoot(startPath) {
166
187
  const fs = /* @__PURE__ */ getFs();
167
188
  const path = /* @__PURE__ */ getPath();
189
+ const cached = gitRootCache.get(startPath);
190
+ if (cached) {
191
+ if (fs.existsSync(path.join(cached, ".git"))) {
192
+ return cached;
193
+ }
194
+ gitRootCache.delete(startPath);
195
+ }
168
196
  let currentPath = startPath;
169
197
  while (true) {
170
198
  try {
171
199
  const gitPath = path.join(currentPath, ".git");
172
200
  if (fs.existsSync(gitPath)) {
201
+ gitRootCache.set(startPath, currentPath);
173
202
  return currentPath;
174
203
  }
175
204
  } catch {
@@ -189,9 +218,8 @@ function parseGitDiffStdout(stdout, options, spawnCwd) {
189
218
  porcelain = false,
190
219
  ...matcherOptions
191
220
  } = { __proto__: null, ...options };
192
- const fs = /* @__PURE__ */ getFs();
193
221
  const path = /* @__PURE__ */ getPath();
194
- const cwd = cwdOption === defaultRoot ? defaultRoot : fs.realpathSync(cwdOption);
222
+ const cwd = cwdOption === defaultRoot ? defaultRoot : getCachedRealpath(cwdOption);
195
223
  const rootPath = defaultRoot;
196
224
  let rawFiles = stdout ? (0, import_strings.stripAnsi)(stdout).split("\n").map((line) => line.trimEnd()).filter((line) => line) : [];
197
225
  if (porcelain) {
@@ -255,10 +283,9 @@ async function isChanged(pathname, options) {
255
283
  ...options,
256
284
  absolute: false
257
285
  });
258
- const fs = /* @__PURE__ */ getFs();
259
286
  const path = /* @__PURE__ */ getPath();
260
- const resolvedPathname = fs.realpathSync(pathname);
261
- const baseCwd = options?.cwd ? fs.realpathSync(options["cwd"]) : getCwd();
287
+ const resolvedPathname = getCachedRealpath(pathname);
288
+ const baseCwd = options?.cwd ? getCachedRealpath(options["cwd"]) : getCwd();
262
289
  const relativePath = (0, import_normalize.normalizePath)(path.relative(baseCwd, resolvedPathname));
263
290
  return files.includes(relativePath);
264
291
  }
@@ -268,11 +295,10 @@ function isChangedSync(pathname, options) {
268
295
  ...options,
269
296
  absolute: false
270
297
  });
271
- const fs = /* @__PURE__ */ getFs();
272
298
  const path = /* @__PURE__ */ getPath();
273
299
  try {
274
- const resolvedPathname = fs.realpathSync(pathname);
275
- const baseCwd = options?.cwd ? fs.realpathSync(options["cwd"]) : getCwd();
300
+ const resolvedPathname = getCachedRealpath(pathname);
301
+ const baseCwd = options?.cwd ? getCachedRealpath(options["cwd"]) : getCwd();
276
302
  const relativePath = (0, import_normalize.normalizePath)(path.relative(baseCwd, resolvedPathname));
277
303
  return files.includes(relativePath);
278
304
  } catch {
@@ -285,10 +311,9 @@ async function isUnstaged(pathname, options) {
285
311
  ...options,
286
312
  absolute: false
287
313
  });
288
- const fs = /* @__PURE__ */ getFs();
289
314
  const path = /* @__PURE__ */ getPath();
290
- const resolvedPathname = fs.realpathSync(pathname);
291
- const baseCwd = options?.cwd ? fs.realpathSync(options["cwd"]) : getCwd();
315
+ const resolvedPathname = getCachedRealpath(pathname);
316
+ const baseCwd = options?.cwd ? getCachedRealpath(options["cwd"]) : getCwd();
292
317
  const relativePath = (0, import_normalize.normalizePath)(path.relative(baseCwd, resolvedPathname));
293
318
  return files.includes(relativePath);
294
319
  }
@@ -298,10 +323,9 @@ function isUnstagedSync(pathname, options) {
298
323
  ...options,
299
324
  absolute: false
300
325
  });
301
- const fs = /* @__PURE__ */ getFs();
302
326
  const path = /* @__PURE__ */ getPath();
303
- const resolvedPathname = fs.realpathSync(pathname);
304
- const baseCwd = options?.cwd ? fs.realpathSync(options["cwd"]) : getCwd();
327
+ const resolvedPathname = getCachedRealpath(pathname);
328
+ const baseCwd = options?.cwd ? getCachedRealpath(options["cwd"]) : getCwd();
305
329
  const relativePath = (0, import_normalize.normalizePath)(path.relative(baseCwd, resolvedPathname));
306
330
  return files.includes(relativePath);
307
331
  }
@@ -311,10 +335,9 @@ async function isStaged(pathname, options) {
311
335
  ...options,
312
336
  absolute: false
313
337
  });
314
- const fs = /* @__PURE__ */ getFs();
315
338
  const path = /* @__PURE__ */ getPath();
316
- const resolvedPathname = fs.realpathSync(pathname);
317
- const baseCwd = options?.cwd ? fs.realpathSync(options["cwd"]) : getCwd();
339
+ const resolvedPathname = getCachedRealpath(pathname);
340
+ const baseCwd = options?.cwd ? getCachedRealpath(options["cwd"]) : getCwd();
318
341
  const relativePath = (0, import_normalize.normalizePath)(path.relative(baseCwd, resolvedPathname));
319
342
  return files.includes(relativePath);
320
343
  }
@@ -324,10 +347,9 @@ function isStagedSync(pathname, options) {
324
347
  ...options,
325
348
  absolute: false
326
349
  });
327
- const fs = /* @__PURE__ */ getFs();
328
350
  const path = /* @__PURE__ */ getPath();
329
- const resolvedPathname = fs.realpathSync(pathname);
330
- const baseCwd = options?.cwd ? fs.realpathSync(options["cwd"]) : getCwd();
351
+ const resolvedPathname = getCachedRealpath(pathname);
352
+ const baseCwd = options?.cwd ? getCachedRealpath(options["cwd"]) : getCwd();
331
353
  const relativePath = (0, import_normalize.normalizePath)(path.relative(baseCwd, resolvedPathname));
332
354
  return files.includes(relativePath);
333
355
  }
@@ -25,6 +25,7 @@ __export(http_request_exports, {
25
25
  httpText: () => httpText
26
26
  });
27
27
  module.exports = __toCommonJS(http_request_exports);
28
+ var import_fs = require("./fs.js");
28
29
  let _fs;
29
30
  // @__NO_SIDE_EFFECTS__
30
31
  function getFs() {
@@ -308,18 +309,31 @@ async function httpDownload(url, destPath, options) {
308
309
  }
309
310
  };
310
311
  }
312
+ const fs = /* @__PURE__ */ getFs();
313
+ const tempPath = `${destPath}.download`;
314
+ if (fs.existsSync(tempPath)) {
315
+ await (0, import_fs.safeDelete)(tempPath);
316
+ }
311
317
  let lastError;
312
318
  for (let attempt = 0; attempt <= retries; attempt++) {
313
319
  try {
314
- return await httpDownloadAttempt(url, destPath, {
320
+ const result = await httpDownloadAttempt(url, tempPath, {
315
321
  followRedirects,
316
322
  headers,
317
323
  maxRedirects,
318
324
  onProgress: progressCallback,
319
325
  timeout
320
326
  });
327
+ await fs.promises.rename(tempPath, destPath);
328
+ return {
329
+ path: destPath,
330
+ size: result.size
331
+ };
321
332
  } catch (e) {
322
333
  lastError = e;
334
+ if (fs.existsSync(tempPath)) {
335
+ await (0, import_fs.safeDelete)(tempPath);
336
+ }
323
337
  if (attempt === retries) {
324
338
  break;
325
339
  }
@@ -123,10 +123,10 @@ class ProcessLockManager {
123
123
  */
124
124
  isStale(lockPath, staleMs) {
125
125
  try {
126
- if (!existsSync(lockPath)) {
126
+ const stats = statSync(lockPath, { throwIfNoEntry: false });
127
+ if (!stats) {
127
128
  return false;
128
129
  }
129
- const stats = statSync(lockPath);
130
130
  const ageSeconds = Math.floor((Date.now() - stats.mtime.getTime()) / 1e3);
131
131
  const staleSeconds = Math.floor(staleMs / 1e3);
132
132
  return ageSeconds > staleSeconds;
@@ -169,7 +169,7 @@ class ProcessLockManager {
169
169
  return await (0, import_promises.pRetry)(
170
170
  async () => {
171
171
  try {
172
- if (existsSync(lockPath) && this.isStale(lockPath, staleMs)) {
172
+ if (this.isStale(lockPath, staleMs)) {
173
173
  logger.log(`Removing stale lock: ${lockPath}`);
174
174
  try {
175
175
  (0, import_fs.safeDeleteSync)(lockPath, { recursive: true });
package/dist/spawn.js CHANGED
@@ -53,9 +53,18 @@ function getPath() {
53
53
  }
54
54
  return _path;
55
55
  }
56
+ let _fs;
57
+ // @__NO_SIDE_EFFECTS__
58
+ function getFs() {
59
+ if (_fs === void 0) {
60
+ _fs = require("fs");
61
+ }
62
+ return _fs;
63
+ }
56
64
  const abortSignal = (0, import_process.getAbortSignal)();
57
65
  const spinner = (0, import_spinner.getDefaultSpinner)();
58
66
  const stackCache = /* @__PURE__ */ new WeakMap();
67
+ const spawnBinPathCache = /* @__PURE__ */ new Map();
59
68
  const windowsScriptExtRegExp = /\.(?:cmd|bat|ps1)$/i;
60
69
  let _child_process;
61
70
  // @__NO_SIDE_EFFECTS__
@@ -178,14 +187,28 @@ function spawn(cmd, args, options, extra) {
178
187
  const cwd = spawnOptions.cwd ? String(spawnOptions.cwd) : void 0;
179
188
  let actualCmd = cmd;
180
189
  if (!(0, import_normalize.isPath)(cmd)) {
181
- const resolved = (0, import_bin.whichSync)(cmd, { cwd, nothrow: true });
182
- if (resolved && typeof resolved === "string") {
183
- actualCmd = resolved;
190
+ const fs = /* @__PURE__ */ getFs();
191
+ const cached = spawnBinPathCache.get(cmd);
192
+ if (cached) {
193
+ if (fs.existsSync(cached)) {
194
+ actualCmd = cached;
195
+ } else {
196
+ spawnBinPathCache.delete(cmd);
197
+ }
198
+ }
199
+ if (actualCmd === cmd) {
200
+ const resolved = (0, import_bin.whichSync)(cmd, { cwd, nothrow: true });
201
+ if (resolved && typeof resolved === "string") {
202
+ actualCmd = resolved;
203
+ spawnBinPathCache.set(cmd, resolved);
204
+ }
184
205
  }
185
206
  }
186
207
  const WIN32 = process.platform === "win32";
187
208
  if (WIN32 && shell && windowsScriptExtRegExp.test(actualCmd)) {
188
- actualCmd = (/* @__PURE__ */ getPath()).basename(actualCmd, (/* @__PURE__ */ getPath()).extname(actualCmd));
209
+ if (!(0, import_normalize.isPath)(actualCmd)) {
210
+ actualCmd = (/* @__PURE__ */ getPath()).basename(actualCmd, (/* @__PURE__ */ getPath()).extname(actualCmd));
211
+ }
189
212
  }
190
213
  const wasSpinning = !!spinnerInstance?.isSpinning;
191
214
  const shouldStopSpinner = wasSpinning && !/* @__PURE__ */ isStdioType(stdio, "ignore") && !/* @__PURE__ */ isStdioType(stdio, "pipe");
@@ -269,7 +292,9 @@ function spawnSync(cmd, args, options) {
269
292
  const shell = (0, import_objects.getOwn)(options, "shell");
270
293
  const WIN32 = process.platform === "win32";
271
294
  if (WIN32 && shell && windowsScriptExtRegExp.test(actualCmd)) {
272
- actualCmd = (/* @__PURE__ */ getPath()).basename(actualCmd, (/* @__PURE__ */ getPath()).extname(actualCmd));
295
+ if (!(0, import_normalize.isPath)(actualCmd)) {
296
+ actualCmd = (/* @__PURE__ */ getPath()).basename(actualCmd, (/* @__PURE__ */ getPath()).extname(actualCmd));
297
+ }
273
298
  }
274
299
  const { stripAnsi: shouldStripAnsi = true, ...rawSpawnOptions } = {
275
300
  __proto__: null,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@socketsecurity/lib",
3
- "version": "5.8.0",
4
- "packageManager": "pnpm@10.32.0",
3
+ "version": "5.8.2",
4
+ "packageManager": "pnpm@10.32.1",
5
5
  "license": "MIT",
6
6
  "description": "Core utilities and infrastructure for Socket.dev security tools",
7
7
  "keywords": [
@@ -734,7 +734,7 @@
734
734
  "@socketregistry/is-unicode-supported": "1.0.5",
735
735
  "@socketregistry/packageurl-js": "1.3.5",
736
736
  "@socketregistry/yocto-spinner": "1.0.25",
737
- "@socketsecurity/lib-stable": "npm:@socketsecurity/lib@5.7.0",
737
+ "@socketsecurity/lib-stable": "npm:@socketsecurity/lib@5.8.1",
738
738
  "@types/node": "24.9.2",
739
739
  "@typescript/native-preview": "7.0.0-dev.20250920.1",
740
740
  "@vitest/coverage-v8": "4.0.3",
@@ -763,7 +763,7 @@
763
763
  "normalize-package-data": "8.0.0",
764
764
  "npm-package-arg": "13.0.0",
765
765
  "oxfmt": "^0.37.0",
766
- "oxlint": "^1.52.0",
766
+ "oxlint": "1.53.0",
767
767
  "pacote": "21.0.1",
768
768
  "picomatch": "2.3.1",
769
769
  "pony-cause": "2.1.11",
@@ -815,9 +815,9 @@
815
815
  "lru-cache": "11.2.2",
816
816
  "minimatch": "9.0.5",
817
817
  "minipass": "7.1.3",
818
- "minipass@7": "7.1.3",
819
818
  "minipass-fetch": "4.0.1",
820
819
  "minipass-sized": "1.0.3",
820
+ "minipass@7": "7.1.3",
821
821
  "minizlib": "3.1.0",
822
822
  "npm-package-arg": "12.0.2",
823
823
  "npm-pick-manifest": "10.0.0",