@socketsecurity/lib 2.9.1 → 2.10.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 +20 -0
- package/dist/dlx-binary.d.ts +83 -0
- package/dist/dlx-binary.js +7 -7
- package/dist/dlx-binary.js.map +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ 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
|
+
## [2.10.0](https://github.com/SocketDev/socket-lib/releases/tag/v2.10.0) - 2025-10-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Unified DLX metadata schema**: Standardized `.dlx-metadata.json` format across TypeScript and C++ implementations
|
|
13
|
+
- Exported `DlxMetadata` interface as canonical schema reference
|
|
14
|
+
- Core fields: `version`, `cache_key`, `timestamp`, `checksum`, `checksum_algorithm`, `platform`, `arch`, `size`, `source`
|
|
15
|
+
- Support for `source` tracking (download vs decompression origin)
|
|
16
|
+
- Reserved `extra` field for implementation-specific data
|
|
17
|
+
- Comprehensive documentation with examples for both download and decompression use cases
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **DLX binary metadata structure**: Updated `writeMetadata()` to use unified schema with additional fields
|
|
22
|
+
- Now includes `cache_key` (first 16 chars of SHA-512 hash)
|
|
23
|
+
- Added `size` field for cached binary size
|
|
24
|
+
- Added `checksum_algorithm` field (currently "sha256")
|
|
25
|
+
- Restructured to use `source.type` and `source.url` for origin tracking
|
|
26
|
+
- Maintains backward compatibility in `listDlxCache()` reader
|
|
27
|
+
|
|
8
28
|
## [2.9.1](https://github.com/SocketDev/socket-lib/releases/tag/v2.9.1) - 2025-10-30
|
|
9
29
|
|
|
10
30
|
### Added
|
package/dist/dlx-binary.d.ts
CHANGED
|
@@ -22,6 +22,89 @@ export interface DlxBinaryResult {
|
|
|
22
22
|
/** The spawn promise for the running process. */
|
|
23
23
|
spawnPromise: ReturnType<typeof spawn>;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Metadata structure for cached binaries (.dlx-metadata.json).
|
|
27
|
+
* Unified schema shared across TypeScript (dlxBinary) and C++ (socket_macho_decompress).
|
|
28
|
+
*
|
|
29
|
+
* Core Fields (present in all implementations):
|
|
30
|
+
* - version: Schema version (currently "1.0.0")
|
|
31
|
+
* - cache_key: First 16 chars of SHA-512 hash (matches directory name)
|
|
32
|
+
* - timestamp: Unix timestamp in milliseconds
|
|
33
|
+
* - checksum: Full hash of cached binary (SHA-512 for C++, SHA-256 for TypeScript)
|
|
34
|
+
* - checksum_algorithm: "sha512" or "sha256"
|
|
35
|
+
* - platform: "darwin" | "linux" | "win32"
|
|
36
|
+
* - arch: "x64" | "arm64"
|
|
37
|
+
* - size: Size of cached binary in bytes
|
|
38
|
+
* - source: Origin information
|
|
39
|
+
* - type: "download" (from URL) or "decompression" (from embedded binary)
|
|
40
|
+
* - url: Download URL (if type is "download")
|
|
41
|
+
* - path: Source binary path (if type is "decompression")
|
|
42
|
+
*
|
|
43
|
+
* Extra Fields (implementation-specific):
|
|
44
|
+
* - For C++ decompression:
|
|
45
|
+
* - compressed_size: Size of compressed data in bytes
|
|
46
|
+
* - compression_algorithm: Brotli level (numeric)
|
|
47
|
+
* - compression_ratio: original_size / compressed_size
|
|
48
|
+
*
|
|
49
|
+
* Example (TypeScript download):
|
|
50
|
+
* ```json
|
|
51
|
+
* {
|
|
52
|
+
* "version": "1.0.0",
|
|
53
|
+
* "cache_key": "a1b2c3d4e5f67890",
|
|
54
|
+
* "timestamp": 1730332800000,
|
|
55
|
+
* "checksum": "sha256-abc123...",
|
|
56
|
+
* "checksum_algorithm": "sha256",
|
|
57
|
+
* "platform": "darwin",
|
|
58
|
+
* "arch": "arm64",
|
|
59
|
+
* "size": 15000000,
|
|
60
|
+
* "source": {
|
|
61
|
+
* "type": "download",
|
|
62
|
+
* "url": "https://example.com/binary"
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* Example (C++ decompression):
|
|
68
|
+
* ```json
|
|
69
|
+
* {
|
|
70
|
+
* "version": "1.0.0",
|
|
71
|
+
* "cache_key": "0123456789abcdef",
|
|
72
|
+
* "timestamp": 1730332800000,
|
|
73
|
+
* "checksum": "sha512-def456...",
|
|
74
|
+
* "checksum_algorithm": "sha512",
|
|
75
|
+
* "platform": "darwin",
|
|
76
|
+
* "arch": "arm64",
|
|
77
|
+
* "size": 13000000,
|
|
78
|
+
* "source": {
|
|
79
|
+
* "type": "decompression",
|
|
80
|
+
* "path": "/usr/local/bin/socket"
|
|
81
|
+
* },
|
|
82
|
+
* "extra": {
|
|
83
|
+
* "compressed_size": 1700000,
|
|
84
|
+
* "compression_algorithm": 3,
|
|
85
|
+
* "compression_ratio": 7.647
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @internal This interface documents the metadata file format.
|
|
91
|
+
*/
|
|
92
|
+
export interface DlxMetadata {
|
|
93
|
+
version: string;
|
|
94
|
+
cache_key: string;
|
|
95
|
+
timestamp: number;
|
|
96
|
+
checksum: string;
|
|
97
|
+
checksum_algorithm: string;
|
|
98
|
+
platform: string;
|
|
99
|
+
arch: string;
|
|
100
|
+
size: number;
|
|
101
|
+
source?: {
|
|
102
|
+
type: 'download' | 'decompression';
|
|
103
|
+
url?: string;
|
|
104
|
+
path?: string;
|
|
105
|
+
};
|
|
106
|
+
extra?: Record<string, unknown>;
|
|
107
|
+
}
|
|
25
108
|
/**
|
|
26
109
|
* Clean expired entries from the DLX cache.
|
|
27
110
|
*/
|
package/dist/dlx-binary.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* Socket Lib - Built with esbuild */
|
|
2
|
-
var
|
|
3
|
-
Destination: ${
|
|
4
|
-
Check your internet connection or verify the URL is accessible.`,{cause:
|
|
5
|
-
Please check directory permissions or run with appropriate access.`,{cause:
|
|
6
|
-
Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`,{cause:
|
|
7
|
-
Please check directory permissions or run with appropriate access.`,{cause:w}):
|
|
8
|
-
Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`,{cause:w}):new Error(`Failed to create binary cache directory: ${
|
|
2
|
+
var K=Object.create;var P=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var Y=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var V=(e,t)=>{for(var r in t)P(e,r,{get:t[r],enumerable:!0})},T=(e,t,r,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of J(t))!q.call(e,n)&&n!==r&&P(e,n,{get:()=>t[n],enumerable:!(i=X(t,n))||i.enumerable});return e};var j=(e,t,r)=>(r=e!=null?K(Y(e)):{},T(t||!e||!e.__esModule?P(r,"default",{value:e,enumerable:!0}):r,e)),W=e=>T(P({},"__esModule",{value:!0}),e);var et={};V(et,{cleanDlxCache:()=>U,dlxBinary:()=>G,downloadBinary:()=>Q,executeBinary:()=>Z,getDlxCachePath:()=>x,listDlxCache:()=>tt});module.exports=W(et);var S=require("node:crypto"),s=require("node:fs"),D=j(require("node:os")),f=j(require("node:path")),_=require("#constants/platform"),O=require("./dlx"),B=require("./http-request"),d=require("./fs"),v=require("./objects"),R=require("./path"),I=require("./paths"),N=require("./process-lock"),A=require("./spawn");function k(e){return f.default.join(e,".dlx-metadata.json")}async function F(e,t){try{const r=k(e);if(!(0,s.existsSync)(r))return!1;const i=await(0,d.readJson)(r,{throws:!1});if(!(0,v.isObjectObject)(i))return!1;const n=Date.now(),a=i.timestamp;return typeof a!="number"||a<=0?!1:n-a<t}catch{return!1}}async function L(e,t,r){const i=f.default.dirname(t),n=f.default.join(i,"concurrency.lock");return await N.processLock.withLock(n,async()=>{if((0,s.existsSync)(t)&&(await s.promises.stat(t)).size>0){const p=await s.promises.readFile(t),l=(0,S.createHash)("sha256");return l.update(p),l.digest("hex")}try{await(0,B.httpDownload)(e,t)}catch(o){throw new Error(`Failed to download binary from ${e}
|
|
3
|
+
Destination: ${t}
|
|
4
|
+
Check your internet connection or verify the URL is accessible.`,{cause:o})}const a=await s.promises.readFile(t),c=(0,S.createHash)("sha256");c.update(a);const u=c.digest("hex");if(r&&u!==r)throw await(0,d.safeDelete)(t),new Error(`Checksum mismatch: expected ${r}, got ${u}`);return _.WIN32||await s.promises.chmod(t,493),u},{staleMs:5e3,touchIntervalMs:2e3})}async function z(e,t,r,i,n){const a=k(e),c={version:"1.0.0",cache_key:t,timestamp:Date.now(),checksum:i,checksum_algorithm:"sha256",platform:D.default.platform(),arch:D.default.arch(),size:n,source:{type:"download",url:r}};await s.promises.writeFile(a,JSON.stringify(c,null,2))}async function U(e=require("#constants/time").DLX_BINARY_CACHE_TTL){const t=x();if(!(0,s.existsSync)(t))return 0;let r=0;const i=Date.now(),n=await s.promises.readdir(t);for(const a of n){const c=f.default.join(t,a),u=k(c);try{if(!await(0,d.isDir)(c))continue;const o=await(0,d.readJson)(u,{throws:!1});if(!o||typeof o!="object"||Array.isArray(o))continue;const p=o.timestamp;(typeof p=="number"&&p>0?i-p:Number.POSITIVE_INFINITY)>e&&(await(0,d.safeDelete)(c,{force:!0,recursive:!0}),r+=1)}catch{try{(await s.promises.readdir(c)).length||(await(0,d.safeDelete)(c),r+=1)}catch{}}}return r}async function G(e,t,r){const{cacheTtl:i=require("#constants/time").DLX_BINARY_CACHE_TTL,checksum:n,force:a=!1,name:c,spawnOptions:u,url:o}={__proto__:null,...t},p=x(),l=c||`binary-${process.platform}-${D.default.arch()}`,E=`${o}:${l}`,h=(0,O.generateCacheKey)(E),m=f.default.join(p,h),g=(0,R.normalizePath)(f.default.join(m,l));let w=!1,b=n;if(!a&&(0,s.existsSync)(m)&&await F(m,i))try{const $=k(m),y=await(0,d.readJson)($,{throws:!1});y&&typeof y=="object"&&!Array.isArray(y)&&typeof y.checksum=="string"?b=y.checksum:w=!0}catch{w=!0}else w=!0;if(w){try{await s.promises.mkdir(m,{recursive:!0})}catch(y){const C=y.code;throw C==="EACCES"||C==="EPERM"?new Error(`Permission denied creating binary cache directory: ${m}
|
|
5
|
+
Please check directory permissions or run with appropriate access.`,{cause:y}):C==="EROFS"?new Error(`Cannot create binary cache directory on read-only filesystem: ${m}
|
|
6
|
+
Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`,{cause:y}):new Error(`Failed to create binary cache directory: ${m}`,{cause:y})}b=await L(o,g,n);const $=await s.promises.stat(g);await z(m,h,o,b||"",$.size)}const H=_.WIN32&&/\.(?:bat|cmd|ps1)$/i.test(g)?{...u,env:{...u?.env,PATH:`${m}${f.default.delimiter}${process.env.PATH||""}`},shell:!0}:u,M=(0,A.spawn)(g,e,H,r);return{binaryPath:g,downloaded:w,spawnPromise:M}}async function Q(e){const{cacheTtl:t=require("#constants/time").DLX_BINARY_CACHE_TTL,checksum:r,force:i=!1,name:n,url:a}={__proto__:null,...e},c=x(),u=n||`binary-${process.platform}-${D.default.arch()}`,o=`${a}:${u}`,p=(0,O.generateCacheKey)(o),l=f.default.join(c,p),E=(0,R.normalizePath)(f.default.join(l,u));let h=!1;if(!i&&(0,s.existsSync)(l)&&await F(l,t))h=!1;else{try{await s.promises.mkdir(l,{recursive:!0})}catch(w){const b=w.code;throw b==="EACCES"||b==="EPERM"?new Error(`Permission denied creating binary cache directory: ${l}
|
|
7
|
+
Please check directory permissions or run with appropriate access.`,{cause:w}):b==="EROFS"?new Error(`Cannot create binary cache directory on read-only filesystem: ${l}
|
|
8
|
+
Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`,{cause:w}):new Error(`Failed to create binary cache directory: ${l}`,{cause:w})}const m=await L(a,E,r),g=await s.promises.stat(E);await z(l,p,a,m||"",g.size),h=!0}return{binaryPath:E,downloaded:h}}function Z(e,t,r,i){const n=_.WIN32&&/\.(?:bat|cmd|ps1)$/i.test(e),a=f.default.dirname(e),c=n?{...r,env:{...r?.env,PATH:`${a}${f.default.delimiter}${process.env.PATH||""}`},shell:!0}:r;return(0,A.spawn)(e,t,c,i)}function x(){return(0,I.getSocketDlxDir)()}async function tt(){const e=x();if(!(0,s.existsSync)(e))return[];const t=[],r=Date.now(),i=await s.promises.readdir(e);for(const n of i){const a=f.default.join(e,n);try{if(!await(0,d.isDir)(a))continue;const c=k(a),u=await(0,d.readJson)(c,{throws:!1});if(!u||typeof u!="object"||Array.isArray(u))continue;const o=u,l=o.source?.url||o.url||"",h=(await s.promises.readdir(a)).find(m=>!m.startsWith("."));if(h){const m=f.default.join(a,h),g=await s.promises.stat(m);t.push({age:r-(o.timestamp||0),arch:o.arch||"unknown",checksum:o.checksum||"",name:h,platform:o.platform||"unknown",size:g.size,url:l})}}catch{}}return t}0&&(module.exports={cleanDlxCache,dlxBinary,downloadBinary,executeBinary,getDlxCachePath,listDlxCache});
|
|
9
9
|
//# sourceMappingURL=dlx-binary.js.map
|
package/dist/dlx-binary.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/dlx-binary.ts"],
|
|
4
|
-
"sourcesContent": ["/** @fileoverview DLX binary execution utilities for Socket ecosystem. */\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, promises as fs } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport { WIN32 } from '#constants/platform'\n\nimport { generateCacheKey } from './dlx'\nimport { httpDownload } from './http-request'\nimport { isDir, readJson, safeDelete } from './fs'\nimport { isObjectObject } from './objects'\nimport { normalizePath } from './path'\nimport { getSocketDlxDir } from './paths'\nimport { processLock } from './process-lock'\nimport type { SpawnExtra, SpawnOptions } from './spawn'\nimport { spawn } from './spawn'\n\nexport interface DlxBinaryOptions {\n /** URL to download the binary from. */\n url: string\n /** Optional name for the cached binary (defaults to URL hash). */\n name?: string | undefined\n /** Expected checksum (sha256) for verification. */\n checksum?: string | undefined\n /** Cache TTL in milliseconds (default: 7 days). */\n cacheTtl?: number | undefined\n /** Force re-download even if cached. */\n force?: boolean | undefined\n /** Additional spawn options. */\n spawnOptions?: SpawnOptions | undefined\n}\n\nexport interface DlxBinaryResult {\n /** Path to the cached binary. */\n binaryPath: string\n /** Whether the binary was newly downloaded. */\n downloaded: boolean\n /** The spawn promise for the running process. */\n spawnPromise: ReturnType<typeof spawn>\n}\n\n/**\n * Get metadata file path for a cached binary.\n */\nfunction getMetadataPath(cacheEntryPath: string): string {\n return path.join(cacheEntryPath, '.dlx-metadata.json')\n}\n\n/**\n * Check if a cached binary is still valid.\n */\nasync function isCacheValid(\n cacheEntryPath: string,\n cacheTtl: number,\n): Promise<boolean> {\n try {\n const metaPath = getMetadataPath(cacheEntryPath)\n if (!existsSync(metaPath)) {\n return false\n }\n\n const metadata = await readJson(metaPath, { throws: false })\n if (!isObjectObject(metadata)) {\n return false\n }\n const now = Date.now()\n const timestamp = (metadata as Record<string, unknown>)['timestamp']\n // If timestamp is missing or invalid, cache is invalid\n if (typeof timestamp !== 'number' || timestamp <= 0) {\n return false\n }\n const age = now - timestamp\n\n return age < cacheTtl\n } catch {\n return false\n }\n}\n\n/**\n * Download a file from a URL with integrity checking and concurrent download protection.\n * Uses processLock to prevent multiple processes from downloading the same binary simultaneously.\n * Internal helper function for downloading binary files.\n */\nasync function downloadBinaryFile(\n url: string,\n destPath: string,\n checksum?: string | undefined,\n): Promise<string> {\n // Use process lock to prevent concurrent downloads.\n // Lock is placed in the cache entry directory as 'concurrency.lock'.\n const cacheEntryDir = path.dirname(destPath)\n const lockPath = path.join(cacheEntryDir, 'concurrency.lock')\n\n return await processLock.withLock(\n lockPath,\n async () => {\n // Check if file was downloaded while waiting for lock.\n if (existsSync(destPath)) {\n const stats = await fs.stat(destPath)\n if (stats.size > 0) {\n // File exists, compute and return checksum.\n const fileBuffer = await fs.readFile(destPath)\n const hasher = createHash('sha256')\n hasher.update(fileBuffer)\n return hasher.digest('hex')\n }\n }\n\n // Download the file.\n try {\n await httpDownload(url, destPath)\n } catch (e) {\n throw new Error(\n `Failed to download binary from ${url}\\n` +\n `Destination: ${destPath}\\n` +\n 'Check your internet connection or verify the URL is accessible.',\n { cause: e },\n )\n }\n\n // Compute checksum of downloaded file.\n const fileBuffer = await fs.readFile(destPath)\n const hasher = createHash('sha256')\n hasher.update(fileBuffer)\n const actualChecksum = hasher.digest('hex')\n\n // Verify checksum if provided.\n if (checksum && actualChecksum !== checksum) {\n // Clean up invalid file.\n await safeDelete(destPath)\n throw new Error(\n `Checksum mismatch: expected ${checksum}, got ${actualChecksum}`,\n )\n }\n\n // Make executable on POSIX systems.\n if (!WIN32) {\n await fs.chmod(destPath, 0o755)\n }\n\n return actualChecksum\n },\n {\n // Align with npm npx locking strategy.\n staleMs: 5000,\n touchIntervalMs: 2000,\n },\n )\n}\n\n/**\n * Write metadata for a cached binary.\n */\nasync function writeMetadata(\n cacheEntryPath: string,\n url: string,\n checksum: string,\n): Promise<void> {\n const metaPath = getMetadataPath(cacheEntryPath)\n const metadata = {\n arch: os.arch(),\n checksum,\n platform: os.platform(),\n timestamp: Date.now(),\n url,\n version: '1.0.0',\n }\n await fs.writeFile(metaPath, JSON.stringify(metadata, null, 2))\n}\n\n/**\n * Clean expired entries from the DLX cache.\n */\nexport async function cleanDlxCache(\n maxAge: number = /*@__INLINE__*/ require('#constants/time').DLX_BINARY_CACHE_TTL,\n): Promise<number> {\n const cacheDir = getDlxCachePath()\n\n if (!existsSync(cacheDir)) {\n return 0\n }\n\n let cleaned = 0\n const now = Date.now()\n const entries = await fs.readdir(cacheDir)\n\n for (const entry of entries) {\n const entryPath = path.join(cacheDir, entry)\n const metaPath = getMetadataPath(entryPath)\n\n try {\n // eslint-disable-next-line no-await-in-loop\n if (!(await isDir(entryPath))) {\n continue\n }\n\n // eslint-disable-next-line no-await-in-loop\n const metadata = await readJson(metaPath, { throws: false })\n if (\n !metadata ||\n typeof metadata !== 'object' ||\n Array.isArray(metadata)\n ) {\n continue\n }\n const timestamp = (metadata as Record<string, unknown>)['timestamp']\n // If timestamp is missing or invalid, treat as expired (age = infinity)\n const age =\n typeof timestamp === 'number' && timestamp > 0\n ? now - timestamp\n : Number.POSITIVE_INFINITY\n\n if (age > maxAge) {\n // Remove entire cache entry directory.\n // eslint-disable-next-line no-await-in-loop\n await safeDelete(entryPath, { force: true, recursive: true })\n cleaned += 1\n }\n } catch {\n // If we can't read metadata, check if directory is empty or corrupted.\n try {\n // eslint-disable-next-line no-await-in-loop\n const contents = await fs.readdir(entryPath)\n if (!contents.length) {\n // Remove empty directory.\n // eslint-disable-next-line no-await-in-loop\n await safeDelete(entryPath)\n cleaned += 1\n }\n } catch {}\n }\n }\n\n return cleaned\n}\n\n/**\n * Download and execute a binary from a URL with caching.\n */\nexport async function dlxBinary(\n args: readonly string[] | string[],\n options?: DlxBinaryOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<DlxBinaryResult> {\n const {\n cacheTtl = /*@__INLINE__*/ require('#constants/time').DLX_BINARY_CACHE_TTL,\n checksum,\n force = false,\n name,\n spawnOptions,\n url,\n } = { __proto__: null, ...options } as DlxBinaryOptions\n\n // Generate cache paths similar to pnpm/npx structure.\n const cacheDir = getDlxCachePath()\n const binaryName = name || `binary-${process.platform}-${os.arch()}`\n // Create spec from URL and binary name for unique cache identity.\n const spec = `${url}:${binaryName}`\n const cacheKey = generateCacheKey(spec)\n const cacheEntryDir = path.join(cacheDir, cacheKey)\n const binaryPath = normalizePath(path.join(cacheEntryDir, binaryName))\n\n let downloaded = false\n let computedChecksum = checksum\n\n // Check if we need to download.\n if (\n !force &&\n existsSync(cacheEntryDir) &&\n (await isCacheValid(cacheEntryDir, cacheTtl))\n ) {\n // Binary is cached and valid, read the checksum from metadata.\n try {\n const metaPath = getMetadataPath(cacheEntryDir)\n const metadata = await readJson(metaPath, { throws: false })\n if (\n metadata &&\n typeof metadata === 'object' &&\n !Array.isArray(metadata) &&\n typeof (metadata as Record<string, unknown>)['checksum'] === 'string'\n ) {\n computedChecksum = (metadata as Record<string, unknown>)[\n 'checksum'\n ] as string\n } else {\n // If metadata is invalid, re-download.\n downloaded = true\n }\n } catch {\n // If we can't read metadata, re-download.\n downloaded = true\n }\n } else {\n downloaded = true\n }\n\n if (downloaded) {\n // Ensure cache directory exists before downloading.\n try {\n await fs.mkdir(cacheEntryDir, { recursive: true })\n } catch (e) {\n const code = (e as NodeJS.ErrnoException).code\n if (code === 'EACCES' || code === 'EPERM') {\n throw new Error(\n `Permission denied creating binary cache directory: ${cacheEntryDir}\\n` +\n 'Please check directory permissions or run with appropriate access.',\n { cause: e },\n )\n }\n if (code === 'EROFS') {\n throw new Error(\n `Cannot create binary cache directory on read-only filesystem: ${cacheEntryDir}\\n` +\n 'Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.',\n { cause: e },\n )\n }\n throw new Error(\n `Failed to create binary cache directory: ${cacheEntryDir}`,\n { cause: e },\n )\n }\n\n // Download the binary.\n computedChecksum = await downloadBinaryFile(url, binaryPath, checksum)\n await writeMetadata(cacheEntryDir, url, computedChecksum || '')\n }\n\n // Execute the binary.\n // On Windows, script files (.bat, .cmd, .ps1) require shell: true because\n // they are not executable on their own and must be run through cmd.exe.\n // Note: .exe files are actual binaries and don't need shell mode.\n const needsShell = WIN32 && /\\.(?:bat|cmd|ps1)$/i.test(binaryPath)\n // Windows cmd.exe PATH resolution behavior:\n // When shell: true on Windows with .cmd/.bat/.ps1 files, spawn will automatically\n // strip the full path down to just the basename without extension (e.g.,\n // C:\\cache\\test.cmd becomes just \"test\"). Windows cmd.exe then searches for \"test\"\n // in directories listed in PATH, trying each extension from PATHEXT environment\n // variable (.COM, .EXE, .BAT, .CMD, etc.) until it finds a match.\n //\n // Since our binaries are downloaded to a custom cache directory that's not in PATH\n // (unlike system package managers like npm/pnpm/yarn which are already in PATH),\n // we must prepend the cache directory to PATH so cmd.exe can locate the binary.\n //\n // This approach is consistent with how other tools handle Windows command execution:\n // - npm's promise-spawn: uses which.sync() to find commands in PATH\n // - cross-spawn: spawns cmd.exe with escaped arguments\n // - Node.js spawn with shell: true: delegates to cmd.exe which uses PATH\n const finalSpawnOptions = needsShell\n ? {\n ...spawnOptions,\n env: {\n ...spawnOptions?.env,\n PATH: `${cacheEntryDir}${path.delimiter}${process.env['PATH'] || ''}`,\n },\n shell: true,\n }\n : spawnOptions\n const spawnPromise = spawn(binaryPath, args, finalSpawnOptions, spawnExtra)\n\n return {\n binaryPath,\n downloaded,\n spawnPromise,\n }\n}\n\n/**\n * Download a binary from a URL with caching (without execution).\n * Similar to downloadPackage from dlx-package.\n *\n * @returns Object containing the path to the cached binary and whether it was downloaded\n */\nexport async function downloadBinary(\n options: Omit<DlxBinaryOptions, 'spawnOptions'>,\n): Promise<{ binaryPath: string; downloaded: boolean }> {\n const {\n cacheTtl = /*@__INLINE__*/ require('#constants/time').DLX_BINARY_CACHE_TTL,\n checksum,\n force = false,\n name,\n url,\n } = { __proto__: null, ...options } as DlxBinaryOptions\n\n // Generate cache paths similar to pnpm/npx structure.\n const cacheDir = getDlxCachePath()\n const binaryName = name || `binary-${process.platform}-${os.arch()}`\n // Create spec from URL and binary name for unique cache identity.\n const spec = `${url}:${binaryName}`\n const cacheKey = generateCacheKey(spec)\n const cacheEntryDir = path.join(cacheDir, cacheKey)\n const binaryPath = normalizePath(path.join(cacheEntryDir, binaryName))\n\n let downloaded = false\n\n // Check if we need to download.\n if (\n !force &&\n existsSync(cacheEntryDir) &&\n (await isCacheValid(cacheEntryDir, cacheTtl))\n ) {\n // Binary is cached and valid.\n downloaded = false\n } else {\n // Ensure cache directory exists before downloading.\n try {\n await fs.mkdir(cacheEntryDir, { recursive: true })\n } catch (e) {\n const code = (e as NodeJS.ErrnoException).code\n if (code === 'EACCES' || code === 'EPERM') {\n throw new Error(\n `Permission denied creating binary cache directory: ${cacheEntryDir}\\n` +\n 'Please check directory permissions or run with appropriate access.',\n { cause: e },\n )\n }\n if (code === 'EROFS') {\n throw new Error(\n `Cannot create binary cache directory on read-only filesystem: ${cacheEntryDir}\\n` +\n 'Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.',\n { cause: e },\n )\n }\n throw new Error(\n `Failed to create binary cache directory: ${cacheEntryDir}`,\n { cause: e },\n )\n }\n\n // Download the binary.\n const computedChecksum = await downloadBinaryFile(url, binaryPath, checksum)\n await writeMetadata(cacheEntryDir, url, computedChecksum || '')\n downloaded = true\n }\n\n return {\n binaryPath,\n downloaded,\n }\n}\n\n/**\n * Execute a cached binary without re-downloading.\n * Similar to executePackage from dlx-package.\n * Binary must have been previously downloaded via downloadBinary or dlxBinary.\n *\n * @param binaryPath Path to the cached binary (from downloadBinary result)\n * @param args Arguments to pass to the binary\n * @param spawnOptions Spawn options for execution\n * @param spawnExtra Extra spawn configuration\n * @returns The spawn promise for the running process\n */\nexport function executeBinary(\n binaryPath: string,\n args: readonly string[] | string[],\n spawnOptions?: SpawnOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): ReturnType<typeof spawn> {\n // On Windows, script files (.bat, .cmd, .ps1) require shell: true because\n // they are not executable on their own and must be run through cmd.exe.\n // Note: .exe files are actual binaries and don't need shell mode.\n const needsShell = WIN32 && /\\.(?:bat|cmd|ps1)$/i.test(binaryPath)\n\n // Windows cmd.exe PATH resolution behavior:\n // When shell: true on Windows with .cmd/.bat/.ps1 files, spawn will automatically\n // strip the full path down to just the basename without extension. Windows cmd.exe\n // then searches for the binary in directories listed in PATH.\n //\n // Since our binaries are downloaded to a custom cache directory that's not in PATH,\n // we must prepend the cache directory to PATH so cmd.exe can locate the binary.\n const cacheEntryDir = path.dirname(binaryPath)\n const finalSpawnOptions = needsShell\n ? {\n ...spawnOptions,\n env: {\n ...spawnOptions?.env,\n PATH: `${cacheEntryDir}${path.delimiter}${process.env['PATH'] || ''}`,\n },\n shell: true,\n }\n : spawnOptions\n\n return spawn(binaryPath, args, finalSpawnOptions, spawnExtra)\n}\n\n/**\n * Get the DLX binary cache directory path.\n * Returns normalized path for cross-platform compatibility.\n * Uses same directory as dlx-package for unified DLX storage.\n */\nexport function getDlxCachePath(): string {\n return getSocketDlxDir()\n}\n\n/**\n * Get information about cached binaries.\n */\nexport async function listDlxCache(): Promise<\n Array<{\n age: number\n arch: string\n checksum: string\n name: string\n platform: string\n size: number\n url: string\n }>\n> {\n const cacheDir = getDlxCachePath()\n\n if (!existsSync(cacheDir)) {\n return []\n }\n\n const results = []\n const now = Date.now()\n const entries = await fs.readdir(cacheDir)\n\n for (const entry of entries) {\n const entryPath = path.join(cacheDir, entry)\n try {\n // eslint-disable-next-line no-await-in-loop\n if (!(await isDir(entryPath))) {\n continue\n }\n\n const metaPath = getMetadataPath(entryPath)\n // eslint-disable-next-line no-await-in-loop\n const metadata = await readJson(metaPath, { throws: false })\n if (\n !metadata ||\n typeof metadata !== 'object' ||\n Array.isArray(metadata)\n ) {\n continue\n }\n\n // Find the binary file in the directory.\n // eslint-disable-next-line no-await-in-loop\n const files = await fs.readdir(entryPath)\n const binaryFile = files.find(f => !f.startsWith('.'))\n\n if (binaryFile) {\n const binaryPath = path.join(entryPath, binaryFile)\n // eslint-disable-next-line no-await-in-loop\n const binaryStats = await fs.stat(binaryPath)\n\n const metaObj = metadata as Record<string, unknown>\n results.push({\n age: now - ((metaObj['timestamp'] as number) || 0),\n arch: (metaObj['arch'] as string) || 'unknown',\n checksum: (metaObj['checksum'] as string) || '',\n name: binaryFile,\n platform: (metaObj['platform'] as string) || 'unknown',\n size: binaryStats.size,\n url: (metaObj['url'] as string) || '',\n })\n }\n } catch {}\n }\n\n return results\n}\n"],
|
|
5
|
-
"mappings": ";6iBAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,mBAAAE,EAAA,cAAAC,EAAA,mBAAAC,EAAA,kBAAAC,EAAA,oBAAAC,EAAA,iBAAAC,
|
|
6
|
-
"names": ["dlx_binary_exports", "__export", "cleanDlxCache", "dlxBinary", "downloadBinary", "executeBinary", "getDlxCachePath", "listDlxCache", "__toCommonJS", "import_node_crypto", "import_node_fs", "import_node_os", "import_node_path", "import_platform", "import_dlx", "import_http_request", "import_fs", "import_objects", "import_path", "import_paths", "import_process_lock", "import_spawn", "getMetadataPath", "cacheEntryPath", "path", "isCacheValid", "cacheTtl", "metaPath", "metadata", "now", "timestamp", "downloadBinaryFile", "url", "destPath", "checksum", "cacheEntryDir", "lockPath", "fs", "fileBuffer", "hasher", "e", "actualChecksum", "writeMetadata", "os", "maxAge", "cacheDir", "cleaned", "entries", "entry", "entryPath", "args", "options", "spawnExtra", "force", "name", "spawnOptions", "binaryName", "spec", "
|
|
4
|
+
"sourcesContent": ["/** @fileoverview DLX binary execution utilities for Socket ecosystem. */\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, promises as fs } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport { WIN32 } from '#constants/platform'\n\nimport { generateCacheKey } from './dlx'\nimport { httpDownload } from './http-request'\nimport { isDir, readJson, safeDelete } from './fs'\nimport { isObjectObject } from './objects'\nimport { normalizePath } from './path'\nimport { getSocketDlxDir } from './paths'\nimport { processLock } from './process-lock'\nimport type { SpawnExtra, SpawnOptions } from './spawn'\nimport { spawn } from './spawn'\n\nexport interface DlxBinaryOptions {\n /** URL to download the binary from. */\n url: string\n /** Optional name for the cached binary (defaults to URL hash). */\n name?: string | undefined\n /** Expected checksum (sha256) for verification. */\n checksum?: string | undefined\n /** Cache TTL in milliseconds (default: 7 days). */\n cacheTtl?: number | undefined\n /** Force re-download even if cached. */\n force?: boolean | undefined\n /** Additional spawn options. */\n spawnOptions?: SpawnOptions | undefined\n}\n\nexport interface DlxBinaryResult {\n /** Path to the cached binary. */\n binaryPath: string\n /** Whether the binary was newly downloaded. */\n downloaded: boolean\n /** The spawn promise for the running process. */\n spawnPromise: ReturnType<typeof spawn>\n}\n\n/**\n * Metadata structure for cached binaries (.dlx-metadata.json).\n * Unified schema shared across TypeScript (dlxBinary) and C++ (socket_macho_decompress).\n *\n * Core Fields (present in all implementations):\n * - version: Schema version (currently \"1.0.0\")\n * - cache_key: First 16 chars of SHA-512 hash (matches directory name)\n * - timestamp: Unix timestamp in milliseconds\n * - checksum: Full hash of cached binary (SHA-512 for C++, SHA-256 for TypeScript)\n * - checksum_algorithm: \"sha512\" or \"sha256\"\n * - platform: \"darwin\" | \"linux\" | \"win32\"\n * - arch: \"x64\" | \"arm64\"\n * - size: Size of cached binary in bytes\n * - source: Origin information\n * - type: \"download\" (from URL) or \"decompression\" (from embedded binary)\n * - url: Download URL (if type is \"download\")\n * - path: Source binary path (if type is \"decompression\")\n *\n * Extra Fields (implementation-specific):\n * - For C++ decompression:\n * - compressed_size: Size of compressed data in bytes\n * - compression_algorithm: Brotli level (numeric)\n * - compression_ratio: original_size / compressed_size\n *\n * Example (TypeScript download):\n * ```json\n * {\n * \"version\": \"1.0.0\",\n * \"cache_key\": \"a1b2c3d4e5f67890\",\n * \"timestamp\": 1730332800000,\n * \"checksum\": \"sha256-abc123...\",\n * \"checksum_algorithm\": \"sha256\",\n * \"platform\": \"darwin\",\n * \"arch\": \"arm64\",\n * \"size\": 15000000,\n * \"source\": {\n * \"type\": \"download\",\n * \"url\": \"https://example.com/binary\"\n * }\n * }\n * ```\n *\n * Example (C++ decompression):\n * ```json\n * {\n * \"version\": \"1.0.0\",\n * \"cache_key\": \"0123456789abcdef\",\n * \"timestamp\": 1730332800000,\n * \"checksum\": \"sha512-def456...\",\n * \"checksum_algorithm\": \"sha512\",\n * \"platform\": \"darwin\",\n * \"arch\": \"arm64\",\n * \"size\": 13000000,\n * \"source\": {\n * \"type\": \"decompression\",\n * \"path\": \"/usr/local/bin/socket\"\n * },\n * \"extra\": {\n * \"compressed_size\": 1700000,\n * \"compression_algorithm\": 3,\n * \"compression_ratio\": 7.647\n * }\n * }\n * ```\n *\n * @internal This interface documents the metadata file format.\n */\nexport interface DlxMetadata {\n version: string\n cache_key: string\n timestamp: number\n checksum: string\n checksum_algorithm: string\n platform: string\n arch: string\n size: number\n source?: {\n type: 'download' | 'decompression'\n url?: string\n path?: string\n }\n extra?: Record<string, unknown>\n}\n\n/**\n * Get metadata file path for a cached binary.\n */\nfunction getMetadataPath(cacheEntryPath: string): string {\n return path.join(cacheEntryPath, '.dlx-metadata.json')\n}\n\n/**\n * Check if a cached binary is still valid.\n */\nasync function isCacheValid(\n cacheEntryPath: string,\n cacheTtl: number,\n): Promise<boolean> {\n try {\n const metaPath = getMetadataPath(cacheEntryPath)\n if (!existsSync(metaPath)) {\n return false\n }\n\n const metadata = await readJson(metaPath, { throws: false })\n if (!isObjectObject(metadata)) {\n return false\n }\n const now = Date.now()\n const timestamp = (metadata as Record<string, unknown>)['timestamp']\n // If timestamp is missing or invalid, cache is invalid\n if (typeof timestamp !== 'number' || timestamp <= 0) {\n return false\n }\n const age = now - timestamp\n\n return age < cacheTtl\n } catch {\n return false\n }\n}\n\n/**\n * Download a file from a URL with integrity checking and concurrent download protection.\n * Uses processLock to prevent multiple processes from downloading the same binary simultaneously.\n * Internal helper function for downloading binary files.\n */\nasync function downloadBinaryFile(\n url: string,\n destPath: string,\n checksum?: string | undefined,\n): Promise<string> {\n // Use process lock to prevent concurrent downloads.\n // Lock is placed in the cache entry directory as 'concurrency.lock'.\n const cacheEntryDir = path.dirname(destPath)\n const lockPath = path.join(cacheEntryDir, 'concurrency.lock')\n\n return await processLock.withLock(\n lockPath,\n async () => {\n // Check if file was downloaded while waiting for lock.\n if (existsSync(destPath)) {\n const stats = await fs.stat(destPath)\n if (stats.size > 0) {\n // File exists, compute and return checksum.\n const fileBuffer = await fs.readFile(destPath)\n const hasher = createHash('sha256')\n hasher.update(fileBuffer)\n return hasher.digest('hex')\n }\n }\n\n // Download the file.\n try {\n await httpDownload(url, destPath)\n } catch (e) {\n throw new Error(\n `Failed to download binary from ${url}\\n` +\n `Destination: ${destPath}\\n` +\n 'Check your internet connection or verify the URL is accessible.',\n { cause: e },\n )\n }\n\n // Compute checksum of downloaded file.\n const fileBuffer = await fs.readFile(destPath)\n const hasher = createHash('sha256')\n hasher.update(fileBuffer)\n const actualChecksum = hasher.digest('hex')\n\n // Verify checksum if provided.\n if (checksum && actualChecksum !== checksum) {\n // Clean up invalid file.\n await safeDelete(destPath)\n throw new Error(\n `Checksum mismatch: expected ${checksum}, got ${actualChecksum}`,\n )\n }\n\n // Make executable on POSIX systems.\n if (!WIN32) {\n await fs.chmod(destPath, 0o755)\n }\n\n return actualChecksum\n },\n {\n // Align with npm npx locking strategy.\n staleMs: 5000,\n touchIntervalMs: 2000,\n },\n )\n}\n\n/**\n * Write metadata for a cached binary.\n * Uses unified schema shared with C++ decompressor and CLI dlxBinary.\n * Schema documentation: See DlxMetadata interface in this file (exported).\n * Core fields: version, cache_key, timestamp, checksum, checksum_algorithm, platform, arch, size, source\n * Note: This implementation uses SHA-256 checksums instead of SHA-512.\n */\nasync function writeMetadata(\n cacheEntryPath: string,\n cacheKey: string,\n url: string,\n checksum: string,\n size: number,\n): Promise<void> {\n const metaPath = getMetadataPath(cacheEntryPath)\n const metadata = {\n version: '1.0.0',\n cache_key: cacheKey,\n timestamp: Date.now(),\n checksum,\n checksum_algorithm: 'sha256',\n platform: os.platform(),\n arch: os.arch(),\n size,\n source: {\n type: 'download',\n url,\n },\n }\n await fs.writeFile(metaPath, JSON.stringify(metadata, null, 2))\n}\n\n/**\n * Clean expired entries from the DLX cache.\n */\nexport async function cleanDlxCache(\n maxAge: number = /*@__INLINE__*/ require('#constants/time').DLX_BINARY_CACHE_TTL,\n): Promise<number> {\n const cacheDir = getDlxCachePath()\n\n if (!existsSync(cacheDir)) {\n return 0\n }\n\n let cleaned = 0\n const now = Date.now()\n const entries = await fs.readdir(cacheDir)\n\n for (const entry of entries) {\n const entryPath = path.join(cacheDir, entry)\n const metaPath = getMetadataPath(entryPath)\n\n try {\n // eslint-disable-next-line no-await-in-loop\n if (!(await isDir(entryPath))) {\n continue\n }\n\n // eslint-disable-next-line no-await-in-loop\n const metadata = await readJson(metaPath, { throws: false })\n if (\n !metadata ||\n typeof metadata !== 'object' ||\n Array.isArray(metadata)\n ) {\n continue\n }\n const timestamp = (metadata as Record<string, unknown>)['timestamp']\n // If timestamp is missing or invalid, treat as expired (age = infinity)\n const age =\n typeof timestamp === 'number' && timestamp > 0\n ? now - timestamp\n : Number.POSITIVE_INFINITY\n\n if (age > maxAge) {\n // Remove entire cache entry directory.\n // eslint-disable-next-line no-await-in-loop\n await safeDelete(entryPath, { force: true, recursive: true })\n cleaned += 1\n }\n } catch {\n // If we can't read metadata, check if directory is empty or corrupted.\n try {\n // eslint-disable-next-line no-await-in-loop\n const contents = await fs.readdir(entryPath)\n if (!contents.length) {\n // Remove empty directory.\n // eslint-disable-next-line no-await-in-loop\n await safeDelete(entryPath)\n cleaned += 1\n }\n } catch {}\n }\n }\n\n return cleaned\n}\n\n/**\n * Download and execute a binary from a URL with caching.\n */\nexport async function dlxBinary(\n args: readonly string[] | string[],\n options?: DlxBinaryOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<DlxBinaryResult> {\n const {\n cacheTtl = /*@__INLINE__*/ require('#constants/time').DLX_BINARY_CACHE_TTL,\n checksum,\n force = false,\n name,\n spawnOptions,\n url,\n } = { __proto__: null, ...options } as DlxBinaryOptions\n\n // Generate cache paths similar to pnpm/npx structure.\n const cacheDir = getDlxCachePath()\n const binaryName = name || `binary-${process.platform}-${os.arch()}`\n // Create spec from URL and binary name for unique cache identity.\n const spec = `${url}:${binaryName}`\n const cacheKey = generateCacheKey(spec)\n const cacheEntryDir = path.join(cacheDir, cacheKey)\n const binaryPath = normalizePath(path.join(cacheEntryDir, binaryName))\n\n let downloaded = false\n let computedChecksum = checksum\n\n // Check if we need to download.\n if (\n !force &&\n existsSync(cacheEntryDir) &&\n (await isCacheValid(cacheEntryDir, cacheTtl))\n ) {\n // Binary is cached and valid, read the checksum from metadata.\n try {\n const metaPath = getMetadataPath(cacheEntryDir)\n const metadata = await readJson(metaPath, { throws: false })\n if (\n metadata &&\n typeof metadata === 'object' &&\n !Array.isArray(metadata) &&\n typeof (metadata as Record<string, unknown>)['checksum'] === 'string'\n ) {\n computedChecksum = (metadata as Record<string, unknown>)[\n 'checksum'\n ] as string\n } else {\n // If metadata is invalid, re-download.\n downloaded = true\n }\n } catch {\n // If we can't read metadata, re-download.\n downloaded = true\n }\n } else {\n downloaded = true\n }\n\n if (downloaded) {\n // Ensure cache directory exists before downloading.\n try {\n await fs.mkdir(cacheEntryDir, { recursive: true })\n } catch (e) {\n const code = (e as NodeJS.ErrnoException).code\n if (code === 'EACCES' || code === 'EPERM') {\n throw new Error(\n `Permission denied creating binary cache directory: ${cacheEntryDir}\\n` +\n 'Please check directory permissions or run with appropriate access.',\n { cause: e },\n )\n }\n if (code === 'EROFS') {\n throw new Error(\n `Cannot create binary cache directory on read-only filesystem: ${cacheEntryDir}\\n` +\n 'Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.',\n { cause: e },\n )\n }\n throw new Error(\n `Failed to create binary cache directory: ${cacheEntryDir}`,\n { cause: e },\n )\n }\n\n // Download the binary.\n computedChecksum = await downloadBinaryFile(url, binaryPath, checksum)\n\n // Get file size for metadata.\n const stats = await fs.stat(binaryPath)\n await writeMetadata(\n cacheEntryDir,\n cacheKey,\n url,\n computedChecksum || '',\n stats.size,\n )\n }\n\n // Execute the binary.\n // On Windows, script files (.bat, .cmd, .ps1) require shell: true because\n // they are not executable on their own and must be run through cmd.exe.\n // Note: .exe files are actual binaries and don't need shell mode.\n const needsShell = WIN32 && /\\.(?:bat|cmd|ps1)$/i.test(binaryPath)\n // Windows cmd.exe PATH resolution behavior:\n // When shell: true on Windows with .cmd/.bat/.ps1 files, spawn will automatically\n // strip the full path down to just the basename without extension (e.g.,\n // C:\\cache\\test.cmd becomes just \"test\"). Windows cmd.exe then searches for \"test\"\n // in directories listed in PATH, trying each extension from PATHEXT environment\n // variable (.COM, .EXE, .BAT, .CMD, etc.) until it finds a match.\n //\n // Since our binaries are downloaded to a custom cache directory that's not in PATH\n // (unlike system package managers like npm/pnpm/yarn which are already in PATH),\n // we must prepend the cache directory to PATH so cmd.exe can locate the binary.\n //\n // This approach is consistent with how other tools handle Windows command execution:\n // - npm's promise-spawn: uses which.sync() to find commands in PATH\n // - cross-spawn: spawns cmd.exe with escaped arguments\n // - Node.js spawn with shell: true: delegates to cmd.exe which uses PATH\n const finalSpawnOptions = needsShell\n ? {\n ...spawnOptions,\n env: {\n ...spawnOptions?.env,\n PATH: `${cacheEntryDir}${path.delimiter}${process.env['PATH'] || ''}`,\n },\n shell: true,\n }\n : spawnOptions\n const spawnPromise = spawn(binaryPath, args, finalSpawnOptions, spawnExtra)\n\n return {\n binaryPath,\n downloaded,\n spawnPromise,\n }\n}\n\n/**\n * Download a binary from a URL with caching (without execution).\n * Similar to downloadPackage from dlx-package.\n *\n * @returns Object containing the path to the cached binary and whether it was downloaded\n */\nexport async function downloadBinary(\n options: Omit<DlxBinaryOptions, 'spawnOptions'>,\n): Promise<{ binaryPath: string; downloaded: boolean }> {\n const {\n cacheTtl = /*@__INLINE__*/ require('#constants/time').DLX_BINARY_CACHE_TTL,\n checksum,\n force = false,\n name,\n url,\n } = { __proto__: null, ...options } as DlxBinaryOptions\n\n // Generate cache paths similar to pnpm/npx structure.\n const cacheDir = getDlxCachePath()\n const binaryName = name || `binary-${process.platform}-${os.arch()}`\n // Create spec from URL and binary name for unique cache identity.\n const spec = `${url}:${binaryName}`\n const cacheKey = generateCacheKey(spec)\n const cacheEntryDir = path.join(cacheDir, cacheKey)\n const binaryPath = normalizePath(path.join(cacheEntryDir, binaryName))\n\n let downloaded = false\n\n // Check if we need to download.\n if (\n !force &&\n existsSync(cacheEntryDir) &&\n (await isCacheValid(cacheEntryDir, cacheTtl))\n ) {\n // Binary is cached and valid.\n downloaded = false\n } else {\n // Ensure cache directory exists before downloading.\n try {\n await fs.mkdir(cacheEntryDir, { recursive: true })\n } catch (e) {\n const code = (e as NodeJS.ErrnoException).code\n if (code === 'EACCES' || code === 'EPERM') {\n throw new Error(\n `Permission denied creating binary cache directory: ${cacheEntryDir}\\n` +\n 'Please check directory permissions or run with appropriate access.',\n { cause: e },\n )\n }\n if (code === 'EROFS') {\n throw new Error(\n `Cannot create binary cache directory on read-only filesystem: ${cacheEntryDir}\\n` +\n 'Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.',\n { cause: e },\n )\n }\n throw new Error(\n `Failed to create binary cache directory: ${cacheEntryDir}`,\n { cause: e },\n )\n }\n\n // Download the binary.\n const computedChecksum = await downloadBinaryFile(url, binaryPath, checksum)\n\n // Get file size for metadata.\n const stats = await fs.stat(binaryPath)\n await writeMetadata(\n cacheEntryDir,\n cacheKey,\n url,\n computedChecksum || '',\n stats.size,\n )\n downloaded = true\n }\n\n return {\n binaryPath,\n downloaded,\n }\n}\n\n/**\n * Execute a cached binary without re-downloading.\n * Similar to executePackage from dlx-package.\n * Binary must have been previously downloaded via downloadBinary or dlxBinary.\n *\n * @param binaryPath Path to the cached binary (from downloadBinary result)\n * @param args Arguments to pass to the binary\n * @param spawnOptions Spawn options for execution\n * @param spawnExtra Extra spawn configuration\n * @returns The spawn promise for the running process\n */\nexport function executeBinary(\n binaryPath: string,\n args: readonly string[] | string[],\n spawnOptions?: SpawnOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): ReturnType<typeof spawn> {\n // On Windows, script files (.bat, .cmd, .ps1) require shell: true because\n // they are not executable on their own and must be run through cmd.exe.\n // Note: .exe files are actual binaries and don't need shell mode.\n const needsShell = WIN32 && /\\.(?:bat|cmd|ps1)$/i.test(binaryPath)\n\n // Windows cmd.exe PATH resolution behavior:\n // When shell: true on Windows with .cmd/.bat/.ps1 files, spawn will automatically\n // strip the full path down to just the basename without extension. Windows cmd.exe\n // then searches for the binary in directories listed in PATH.\n //\n // Since our binaries are downloaded to a custom cache directory that's not in PATH,\n // we must prepend the cache directory to PATH so cmd.exe can locate the binary.\n const cacheEntryDir = path.dirname(binaryPath)\n const finalSpawnOptions = needsShell\n ? {\n ...spawnOptions,\n env: {\n ...spawnOptions?.env,\n PATH: `${cacheEntryDir}${path.delimiter}${process.env['PATH'] || ''}`,\n },\n shell: true,\n }\n : spawnOptions\n\n return spawn(binaryPath, args, finalSpawnOptions, spawnExtra)\n}\n\n/**\n * Get the DLX binary cache directory path.\n * Returns normalized path for cross-platform compatibility.\n * Uses same directory as dlx-package for unified DLX storage.\n */\nexport function getDlxCachePath(): string {\n return getSocketDlxDir()\n}\n\n/**\n * Get information about cached binaries.\n */\nexport async function listDlxCache(): Promise<\n Array<{\n age: number\n arch: string\n checksum: string\n name: string\n platform: string\n size: number\n url: string\n }>\n> {\n const cacheDir = getDlxCachePath()\n\n if (!existsSync(cacheDir)) {\n return []\n }\n\n const results = []\n const now = Date.now()\n const entries = await fs.readdir(cacheDir)\n\n for (const entry of entries) {\n const entryPath = path.join(cacheDir, entry)\n try {\n // eslint-disable-next-line no-await-in-loop\n if (!(await isDir(entryPath))) {\n continue\n }\n\n const metaPath = getMetadataPath(entryPath)\n // eslint-disable-next-line no-await-in-loop\n const metadata = await readJson(metaPath, { throws: false })\n if (\n !metadata ||\n typeof metadata !== 'object' ||\n Array.isArray(metadata)\n ) {\n continue\n }\n\n const metaObj = metadata as Record<string, unknown>\n\n // Get URL from unified schema (source.url) or legacy schema (url).\n // Allow empty URL for backward compatibility with partial metadata.\n const source = metaObj['source'] as Record<string, unknown> | undefined\n const url =\n (source?.['url'] as string) || (metaObj['url'] as string) || ''\n\n // Find the binary file in the directory.\n // eslint-disable-next-line no-await-in-loop\n const files = await fs.readdir(entryPath)\n const binaryFile = files.find(f => !f.startsWith('.'))\n\n if (binaryFile) {\n const binaryPath = path.join(entryPath, binaryFile)\n // eslint-disable-next-line no-await-in-loop\n const binaryStats = await fs.stat(binaryPath)\n\n results.push({\n age: now - ((metaObj['timestamp'] as number) || 0),\n arch: (metaObj['arch'] as string) || 'unknown',\n checksum: (metaObj['checksum'] as string) || '',\n name: binaryFile,\n platform: (metaObj['platform'] as string) || 'unknown',\n size: binaryStats.size,\n url,\n })\n }\n } catch {}\n }\n\n return results\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["dlx_binary_exports", "__export", "cleanDlxCache", "dlxBinary", "downloadBinary", "executeBinary", "getDlxCachePath", "listDlxCache", "__toCommonJS", "import_node_crypto", "import_node_fs", "import_node_os", "import_node_path", "import_platform", "import_dlx", "import_http_request", "import_fs", "import_objects", "import_path", "import_paths", "import_process_lock", "import_spawn", "getMetadataPath", "cacheEntryPath", "path", "isCacheValid", "cacheTtl", "metaPath", "metadata", "now", "timestamp", "downloadBinaryFile", "url", "destPath", "checksum", "cacheEntryDir", "lockPath", "fs", "fileBuffer", "hasher", "e", "actualChecksum", "writeMetadata", "cacheKey", "size", "os", "maxAge", "cacheDir", "cleaned", "entries", "entry", "entryPath", "args", "options", "spawnExtra", "force", "name", "spawnOptions", "binaryName", "spec", "binaryPath", "downloaded", "computedChecksum", "code", "stats", "finalSpawnOptions", "spawnPromise", "needsShell", "results", "metaObj", "binaryFile", "f", "binaryStats"]
|
|
7
7
|
}
|