@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 +64 -1
- package/dist/cache-with-ttl.js +5 -1
- package/dist/dlx/binary.d.ts +20 -0
- package/dist/dlx/binary.js +115 -99
- package/dist/dlx/detect.d.ts +8 -8
- package/dist/dlx/detect.js +18 -18
- package/dist/dlx/manifest.d.ts +32 -31
- package/dist/dlx/manifest.js +114 -112
- package/dist/dlx/package.d.ts +55 -0
- package/dist/dlx/package.js +89 -79
- package/dist/env/ci.js +1 -2
- package/dist/env/rewire.d.ts +33 -22
- package/dist/env/rewire.js +20 -7
- package/dist/env/socket-cli.d.ts +24 -24
- package/dist/env/socket-cli.js +12 -12
- package/dist/env/temp-dir.d.ts +6 -6
- package/dist/env/temp-dir.js +4 -4
- package/dist/env/windows.d.ts +6 -6
- package/dist/env/windows.js +4 -4
- package/dist/github.js +10 -1
- package/dist/packages/specs.js +1 -1
- package/dist/releases/github.d.ts +18 -7
- package/dist/releases/github.js +94 -92
- package/package.json +1 -1
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 `
|
|
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")
|
package/dist/cache-with-ttl.js
CHANGED
|
@@ -54,7 +54,11 @@ function createTtlCache(options) {
|
|
|
54
54
|
return `${opts.prefix}:${key}`;
|
|
55
55
|
}
|
|
56
56
|
function isExpired(entry) {
|
|
57
|
-
|
|
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);
|
package/dist/dlx/binary.d.ts
CHANGED
|
@@ -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>;
|
package/dist/dlx/binary.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
|
129
|
+
if (!force && fs.existsSync(cacheEntryDir) && await isBinaryCacheValid(cacheEntryDir, cacheTtl)) {
|
|
215
130
|
try {
|
|
216
|
-
const metaPath =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
422
|
+
isBinaryCacheValid,
|
|
423
|
+
listDlxCache,
|
|
424
|
+
writeBinaryCacheMetadata
|
|
409
425
|
});
|
package/dist/dlx/detect.d.ts
CHANGED
|
@@ -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.
|
package/dist/dlx/detect.js
CHANGED
|
@@ -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
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
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();
|
package/dist/dlx/manifest.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
85
|
+
* Clear cached data for a specific entry.
|
|
84
86
|
*/
|
|
85
|
-
|
|
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
|
-
*
|
|
98
|
+
* Get all cached package names.
|
|
93
99
|
*/
|
|
94
|
-
|
|
100
|
+
getAllPackages(): string[];
|
|
95
101
|
/**
|
|
96
|
-
*
|
|
102
|
+
* Get a manifest entry by spec (e.g., "@socketsecurity/cli@^2.0.11").
|
|
97
103
|
*/
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
117
|
+
setBinaryEntry(spec: string, cacheKey: string, details: BinaryDetails): Promise<void>;
|
|
117
118
|
/**
|
|
118
|
-
*
|
|
119
|
+
* Set a package manifest entry.
|
|
119
120
|
*/
|
|
120
|
-
|
|
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;
|