@socketsecurity/lib 5.6.0 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,69 @@ 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.7.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.7.0) - 2026-02-12
9
+
10
+ ### Added
11
+
12
+ - **env**: Added `isInEnv()` helper function to check if an environment variable key exists, regardless of its value
13
+ - Returns `true` even for empty strings, `"false"`, `"0"`, etc.
14
+ - Follows same override resolution order as `getEnvValue()` (isolated overrides → shared overrides → process.env)
15
+ - Useful for detecting presence of environment variables independent of their value
16
+
17
+ - **dlx**: Added new exported helper functions
18
+ - `downloadBinaryFile()` - Downloads a binary file from a URL to the dlx cache directory
19
+ - `ensurePackageInstalled()` - Ensures an npm package is installed and cached via Arborist
20
+ - `getBinaryCacheMetadataPath()` - Gets the file path to dlx binary cache metadata (`.dlx-metadata.json`)
21
+ - `isBinaryCacheValid()` - Checks if a cached dlx binary is still valid based on TTL and timestamp
22
+ - `makePackageBinsExecutable()` - Makes npm package binaries executable on Unix systems
23
+ - `parsePackageSpec()` - Parses npm package spec strings (e.g., `pkg@1.0.0`) into name and version
24
+ - `resolveBinaryPath()` - Resolves the absolute path to a binary within an installed package
25
+ - `writeBinaryCacheMetadata()` - Writes dlx binary cache metadata with integrity, size, and source info
26
+
27
+ - **releases**: Added `createAssetMatcher()` utility function for GitHub release asset pattern matching
28
+ - Creates matcher functions that test strings against glob patterns, prefix/suffix, or RegExp
29
+ - Used for dynamic asset discovery in GitHub releases (e.g., matching platform-specific binaries)
30
+
31
+ ### Changed
32
+
33
+ - **env**: Updated `getCI()` to use `isInEnv()` for more accurate CI detection
34
+ - Now returns `true` whenever the `CI` key exists in the environment, not just when truthy
35
+ - Matches standard CI detection behavior where the presence of the key (not its value) indicates a CI environment
36
+
37
+ ### Fixed
38
+
39
+ - **github**: Fixed JSON parsing crash vulnerability by adding try-catch around `JSON.parse()` in GitHub API responses
40
+ - Prevents crashes on malformed, incomplete, or binary responses
41
+ - Error messages now include the response URL for better debugging
42
+
43
+ - **dlx/binary**: Fixed clock skew vulnerabilities in cache validation
44
+ - Cache entries with future timestamps (clock skew) are now treated as expired
45
+ - Metadata writes now use atomic write-then-rename pattern to prevent corruption
46
+ - Added TOCTOU race protection by re-checking binary existence after metadata read
47
+
48
+ - **dlx/cache cleanup**: Fixed handling of future timestamps during cache cleanup
49
+ - Entries with future timestamps (due to clock skew) are now properly treated as expired
50
+
51
+ - **dlx/package**: Fixed scoped package parsing bug where `@scope/package` was incorrectly parsed
52
+ - Changed condition from `startsWith('@')` to `atIndex === 0` for more precise detection
53
+ - Fixes installation failures for scoped packages like `@socketregistry/lib`
54
+
55
+ - **cache-with-ttl**: Added clock skew detection to TTL cache
56
+ - Far-future `expiresAt` values (>2x TTL) are now treated as expired
57
+ - Protects against cache poisoning from clock skew
58
+
59
+ - **packages/specs**: Fixed unconditional `.git` truncation in Git URL parsing
60
+ - Now only removes `.git` suffix when URL actually ends with `.git`
61
+ - Prevents incorrect truncation of URLs containing `.git` in the middle
62
+
63
+ - **releases/github**: Fixed TOCTOU race condition in binary download verification
64
+ - Re-checks binary existence after reading version file
65
+ - Ensures binary is re-downloaded if missing despite version file presence
66
+
67
+ - **provenance**: Fixed incorrect package name in provenance workflow
68
+ - Changed from `@socketregistry/lib` to `@socketsecurity/lib`
69
+
70
+
8
71
  ## [5.6.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.6.0) - 2026-02-08
9
72
 
10
73
  ### Added
@@ -811,7 +874,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
811
874
 
812
875
  ### Changed
813
876
 
814
- - **DLX binary metadata structure**: Updated `writeMetadata()` to use unified schema with additional fields
877
+ - **DLX binary metadata structure**: Updated `writeBinaryCacheMetadata()` to use unified schema with additional fields
815
878
  - Now includes `cache_key` (first 16 chars of SHA-512 hash)
816
879
  - Added `size` field for cached binary size
817
880
  - Added `checksum_algorithm` field (currently "sha256")
@@ -54,7 +54,11 @@ function createTtlCache(options) {
54
54
  return `${opts.prefix}:${key}`;
55
55
  }
56
56
  function isExpired(entry) {
57
- return Date.now() > entry.expiresAt;
57
+ const now = Date.now();
58
+ if (entry.expiresAt > now + ttl * 2) {
59
+ return true;
60
+ }
61
+ return now > entry.expiresAt;
58
62
  }
59
63
  function createMatcher(pattern) {
60
64
  const fullPattern = buildKey(pattern);
@@ -123,6 +123,12 @@ export declare function downloadBinary(options: Omit<DlxBinaryOptions, 'spawnOpt
123
123
  binaryPath: string;
124
124
  downloaded: boolean;
125
125
  }>;
126
+ /**
127
+ * Download a file from a URL with integrity checking and concurrent download protection.
128
+ * Uses processLock to prevent multiple processes from downloading the same binary simultaneously.
129
+ * Internal helper function for downloading binary files.
130
+ */
131
+ export declare function downloadBinaryFile(url: string, destPath: string, integrity?: string | undefined): Promise<string>;
126
132
  /**
127
133
  * Execute a cached binary without re-downloading.
128
134
  * Similar to executePackage from dlx-package.
@@ -141,6 +147,14 @@ export declare function executeBinary(binaryPath: string, args: readonly string[
141
147
  * Uses same directory as dlx-package for unified DLX storage.
142
148
  */
143
149
  export declare function getDlxCachePath(): string;
150
+ /**
151
+ * Get metadata file path for a cached binary.
152
+ */
153
+ export declare function getBinaryCacheMetadataPath(cacheEntryPath: string): string;
154
+ /**
155
+ * Check if a cached binary is still valid.
156
+ */
157
+ export declare function isBinaryCacheValid(cacheEntryPath: string, cacheTtl: number): Promise<boolean>;
144
158
  /**
145
159
  * Get information about cached binaries.
146
160
  */
@@ -151,3 +165,9 @@ export declare function listDlxCache(): Promise<Array<{
151
165
  size: number;
152
166
  url: string;
153
167
  }>>;
168
+ /**
169
+ * Write metadata for a cached binary.
170
+ * Uses unified schema shared with C++ decompressor and CLI dlxBinary.
171
+ * Schema documentation: See DlxMetadata interface in this file (exported).
172
+ */
173
+ export declare function writeBinaryCacheMetadata(cacheEntryPath: string, cacheKey: string, url: string, integrity: string, size: number): Promise<void>;
@@ -22,9 +22,13 @@ __export(binary_exports, {
22
22
  cleanDlxCache: () => cleanDlxCache,
23
23
  dlxBinary: () => dlxBinary,
24
24
  downloadBinary: () => downloadBinary,
25
+ downloadBinaryFile: () => downloadBinaryFile,
25
26
  executeBinary: () => executeBinary,
27
+ getBinaryCacheMetadataPath: () => getBinaryCacheMetadataPath,
26
28
  getDlxCachePath: () => getDlxCachePath,
27
- listDlxCache: () => listDlxCache
29
+ isBinaryCacheValid: () => isBinaryCacheValid,
30
+ listDlxCache: () => listDlxCache,
31
+ writeBinaryCacheMetadata: () => writeBinaryCacheMetadata
28
32
  });
29
33
  module.exports = __toCommonJS(binary_exports);
30
34
  var import_platform = require("../constants/platform");
@@ -61,95 +65,6 @@ function getPath() {
61
65
  }
62
66
  return _path;
63
67
  }
64
- function getMetadataPath(cacheEntryPath) {
65
- return (/* @__PURE__ */ getPath()).join(cacheEntryPath, ".dlx-metadata.json");
66
- }
67
- async function isCacheValid(cacheEntryPath, cacheTtl) {
68
- const fs = /* @__PURE__ */ getFs();
69
- try {
70
- const metaPath = getMetadataPath(cacheEntryPath);
71
- if (!fs.existsSync(metaPath)) {
72
- return false;
73
- }
74
- const metadata = await (0, import_fs.readJson)(metaPath, { throws: false });
75
- if (!(0, import_objects.isObjectObject)(metadata)) {
76
- return false;
77
- }
78
- const now = Date.now();
79
- const timestamp = metadata["timestamp"];
80
- if (typeof timestamp !== "number" || timestamp <= 0) {
81
- return false;
82
- }
83
- const age = now - timestamp;
84
- return age < cacheTtl;
85
- } catch {
86
- return false;
87
- }
88
- }
89
- async function downloadBinaryFile(url, destPath, integrity) {
90
- const crypto = /* @__PURE__ */ getCrypto();
91
- const fs = /* @__PURE__ */ getFs();
92
- const path = /* @__PURE__ */ getPath();
93
- const cacheEntryDir = path.dirname(destPath);
94
- const lockPath = path.join(cacheEntryDir, "concurrency.lock");
95
- return await import_process_lock.processLock.withLock(
96
- lockPath,
97
- async () => {
98
- if (fs.existsSync(destPath)) {
99
- const stats = await fs.promises.stat(destPath);
100
- if (stats.size > 0) {
101
- const fileBuffer2 = await fs.promises.readFile(destPath);
102
- const hash2 = crypto.createHash("sha512").update(fileBuffer2).digest("base64");
103
- return `sha512-${hash2}`;
104
- }
105
- }
106
- try {
107
- await (0, import_http_request.httpDownload)(url, destPath);
108
- } catch (e) {
109
- throw new Error(
110
- `Failed to download binary from ${url}
111
- Destination: ${destPath}
112
- Check your internet connection or verify the URL is accessible.`,
113
- { cause: e }
114
- );
115
- }
116
- const fileBuffer = await fs.promises.readFile(destPath);
117
- const hash = crypto.createHash("sha512").update(fileBuffer).digest("base64");
118
- const actualIntegrity = `sha512-${hash}`;
119
- if (integrity && actualIntegrity !== integrity) {
120
- await (0, import_fs.safeDelete)(destPath);
121
- throw new Error(
122
- `Integrity mismatch: expected ${integrity}, got ${actualIntegrity}`
123
- );
124
- }
125
- if (!import_platform.WIN32) {
126
- await fs.promises.chmod(destPath, 493);
127
- }
128
- return actualIntegrity;
129
- },
130
- {
131
- // Align with npm npx locking strategy.
132
- staleMs: 5e3,
133
- touchIntervalMs: 2e3
134
- }
135
- );
136
- }
137
- async function writeMetadata(cacheEntryPath, cacheKey, url, integrity, size) {
138
- const metaPath = getMetadataPath(cacheEntryPath);
139
- const metadata = {
140
- version: "1.0.0",
141
- cache_key: cacheKey,
142
- timestamp: Date.now(),
143
- integrity,
144
- size,
145
- source: {
146
- type: "download",
147
- url
148
- }
149
- };
150
- const fs = /* @__PURE__ */ getFs();
151
- await fs.promises.writeFile(metaPath, JSON.stringify(metadata, null, 2));
152
- }
153
68
  async function cleanDlxCache(maxAge = import_time.DLX_BINARY_CACHE_TTL) {
154
69
  const cacheDir = getDlxCachePath();
155
70
  const fs = /* @__PURE__ */ getFs();
@@ -162,7 +77,7 @@ async function cleanDlxCache(maxAge = import_time.DLX_BINARY_CACHE_TTL) {
162
77
  const entries = await fs.promises.readdir(cacheDir);
163
78
  for (const entry of entries) {
164
79
  const entryPath = path.join(cacheDir, entry);
165
- const metaPath = getMetadataPath(entryPath);
80
+ const metaPath = getBinaryCacheMetadataPath(entryPath);
166
81
  try {
167
82
  if (!await (0, import_fs.isDir)(entryPath)) {
168
83
  continue;
@@ -173,7 +88,7 @@ async function cleanDlxCache(maxAge = import_time.DLX_BINARY_CACHE_TTL) {
173
88
  }
174
89
  const timestamp = metadata["timestamp"];
175
90
  const age = typeof timestamp === "number" && timestamp > 0 ? now - timestamp : Number.POSITIVE_INFINITY;
176
- if (age > maxAge) {
91
+ if (age < 0 || age > maxAge) {
177
92
  await (0, import_fs.safeDelete)(entryPath, { force: true, recursive: true });
178
93
  cleaned += 1;
179
94
  }
@@ -211,12 +126,15 @@ async function dlxBinary(args, options, spawnExtra) {
211
126
  const binaryPath = (0, import_normalize.normalizePath)(path.join(cacheEntryDir, binaryName));
212
127
  let downloaded = false;
213
128
  let computedIntegrity = integrity;
214
- if (!force && fs.existsSync(cacheEntryDir) && await isCacheValid(cacheEntryDir, cacheTtl)) {
129
+ if (!force && fs.existsSync(cacheEntryDir) && await isBinaryCacheValid(cacheEntryDir, cacheTtl)) {
215
130
  try {
216
- const metaPath = getMetadataPath(cacheEntryDir);
131
+ const metaPath = getBinaryCacheMetadataPath(cacheEntryDir);
217
132
  const metadata = await (0, import_fs.readJson)(metaPath, { throws: false });
218
133
  if (metadata && typeof metadata === "object" && !Array.isArray(metadata) && typeof metadata["integrity"] === "string") {
219
134
  computedIntegrity = metadata["integrity"];
135
+ if (!fs.existsSync(binaryPath)) {
136
+ downloaded = true;
137
+ }
220
138
  } else {
221
139
  downloaded = true;
222
140
  }
@@ -252,7 +170,7 @@ Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`
252
170
  }
253
171
  computedIntegrity = await downloadBinaryFile(url, binaryPath, integrity);
254
172
  const stats = await fs.promises.stat(binaryPath);
255
- await writeMetadata(
173
+ await writeBinaryCacheMetadata(
256
174
  cacheEntryDir,
257
175
  cacheKey,
258
176
  url,
@@ -293,7 +211,7 @@ async function downloadBinary(options) {
293
211
  const cacheEntryDir = path.join(cacheDir, cacheKey);
294
212
  const binaryPath = (0, import_normalize.normalizePath)(path.join(cacheEntryDir, binaryName));
295
213
  let downloaded = false;
296
- if (!force && fs.existsSync(cacheEntryDir) && await isCacheValid(cacheEntryDir, cacheTtl)) {
214
+ if (!force && fs.existsSync(cacheEntryDir) && await isBinaryCacheValid(cacheEntryDir, cacheTtl)) {
297
215
  downloaded = false;
298
216
  } else {
299
217
  try {
@@ -325,7 +243,7 @@ Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`
325
243
  integrity
326
244
  );
327
245
  const stats = await fs.promises.stat(binaryPath);
328
- await writeMetadata(
246
+ await writeBinaryCacheMetadata(
329
247
  cacheEntryDir,
330
248
  cacheKey,
331
249
  url,
@@ -339,6 +257,54 @@ Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`
339
257
  downloaded
340
258
  };
341
259
  }
260
+ async function downloadBinaryFile(url, destPath, integrity) {
261
+ const crypto = /* @__PURE__ */ getCrypto();
262
+ const fs = /* @__PURE__ */ getFs();
263
+ const path = /* @__PURE__ */ getPath();
264
+ const cacheEntryDir = path.dirname(destPath);
265
+ const lockPath = path.join(cacheEntryDir, "concurrency.lock");
266
+ return await import_process_lock.processLock.withLock(
267
+ lockPath,
268
+ async () => {
269
+ if (fs.existsSync(destPath)) {
270
+ const stats = await fs.promises.stat(destPath);
271
+ if (stats.size > 0) {
272
+ const fileBuffer2 = await fs.promises.readFile(destPath);
273
+ const hash2 = crypto.createHash("sha512").update(fileBuffer2).digest("base64");
274
+ return `sha512-${hash2}`;
275
+ }
276
+ }
277
+ try {
278
+ await (0, import_http_request.httpDownload)(url, destPath);
279
+ } catch (e) {
280
+ throw new Error(
281
+ `Failed to download binary from ${url}
282
+ Destination: ${destPath}
283
+ Check your internet connection or verify the URL is accessible.`,
284
+ { cause: e }
285
+ );
286
+ }
287
+ const fileBuffer = await fs.promises.readFile(destPath);
288
+ const hash = crypto.createHash("sha512").update(fileBuffer).digest("base64");
289
+ const actualIntegrity = `sha512-${hash}`;
290
+ if (integrity && actualIntegrity !== integrity) {
291
+ await (0, import_fs.safeDelete)(destPath);
292
+ throw new Error(
293
+ `Integrity mismatch: expected ${integrity}, got ${actualIntegrity}`
294
+ );
295
+ }
296
+ if (!import_platform.WIN32) {
297
+ await fs.promises.chmod(destPath, 493);
298
+ }
299
+ return actualIntegrity;
300
+ },
301
+ {
302
+ // Align with npm npx locking strategy.
303
+ staleMs: 5e3,
304
+ touchIntervalMs: 2e3
305
+ }
306
+ );
307
+ }
342
308
  function executeBinary(binaryPath, args, spawnOptions, spawnExtra) {
343
309
  const needsShell = import_platform.WIN32 && /\.(?:bat|cmd|ps1)$/i.test(binaryPath);
344
310
  const path = /* @__PURE__ */ getPath();
@@ -356,6 +322,34 @@ function executeBinary(binaryPath, args, spawnOptions, spawnExtra) {
356
322
  function getDlxCachePath() {
357
323
  return (0, import_socket.getSocketDlxDir)();
358
324
  }
325
+ function getBinaryCacheMetadataPath(cacheEntryPath) {
326
+ return (/* @__PURE__ */ getPath()).join(cacheEntryPath, ".dlx-metadata.json");
327
+ }
328
+ async function isBinaryCacheValid(cacheEntryPath, cacheTtl) {
329
+ const fs = /* @__PURE__ */ getFs();
330
+ try {
331
+ const metaPath = getBinaryCacheMetadataPath(cacheEntryPath);
332
+ if (!fs.existsSync(metaPath)) {
333
+ return false;
334
+ }
335
+ const metadata = await (0, import_fs.readJson)(metaPath, { throws: false });
336
+ if (!(0, import_objects.isObjectObject)(metadata)) {
337
+ return false;
338
+ }
339
+ const now = Date.now();
340
+ const timestamp = metadata["timestamp"];
341
+ if (typeof timestamp !== "number" || timestamp <= 0) {
342
+ return false;
343
+ }
344
+ const age = now - timestamp;
345
+ if (age < 0) {
346
+ return false;
347
+ }
348
+ return age < cacheTtl;
349
+ } catch {
350
+ return false;
351
+ }
352
+ }
359
353
  async function listDlxCache() {
360
354
  const cacheDir = getDlxCachePath();
361
355
  const fs = /* @__PURE__ */ getFs();
@@ -372,7 +366,7 @@ async function listDlxCache() {
372
366
  if (!await (0, import_fs.isDir)(entryPath)) {
373
367
  continue;
374
368
  }
375
- const metaPath = getMetadataPath(entryPath);
369
+ const metaPath = getBinaryCacheMetadataPath(entryPath);
376
370
  const metadata = await (0, import_fs.readJson)(metaPath, { throws: false });
377
371
  if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
378
372
  continue;
@@ -398,12 +392,34 @@ async function listDlxCache() {
398
392
  }
399
393
  return results;
400
394
  }
395
+ async function writeBinaryCacheMetadata(cacheEntryPath, cacheKey, url, integrity, size) {
396
+ const metaPath = getBinaryCacheMetadataPath(cacheEntryPath);
397
+ const metadata = {
398
+ version: "1.0.0",
399
+ cache_key: cacheKey,
400
+ timestamp: Date.now(),
401
+ integrity,
402
+ size,
403
+ source: {
404
+ type: "download",
405
+ url
406
+ }
407
+ };
408
+ const fs = /* @__PURE__ */ getFs();
409
+ const tmpPath = `${metaPath}.tmp.${process.pid}`;
410
+ await fs.promises.writeFile(tmpPath, JSON.stringify(metadata, null, 2));
411
+ await fs.promises.rename(tmpPath, metaPath);
412
+ }
401
413
  // Annotate the CommonJS export names for ESM import in node:
402
414
  0 && (module.exports = {
403
415
  cleanDlxCache,
404
416
  dlxBinary,
405
417
  downloadBinary,
418
+ downloadBinaryFile,
406
419
  executeBinary,
420
+ getBinaryCacheMetadataPath,
407
421
  getDlxCachePath,
408
- listDlxCache
422
+ isBinaryCacheValid,
423
+ listDlxCache,
424
+ writeBinaryCacheMetadata
409
425
  });
@@ -5,6 +5,14 @@ export interface ExecutableDetectionResult {
5
5
  packageJsonPath?: string;
6
6
  inDlxCache?: boolean;
7
7
  }
8
+ /**
9
+ * Detect executable type for paths in DLX cache.
10
+ * Uses filesystem structure (node_modules/ presence).
11
+ *
12
+ * @param filePath - Path within DLX cache (~/.socket/_dlx/)
13
+ * @returns Detection result
14
+ */
15
+ export declare function detectDlxExecutableType(filePath: string): ExecutableDetectionResult;
8
16
  /**
9
17
  * Detect if a path is a Node.js package or native binary executable.
10
18
  * Works for both DLX cache paths and local filesystem paths.
@@ -27,14 +35,6 @@ export interface ExecutableDetectionResult {
27
35
  * ```
28
36
  */
29
37
  export declare function detectExecutableType(filePath: string): ExecutableDetectionResult;
30
- /**
31
- * Detect executable type for paths in DLX cache.
32
- * Uses filesystem structure (node_modules/ presence).
33
- *
34
- * @param filePath - Path within DLX cache (~/.socket/_dlx/)
35
- * @returns Detection result
36
- */
37
- export declare function detectDlxExecutableType(filePath: string): ExecutableDetectionResult;
38
38
  /**
39
39
  * Detect executable type for local filesystem paths.
40
40
  * Uses package.json and file extension checks.
@@ -46,11 +46,19 @@ function getPath() {
46
46
  return _path;
47
47
  }
48
48
  const NODE_JS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs"]);
49
- function detectExecutableType(filePath) {
50
- if ((0, import_paths.isInSocketDlx)(filePath)) {
51
- return detectDlxExecutableType(filePath);
49
+ function findPackageJson(filePath) {
50
+ const fs = /* @__PURE__ */ getFs();
51
+ const path = /* @__PURE__ */ getPath();
52
+ let currentDir = path.dirname(path.resolve(filePath));
53
+ const root = path.parse(currentDir).root;
54
+ while (currentDir !== root) {
55
+ const packageJsonPath = path.join(currentDir, "package.json");
56
+ if (fs.existsSync(packageJsonPath)) {
57
+ return packageJsonPath;
58
+ }
59
+ currentDir = path.dirname(currentDir);
52
60
  }
53
- return detectLocalExecutableType(filePath);
61
+ return void 0;
54
62
  }
55
63
  function detectDlxExecutableType(filePath) {
56
64
  const fs = /* @__PURE__ */ getFs();
@@ -73,6 +81,12 @@ function detectDlxExecutableType(filePath) {
73
81
  inDlxCache: true
74
82
  };
75
83
  }
84
+ function detectExecutableType(filePath) {
85
+ if ((0, import_paths.isInSocketDlx)(filePath)) {
86
+ return detectDlxExecutableType(filePath);
87
+ }
88
+ return detectLocalExecutableType(filePath);
89
+ }
76
90
  function detectLocalExecutableType(filePath) {
77
91
  const fs = /* @__PURE__ */ getFs();
78
92
  const packageJsonPath = findPackageJson(filePath);
@@ -103,20 +117,6 @@ function detectLocalExecutableType(filePath) {
103
117
  inDlxCache: false
104
118
  };
105
119
  }
106
- function findPackageJson(filePath) {
107
- const fs = /* @__PURE__ */ getFs();
108
- const path = /* @__PURE__ */ getPath();
109
- let currentDir = path.dirname(path.resolve(filePath));
110
- const root = path.parse(currentDir).root;
111
- while (currentDir !== root) {
112
- const packageJsonPath = path.join(currentDir, "package.json");
113
- if (fs.existsSync(packageJsonPath)) {
114
- return packageJsonPath;
115
- }
116
- currentDir = path.dirname(currentDir);
117
- }
118
- return void 0;
119
- }
120
120
  function isJsFilePath(filePath) {
121
121
  const path = /* @__PURE__ */ getPath();
122
122
  const ext = path.extname(filePath).toLowerCase();
@@ -41,18 +41,6 @@ export interface ManifestEntry {
41
41
  timestamp: number;
42
42
  details: PackageDetails | BinaryDetails;
43
43
  }
44
- /**
45
- * Type guard for package entries.
46
- */
47
- export declare function isPackageEntry(entry: ManifestEntry): entry is ManifestEntry & {
48
- details: PackageDetails;
49
- };
50
- /**
51
- * Type guard for binary entries.
52
- */
53
- export declare function isBinaryEntry(entry: ManifestEntry): entry is ManifestEntry & {
54
- details: BinaryDetails;
55
- };
56
44
  /**
57
45
  * Legacy store record format (deprecated, for migration).
58
46
  */
@@ -67,6 +55,18 @@ export interface DlxManifestOptions {
67
55
  */
68
56
  manifestPath?: string;
69
57
  }
58
+ /**
59
+ * Type guard for binary entries.
60
+ */
61
+ export declare function isBinaryEntry(entry: ManifestEntry): entry is ManifestEntry & {
62
+ details: BinaryDetails;
63
+ };
64
+ /**
65
+ * Type guard for package entries.
66
+ */
67
+ export declare function isPackageEntry(entry: ManifestEntry): entry is ManifestEntry & {
68
+ details: PackageDetails;
69
+ };
70
70
  /**
71
71
  * DLX manifest storage manager with atomic operations.
72
72
  * Supports both legacy format (package name keys) and new unified manifest format (spec keys).
@@ -77,47 +77,48 @@ export declare class DlxManifest {
77
77
  constructor(options?: DlxManifestOptions);
78
78
  /**
79
79
  * Read the entire manifest file.
80
+ * @private
80
81
  */
81
82
  private readManifest;
83
+ private writeManifest;
82
84
  /**
83
- * Get a manifest entry by spec (e.g., "@socketsecurity/cli@^2.0.11").
85
+ * Clear cached data for a specific entry.
84
86
  */
85
- getManifestEntry(spec: string): ManifestEntry | undefined;
87
+ clear(name: string): Promise<void>;
88
+ /**
89
+ * Clear all cached data.
90
+ */
91
+ clearAll(): Promise<void>;
86
92
  /**
87
93
  * Get cached update information for a package (legacy format).
88
94
  * @deprecated Use getManifestEntry() for new code.
89
95
  */
90
96
  get(name: string): StoreRecord | undefined;
91
97
  /**
92
- * Set a package manifest entry.
98
+ * Get all cached package names.
93
99
  */
94
- setPackageEntry(spec: string, cacheKey: string, details: PackageDetails): Promise<void>;
100
+ getAllPackages(): string[];
95
101
  /**
96
- * Set a binary manifest entry.
102
+ * Get a manifest entry by spec (e.g., "@socketsecurity/cli@^2.0.11").
97
103
  */
98
- setBinaryEntry(spec: string, cacheKey: string, details: BinaryDetails): Promise<void>;
99
- private writeManifest;
104
+ getManifestEntry(spec: string): ManifestEntry | undefined;
105
+ /**
106
+ * Check if cached data is fresh based on TTL.
107
+ */
108
+ isFresh(record: StoreRecord | undefined, ttlMs: number): boolean;
100
109
  /**
101
110
  * Store update information for a package (legacy format).
102
111
  * @deprecated Use setPackageEntry() for new code.
103
112
  */
104
113
  set(name: string, record: StoreRecord): Promise<void>;
105
114
  /**
106
- * Clear cached data for a specific entry.
107
- */
108
- clear(name: string): Promise<void>;
109
- /**
110
- * Clear all cached data.
111
- */
112
- clearAll(): Promise<void>;
113
- /**
114
- * Check if cached data is fresh based on TTL.
115
+ * Set a binary manifest entry.
115
116
  */
116
- isFresh(record: StoreRecord | undefined, ttlMs: number): boolean;
117
+ setBinaryEntry(spec: string, cacheKey: string, details: BinaryDetails): Promise<void>;
117
118
  /**
118
- * Get all cached package names.
119
+ * Set a package manifest entry.
119
120
  */
120
- getAllPackages(): string[];
121
+ setPackageEntry(spec: string, cacheKey: string, details: PackageDetails): Promise<void>;
121
122
  }
122
123
  // Export singleton instance using default manifest location.
123
124
  export declare const dlxManifest: DlxManifest;