@socketsecurity/lib 2.9.1 → 2.10.1

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,33 @@ 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.1](https://github.com/SocketDev/socket-lib/releases/tag/v2.10.1) - 2025-10-31
9
+
10
+ ### Fixed
11
+
12
+ - **Process lock directory creation**: Use recursive mkdir to ensure parent directories exist when creating lock directory
13
+ - **Node.js debug flags**: Remove buggy `getNodeDebugFlags()` function that returned debug flags without required argument values
14
+
15
+ ## [2.10.0](https://github.com/SocketDev/socket-lib/releases/tag/v2.10.0) - 2025-10-30
16
+
17
+ ### Added
18
+
19
+ - **Unified DLX metadata schema**: Standardized `.dlx-metadata.json` format across TypeScript and C++ implementations
20
+ - Exported `DlxMetadata` interface as canonical schema reference
21
+ - Core fields: `version`, `cache_key`, `timestamp`, `checksum`, `checksum_algorithm`, `platform`, `arch`, `size`, `source`
22
+ - Support for `source` tracking (download vs decompression origin)
23
+ - Reserved `extra` field for implementation-specific data
24
+ - Comprehensive documentation with examples for both download and decompression use cases
25
+
26
+ ### Changed
27
+
28
+ - **DLX binary metadata structure**: Updated `writeMetadata()` to use unified schema with additional fields
29
+ - Now includes `cache_key` (first 16 chars of SHA-512 hash)
30
+ - Added `size` field for cached binary size
31
+ - Added `checksum_algorithm` field (currently "sha256")
32
+ - Restructured to use `source.type` and `source.url` for origin tracking
33
+ - Maintains backward compatibility in `listDlxCache()` reader
34
+
8
35
  ## [2.9.1](https://github.com/SocketDev/socket-lib/releases/tag/v2.9.1) - 2025-10-30
9
36
 
10
37
  ### Added
@@ -17,7 +17,6 @@ export declare function supportsNodeRun(): boolean;
17
17
  export declare function supportsNodeDisableSigusr1Flag(): boolean;
18
18
  export declare function getNodeDisableSigusr1Flags(): string[];
19
19
  export declare function supportsProcessSend(): boolean;
20
- export declare function getNodeDebugFlags(): string[];
21
20
  export declare function getNodeHardenFlags(): string[];
22
21
  export declare function getNodePermissionFlags(): string[];
23
22
  export declare function getNodeNoWarningsFlags(): string[];
@@ -1,3 +1,3 @@
1
1
  /* Socket Lib - Built with esbuild */
2
- var u=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var x=(e,n)=>{for(var s in n)u(e,s,{get:n[s],enumerable:!0})},j=(e,n,s,g)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of b(n))!N.call(e,t)&&t!==s&&u(e,t,{get:()=>n[t],enumerable:!(g=m(n,t))||g.enumerable});return e};var F=e=>j(u({},"__esModule",{value:!0}),e);var q={};x(q,{ESNEXT:()=>W,NODE_SEA_FUSE:()=>R,getExecPath:()=>M,getMaintainedNodeVersions:()=>E,getNodeDebugFlags:()=>w,getNodeDisableSigusr1Flags:()=>O,getNodeHardenFlags:()=>v,getNodeMajorVersion:()=>r,getNodeNoWarningsFlags:()=>A,getNodePermissionFlags:()=>C,getNodeVersion:()=>_,supportsNodeCompileCacheApi:()=>S,supportsNodeCompileCacheEnvVar:()=>h,supportsNodeDisableSigusr1Flag:()=>f,supportsNodeDisableWarningFlag:()=>D,supportsNodePermissionFlag:()=>I,supportsNodeRequireModule:()=>V,supportsNodeRun:()=>y,supportsProcessSend:()=>P});module.exports=F(q);const o=process.version;function _(){return o}function r(){return Number.parseInt(o.slice(1).split(".")[0]||"0",10)}let i;function E(){if(i===void 0)try{i=require("../lib/maintained-node-versions")}catch{i=Object.freeze(Object.assign([],{current:"",last:"",next:"",previous:""}))}return i}function S(){return r()>=24}function h(){return r()>=22}function D(){return r()>=21}function I(){return r()>=20}function V(){const e=r();return e>=23||e===22&&Number.parseInt(o.split(".")[1]||"0",10)>=12}function y(){const e=r();return e>=23||e===22&&Number.parseInt(o.split(".")[1]||"0",10)>=11}function f(){const e=r();return e>=24?Number.parseInt(o.split(".")[1]||"0",10)>=8:e===23?Number.parseInt(o.split(".")[1]||"0",10)>=7:e===22?Number.parseInt(o.split(".")[1]||"0",10)>=14:!1}let p;function O(){return p===void 0&&(p=f()?["--disable-sigusr1"]:["--no-inspect"]),p}function P(){return typeof process.send=="function"}let c;function w(){return c===void 0&&(c=["--inspect","--inspect-brk","--inspect-port","--inspect-publish-uid"]),c}let l;function v(){if(l===void 0){const e=r(),n=["--disable-proto=delete",e>=24?"--permission":"--experimental-permission","--force-node-api-uncaught-exceptions-policy"];e<24&&n.push("--experimental-policy"),l=n}return l}let a;function C(){return a===void 0&&(r()>=24?a=["--allow-fs-read=*","--allow-fs-write=*","--allow-child-process"]:a=[]),a}let d;function A(){return d===void 0&&(d=["--no-warnings","--no-deprecation"]),d}function M(){return process.execPath}const R="NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2",W="esnext";0&&(module.exports={ESNEXT,NODE_SEA_FUSE,getExecPath,getMaintainedNodeVersions,getNodeDebugFlags,getNodeDisableSigusr1Flags,getNodeHardenFlags,getNodeMajorVersion,getNodeNoWarningsFlags,getNodePermissionFlags,getNodeVersion,supportsNodeCompileCacheApi,supportsNodeCompileCacheEnvVar,supportsNodeDisableSigusr1Flag,supportsNodeDisableWarningFlag,supportsNodePermissionFlag,supportsNodeRequireModule,supportsNodeRun,supportsProcessSend});
2
+ var u=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var b=(e,n)=>{for(var s in n)u(e,s,{get:n[s],enumerable:!0})},x=(e,n,s,d)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of m(n))!N.call(e,t)&&t!==s&&u(e,t,{get:()=>n[t],enumerable:!(d=f(n,t))||d.enumerable});return e};var j=e=>x(u({},"__esModule",{value:!0}),e);var R={};b(R,{ESNEXT:()=>M,NODE_SEA_FUSE:()=>A,getExecPath:()=>C,getMaintainedNodeVersions:()=>E,getNodeDisableSigusr1Flags:()=>y,getNodeHardenFlags:()=>P,getNodeMajorVersion:()=>r,getNodeNoWarningsFlags:()=>v,getNodePermissionFlags:()=>w,getNodeVersion:()=>F,supportsNodeCompileCacheApi:()=>_,supportsNodeCompileCacheEnvVar:()=>S,supportsNodeDisableSigusr1Flag:()=>g,supportsNodeDisableWarningFlag:()=>h,supportsNodePermissionFlag:()=>D,supportsNodeRequireModule:()=>I,supportsNodeRun:()=>V,supportsProcessSend:()=>O});module.exports=j(R);const o=process.version;function F(){return o}function r(){return Number.parseInt(o.slice(1).split(".")[0]||"0",10)}let i;function E(){if(i===void 0)try{i=require("../lib/maintained-node-versions")}catch{i=Object.freeze(Object.assign([],{current:"",last:"",next:"",previous:""}))}return i}function _(){return r()>=24}function S(){return r()>=22}function h(){return r()>=21}function D(){return r()>=20}function I(){const e=r();return e>=23||e===22&&Number.parseInt(o.split(".")[1]||"0",10)>=12}function V(){const e=r();return e>=23||e===22&&Number.parseInt(o.split(".")[1]||"0",10)>=11}function g(){const e=r();return e>=24?Number.parseInt(o.split(".")[1]||"0",10)>=8:e===23?Number.parseInt(o.split(".")[1]||"0",10)>=7:e===22?Number.parseInt(o.split(".")[1]||"0",10)>=14:!1}let p;function y(){return p===void 0&&(p=g()?["--disable-sigusr1"]:["--no-inspect"]),p}function O(){return typeof process.send=="function"}let c;function P(){if(c===void 0){const e=r(),n=["--disable-proto=delete",e>=24?"--permission":"--experimental-permission","--force-node-api-uncaught-exceptions-policy"];e<24&&n.push("--experimental-policy"),c=n}return c}let a;function w(){return a===void 0&&(r()>=24?a=["--allow-fs-read=*","--allow-fs-write=*","--allow-child-process"]:a=[]),a}let l;function v(){return l===void 0&&(l=["--no-warnings","--no-deprecation"]),l}function C(){return process.execPath}const A="NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2",M="esnext";0&&(module.exports={ESNEXT,NODE_SEA_FUSE,getExecPath,getMaintainedNodeVersions,getNodeDisableSigusr1Flags,getNodeHardenFlags,getNodeMajorVersion,getNodeNoWarningsFlags,getNodePermissionFlags,getNodeVersion,supportsNodeCompileCacheApi,supportsNodeCompileCacheEnvVar,supportsNodeDisableSigusr1Flag,supportsNodeDisableWarningFlag,supportsNodePermissionFlag,supportsNodeRequireModule,supportsNodeRun,supportsProcessSend});
3
3
  //# sourceMappingURL=node.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/constants/node.ts"],
4
- "sourcesContent": ["/**\n * Node.js runtime: versions, features, flags, and capabilities.\n */\n\nconst NODE_VERSION = process.version\n\n// Version detection.\nexport function getNodeVersion(): string {\n return NODE_VERSION\n}\n\nexport function getNodeMajorVersion(): number {\n return Number.parseInt(NODE_VERSION.slice(1).split('.')[0] || '0', 10)\n}\n\n// Maintained Node.js versions.\nlet _maintainedNodeVersions:\n | (readonly string[] & {\n current: string\n last: string\n next: string\n previous: string\n })\n | undefined\nexport function getMaintainedNodeVersions() {\n if (_maintainedNodeVersions === undefined) {\n try {\n _maintainedNodeVersions = require('../lib/maintained-node-versions')\n } catch {\n _maintainedNodeVersions = Object.freeze(\n Object.assign([], {\n current: '',\n last: '',\n next: '',\n previous: '',\n }),\n ) as typeof _maintainedNodeVersions\n }\n }\n return _maintainedNodeVersions\n}\n\n// Feature detection.\nexport function supportsNodeCompileCacheApi(): boolean {\n const major = getNodeMajorVersion()\n return major >= 24\n}\n\nexport function supportsNodeCompileCacheEnvVar(): boolean {\n const major = getNodeMajorVersion()\n return major >= 22\n}\n\nexport function supportsNodeDisableWarningFlag(): boolean {\n const major = getNodeMajorVersion()\n return major >= 21\n}\n\nexport function supportsNodePermissionFlag(): boolean {\n const major = getNodeMajorVersion()\n return major >= 20\n}\n\nexport function supportsNodeRequireModule(): boolean {\n const major = getNodeMajorVersion()\n return (\n major >= 23 ||\n (major === 22 &&\n Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10) >= 12)\n )\n}\n\nexport function supportsNodeRun(): boolean {\n const major = getNodeMajorVersion()\n return (\n major >= 23 ||\n (major === 22 &&\n Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10) >= 11)\n )\n}\n\nexport function supportsNodeDisableSigusr1Flag(): boolean {\n const major = getNodeMajorVersion()\n // --disable-sigusr1 added in v22.14.0, v23.7.0.\n // Stabilized in v22.20.0, v24.8.0.\n if (major >= 24) {\n const minor = Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10)\n return minor >= 8\n }\n if (major === 23) {\n const minor = Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10)\n return minor >= 7\n }\n if (major === 22) {\n const minor = Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10)\n return minor >= 14\n }\n return false\n}\n\nlet _nodeDisableSigusr1Flags: string[]\nexport function getNodeDisableSigusr1Flags(): string[] {\n if (_nodeDisableSigusr1Flags === undefined) {\n // SIGUSR1 is reserved by Node.js for starting the debugger/inspector.\n // In production CLI environments, we want to prevent debugger attachment.\n //\n // --disable-sigusr1: Prevents Signal I/O Thread from listening to SIGUSR1 (v22.14.0+).\n // --no-inspect: Disables inspector on older Node versions that don't support --disable-sigusr1.\n //\n // Note: --disable-sigusr1 is the correct solution (prevents thread creation entirely).\n // --no-inspect is a fallback that still creates the signal handler thread but blocks later.\n _nodeDisableSigusr1Flags = supportsNodeDisableSigusr1Flag()\n ? ['--disable-sigusr1']\n : ['--no-inspect']\n }\n return _nodeDisableSigusr1Flags\n}\n\nexport function supportsProcessSend(): boolean {\n return typeof process.send === 'function'\n}\n\n// Node.js flags.\nlet _nodeDebugFlags: string[]\nexport function getNodeDebugFlags(): string[] {\n if (_nodeDebugFlags === undefined) {\n _nodeDebugFlags = [\n '--inspect',\n '--inspect-brk',\n '--inspect-port',\n '--inspect-publish-uid',\n ]\n }\n return _nodeDebugFlags\n}\n\nlet _nodeHardenFlags: string[]\nexport function getNodeHardenFlags(): string[] {\n if (_nodeHardenFlags === undefined) {\n const major = getNodeMajorVersion()\n const flags = [\n '--disable-proto=delete',\n // Node.js 24+ uses --permission instead of --experimental-permission.\n // The permission model graduated from experimental to production-ready.\n major >= 24 ? '--permission' : '--experimental-permission',\n // Force uncaught exceptions policy for N-API addons (Node.js 22+).\n '--force-node-api-uncaught-exceptions-policy',\n ]\n // Only add policy flag if we're using experimental permission (Node < 24).\n // Node 24+ --policy requires a policy file which we don't have.\n if (major < 24) {\n flags.push('--experimental-policy')\n }\n _nodeHardenFlags = flags\n }\n return _nodeHardenFlags\n}\n\nlet _nodePermissionFlags: string[]\nexport function getNodePermissionFlags(): string[] {\n if (_nodePermissionFlags === undefined) {\n const major = getNodeMajorVersion()\n // Node.js 24+ requires explicit permission grants when using --permission flag.\n // npm needs filesystem access to read package.json files and node_modules.\n if (major >= 24) {\n _nodePermissionFlags = [\n // Allow reading from the entire filesystem (npm needs to read package.json, node_modules, etc.).\n '--allow-fs-read=*',\n // Allow writing to the entire filesystem (npm needs to write to node_modules, cache, etc.).\n '--allow-fs-write=*',\n // Allow spawning child processes (npm needs to run lifecycle scripts, git, etc.).\n '--allow-child-process',\n ]\n } else {\n // Node.js 20-23 with --experimental-permission doesn't require explicit grants\n // or uses different permission API.\n _nodePermissionFlags = []\n }\n }\n return _nodePermissionFlags\n}\n\nlet _nodeNoWarningsFlags: string[]\nexport function getNodeNoWarningsFlags(): string[] {\n if (_nodeNoWarningsFlags === undefined) {\n _nodeNoWarningsFlags = ['--no-warnings', '--no-deprecation']\n }\n return _nodeNoWarningsFlags\n}\n\n// Execution path.\nexport function getExecPath(): string {\n return process.execPath\n}\n\n// Node.js constants.\nexport const NODE_SEA_FUSE = 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2'\nexport const ESNEXT = 'esnext'\n"],
5
- "mappings": "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",
6
- "names": ["node_exports", "__export", "ESNEXT", "NODE_SEA_FUSE", "getExecPath", "getMaintainedNodeVersions", "getNodeDebugFlags", "getNodeDisableSigusr1Flags", "getNodeHardenFlags", "getNodeMajorVersion", "getNodeNoWarningsFlags", "getNodePermissionFlags", "getNodeVersion", "supportsNodeCompileCacheApi", "supportsNodeCompileCacheEnvVar", "supportsNodeDisableSigusr1Flag", "supportsNodeDisableWarningFlag", "supportsNodePermissionFlag", "supportsNodeRequireModule", "supportsNodeRun", "supportsProcessSend", "__toCommonJS", "NODE_VERSION", "_maintainedNodeVersions", "major", "_nodeDisableSigusr1Flags", "_nodeDebugFlags", "_nodeHardenFlags", "flags", "_nodePermissionFlags", "_nodeNoWarningsFlags"]
4
+ "sourcesContent": ["/**\n * Node.js runtime: versions, features, flags, and capabilities.\n */\n\nconst NODE_VERSION = process.version\n\n// Version detection.\nexport function getNodeVersion(): string {\n return NODE_VERSION\n}\n\nexport function getNodeMajorVersion(): number {\n return Number.parseInt(NODE_VERSION.slice(1).split('.')[0] || '0', 10)\n}\n\n// Maintained Node.js versions.\nlet _maintainedNodeVersions:\n | (readonly string[] & {\n current: string\n last: string\n next: string\n previous: string\n })\n | undefined\nexport function getMaintainedNodeVersions() {\n if (_maintainedNodeVersions === undefined) {\n try {\n _maintainedNodeVersions = require('../lib/maintained-node-versions')\n } catch {\n _maintainedNodeVersions = Object.freeze(\n Object.assign([], {\n current: '',\n last: '',\n next: '',\n previous: '',\n }),\n ) as typeof _maintainedNodeVersions\n }\n }\n return _maintainedNodeVersions\n}\n\n// Feature detection.\nexport function supportsNodeCompileCacheApi(): boolean {\n const major = getNodeMajorVersion()\n return major >= 24\n}\n\nexport function supportsNodeCompileCacheEnvVar(): boolean {\n const major = getNodeMajorVersion()\n return major >= 22\n}\n\nexport function supportsNodeDisableWarningFlag(): boolean {\n const major = getNodeMajorVersion()\n return major >= 21\n}\n\nexport function supportsNodePermissionFlag(): boolean {\n const major = getNodeMajorVersion()\n return major >= 20\n}\n\nexport function supportsNodeRequireModule(): boolean {\n const major = getNodeMajorVersion()\n return (\n major >= 23 ||\n (major === 22 &&\n Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10) >= 12)\n )\n}\n\nexport function supportsNodeRun(): boolean {\n const major = getNodeMajorVersion()\n return (\n major >= 23 ||\n (major === 22 &&\n Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10) >= 11)\n )\n}\n\nexport function supportsNodeDisableSigusr1Flag(): boolean {\n const major = getNodeMajorVersion()\n // --disable-sigusr1 added in v22.14.0, v23.7.0.\n // Stabilized in v22.20.0, v24.8.0.\n if (major >= 24) {\n const minor = Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10)\n return minor >= 8\n }\n if (major === 23) {\n const minor = Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10)\n return minor >= 7\n }\n if (major === 22) {\n const minor = Number.parseInt(NODE_VERSION.split('.')[1] || '0', 10)\n return minor >= 14\n }\n return false\n}\n\nlet _nodeDisableSigusr1Flags: string[]\nexport function getNodeDisableSigusr1Flags(): string[] {\n if (_nodeDisableSigusr1Flags === undefined) {\n // SIGUSR1 is reserved by Node.js for starting the debugger/inspector.\n // In production CLI environments, we want to prevent debugger attachment.\n //\n // --disable-sigusr1: Prevents Signal I/O Thread from listening to SIGUSR1 (v22.14.0+).\n // --no-inspect: Disables inspector on older Node versions that don't support --disable-sigusr1.\n //\n // Note: --disable-sigusr1 is the correct solution (prevents thread creation entirely).\n // --no-inspect is a fallback that still creates the signal handler thread but blocks later.\n _nodeDisableSigusr1Flags = supportsNodeDisableSigusr1Flag()\n ? ['--disable-sigusr1']\n : ['--no-inspect']\n }\n return _nodeDisableSigusr1Flags\n}\n\nexport function supportsProcessSend(): boolean {\n return typeof process.send === 'function'\n}\n\n// Node.js flags.\nlet _nodeHardenFlags: string[]\nexport function getNodeHardenFlags(): string[] {\n if (_nodeHardenFlags === undefined) {\n const major = getNodeMajorVersion()\n const flags = [\n '--disable-proto=delete',\n // Node.js 24+ uses --permission instead of --experimental-permission.\n // The permission model graduated from experimental to production-ready.\n major >= 24 ? '--permission' : '--experimental-permission',\n // Force uncaught exceptions policy for N-API addons (Node.js 22+).\n '--force-node-api-uncaught-exceptions-policy',\n ]\n // Only add policy flag if we're using experimental permission (Node < 24).\n // Node 24+ --policy requires a policy file which we don't have.\n if (major < 24) {\n flags.push('--experimental-policy')\n }\n _nodeHardenFlags = flags\n }\n return _nodeHardenFlags\n}\n\nlet _nodePermissionFlags: string[]\nexport function getNodePermissionFlags(): string[] {\n if (_nodePermissionFlags === undefined) {\n const major = getNodeMajorVersion()\n // Node.js 24+ requires explicit permission grants when using --permission flag.\n // npm needs filesystem access to read package.json files and node_modules.\n if (major >= 24) {\n _nodePermissionFlags = [\n // Allow reading from the entire filesystem (npm needs to read package.json, node_modules, etc.).\n '--allow-fs-read=*',\n // Allow writing to the entire filesystem (npm needs to write to node_modules, cache, etc.).\n '--allow-fs-write=*',\n // Allow spawning child processes (npm needs to run lifecycle scripts, git, etc.).\n '--allow-child-process',\n ]\n } else {\n // Node.js 20-23 with --experimental-permission doesn't require explicit grants\n // or uses different permission API.\n _nodePermissionFlags = []\n }\n }\n return _nodePermissionFlags\n}\n\nlet _nodeNoWarningsFlags: string[]\nexport function getNodeNoWarningsFlags(): string[] {\n if (_nodeNoWarningsFlags === undefined) {\n _nodeNoWarningsFlags = ['--no-warnings', '--no-deprecation']\n }\n return _nodeNoWarningsFlags\n}\n\n// Execution path.\nexport function getExecPath(): string {\n return process.execPath\n}\n\n// Node.js constants.\nexport const NODE_SEA_FUSE = 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2'\nexport const ESNEXT = 'esnext'\n"],
5
+ "mappings": "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",
6
+ "names": ["node_exports", "__export", "ESNEXT", "NODE_SEA_FUSE", "getExecPath", "getMaintainedNodeVersions", "getNodeDisableSigusr1Flags", "getNodeHardenFlags", "getNodeMajorVersion", "getNodeNoWarningsFlags", "getNodePermissionFlags", "getNodeVersion", "supportsNodeCompileCacheApi", "supportsNodeCompileCacheEnvVar", "supportsNodeDisableSigusr1Flag", "supportsNodeDisableWarningFlag", "supportsNodePermissionFlag", "supportsNodeRequireModule", "supportsNodeRun", "supportsProcessSend", "__toCommonJS", "NODE_VERSION", "_maintainedNodeVersions", "major", "_nodeDisableSigusr1Flags", "_nodeHardenFlags", "flags", "_nodePermissionFlags", "_nodeNoWarningsFlags"]
7
7
  }
@@ -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
  */
@@ -1,9 +1,9 @@
1
1
  /* Socket Lib - Built with esbuild */
2
- var z=Object.create;var P=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var q=(t,e)=>{for(var n in e)P(t,n,{get:e[n],enumerable:!0})},R=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of X(e))!Y.call(t,r)&&r!==n&&P(t,r,{get:()=>e[r],enumerable:!(i=K(e,r))||i.enumerable});return t};var T=(t,e,n)=>(n=t!=null?z(J(t)):{},R(e||!t||!t.__esModule?P(n,"default",{value:t,enumerable:!0}):n,t)),V=t=>R(P({},"__esModule",{value:!0}),t);var ee={};q(ee,{cleanDlxCache:()=>W,dlxBinary:()=>U,downloadBinary:()=>G,executeBinary:()=>Q,getDlxCachePath:()=>k,listDlxCache:()=>Z});module.exports=V(ee);var S=require("node:crypto"),o=require("node:fs"),D=T(require("node:os")),u=T(require("node:path")),$=require("#constants/platform"),_=require("./dlx"),j=require("./http-request"),p=require("./fs"),B=require("./objects"),O=require("./path"),v=require("./paths"),I=require("./process-lock"),A=require("./spawn");function x(t){return u.default.join(t,".dlx-metadata.json")}async function N(t,e){try{const n=x(t);if(!(0,o.existsSync)(n))return!1;const i=await(0,p.readJson)(n,{throws:!1});if(!(0,B.isObjectObject)(i))return!1;const r=Date.now(),a=i.timestamp;return typeof a!="number"||a<=0?!1:r-a<e}catch{return!1}}async function F(t,e,n){const i=u.default.dirname(e),r=u.default.join(i,"concurrency.lock");return await I.processLock.withLock(r,async()=>{if((0,o.existsSync)(e)&&(await o.promises.stat(e)).size>0){const f=await o.promises.readFile(e),s=(0,S.createHash)("sha256");return s.update(f),s.digest("hex")}try{await(0,j.httpDownload)(t,e)}catch(m){throw new Error(`Failed to download binary from ${t}
3
- Destination: ${e}
4
- Check your internet connection or verify the URL is accessible.`,{cause:m})}const a=await o.promises.readFile(e),c=(0,S.createHash)("sha256");c.update(a);const l=c.digest("hex");if(n&&l!==n)throw await(0,p.safeDelete)(e),new Error(`Checksum mismatch: expected ${n}, got ${l}`);return $.WIN32||await o.promises.chmod(e,493),l},{staleMs:5e3,touchIntervalMs:2e3})}async function L(t,e,n){const i=x(t),r={arch:D.default.arch(),checksum:n,platform:D.default.platform(),timestamp:Date.now(),url:e,version:"1.0.0"};await o.promises.writeFile(i,JSON.stringify(r,null,2))}async function W(t=require("#constants/time").DLX_BINARY_CACHE_TTL){const e=k();if(!(0,o.existsSync)(e))return 0;let n=0;const i=Date.now(),r=await o.promises.readdir(e);for(const a of r){const c=u.default.join(e,a),l=x(c);try{if(!await(0,p.isDir)(c))continue;const m=await(0,p.readJson)(l,{throws:!1});if(!m||typeof m!="object"||Array.isArray(m))continue;const f=m.timestamp;(typeof f=="number"&&f>0?i-f:Number.POSITIVE_INFINITY)>t&&(await(0,p.safeDelete)(c,{force:!0,recursive:!0}),n+=1)}catch{try{(await o.promises.readdir(c)).length||(await(0,p.safeDelete)(c),n+=1)}catch{}}}return n}async function U(t,e,n){const{cacheTtl:i=require("#constants/time").DLX_BINARY_CACHE_TTL,checksum:r,force:a=!1,name:c,spawnOptions:l,url:m}={__proto__:null,...e},f=k(),s=c||`binary-${process.platform}-${D.default.arch()}`,b=`${m}:${s}`,h=(0,_.generateCacheKey)(b),d=u.default.join(f,h),w=(0,O.normalizePath)(u.default.join(d,s));let y=!1,C=r;if(!a&&(0,o.existsSync)(d)&&await N(d,i))try{const E=x(d),g=await(0,p.readJson)(E,{throws:!1});g&&typeof g=="object"&&!Array.isArray(g)&&typeof g.checksum=="string"?C=g.checksum:y=!0}catch{y=!0}else y=!0;if(y){try{await o.promises.mkdir(d,{recursive:!0})}catch(E){const g=E.code;throw g==="EACCES"||g==="EPERM"?new Error(`Permission denied creating binary cache directory: ${d}
5
- Please check directory permissions or run with appropriate access.`,{cause:E}):g==="EROFS"?new Error(`Cannot create binary cache directory on read-only filesystem: ${d}
6
- Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`,{cause:E}):new Error(`Failed to create binary cache directory: ${d}`,{cause:E})}C=await F(m,w,r),await L(d,m,C||"")}const H=$.WIN32&&/\.(?:bat|cmd|ps1)$/i.test(w)?{...l,env:{...l?.env,PATH:`${d}${u.default.delimiter}${process.env.PATH||""}`},shell:!0}:l,M=(0,A.spawn)(w,t,H,n);return{binaryPath:w,downloaded:y,spawnPromise:M}}async function G(t){const{cacheTtl:e=require("#constants/time").DLX_BINARY_CACHE_TTL,checksum:n,force:i=!1,name:r,url:a}={__proto__:null,...t},c=k(),l=r||`binary-${process.platform}-${D.default.arch()}`,m=`${a}:${l}`,f=(0,_.generateCacheKey)(m),s=u.default.join(c,f),b=(0,O.normalizePath)(u.default.join(s,l));let h=!1;if(!i&&(0,o.existsSync)(s)&&await N(s,e))h=!1;else{try{await o.promises.mkdir(s,{recursive:!0})}catch(w){const y=w.code;throw y==="EACCES"||y==="EPERM"?new Error(`Permission denied creating binary cache directory: ${s}
7
- Please check directory permissions or run with appropriate access.`,{cause:w}):y==="EROFS"?new Error(`Cannot create binary cache directory on read-only filesystem: ${s}
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: ${s}`,{cause:w})}const d=await F(a,b,n);await L(s,a,d||""),h=!0}return{binaryPath:b,downloaded:h}}function Q(t,e,n,i){const r=$.WIN32&&/\.(?:bat|cmd|ps1)$/i.test(t),a=u.default.dirname(t),c=r?{...n,env:{...n?.env,PATH:`${a}${u.default.delimiter}${process.env.PATH||""}`},shell:!0}:n;return(0,A.spawn)(t,e,c,i)}function k(){return(0,v.getSocketDlxDir)()}async function Z(){const t=k();if(!(0,o.existsSync)(t))return[];const e=[],n=Date.now(),i=await o.promises.readdir(t);for(const r of i){const a=u.default.join(t,r);try{if(!await(0,p.isDir)(a))continue;const c=x(a),l=await(0,p.readJson)(c,{throws:!1});if(!l||typeof l!="object"||Array.isArray(l))continue;const f=(await o.promises.readdir(a)).find(s=>!s.startsWith("."));if(f){const s=u.default.join(a,f),b=await o.promises.stat(s),h=l;e.push({age:n-(h.timestamp||0),arch:h.arch||"unknown",checksum:h.checksum||"",name:f,platform:h.platform||"unknown",size:b.size,url:h.url||""})}}catch{}}return e}0&&(module.exports={cleanDlxCache,dlxBinary,downloadBinary,executeBinary,getDlxCachePath,listDlxCache});
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
@@ -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": "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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", "os", "maxAge", "cacheDir", "cleaned", "entries", "entry", "entryPath", "args", "options", "spawnExtra", "force", "name", "spawnOptions", "binaryName", "spec", "cacheKey", "binaryPath", "downloaded", "computedChecksum", "code", "finalSpawnOptions", "spawnPromise", "needsShell", "results", "binaryFile", "f", "binaryStats", "metaObj"]
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
  }
@@ -1,5 +1,5 @@
1
1
  /* Socket Lib - Built with esbuild */
2
- var f=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var E=(n,e)=>{for(var r in e)f(n,r,{get:e[r],enumerable:!0})},T=(n,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of w(e))!g.call(n,i)&&i!==r&&f(n,i,{get:()=>e[i],enumerable:!(s=v(e,i))||s.enumerable});return n};var S=n=>T(f({},"__esModule",{value:!0}),n);var L={};E(L,{processLock:()=>$});module.exports=S(L);var t=require("node:fs"),u=require("./fs"),d=require("./logger"),p=require("./promises"),h=require("./signal-exit");class x{activeLocks=new Set;touchTimers=new Map;exitHandlerRegistered=!1;ensureExitHandler(){this.exitHandlerRegistered||((0,h.onExit)(()=>{for(const e of this.touchTimers.values())clearInterval(e);this.touchTimers.clear();for(const e of this.activeLocks)try{(0,t.existsSync)(e)&&(0,u.safeDeleteSync)(e,{recursive:!0})}catch{}}),this.exitHandlerRegistered=!0)}touchLock(e){try{if((0,t.existsSync)(e)){const r=new Date;(0,t.utimesSync)(e,r,r)}}catch(r){d.logger.warn(`Failed to touch lock ${e}: ${r instanceof Error?r.message:String(r)}`)}}startTouchTimer(e,r){if(r<=0||this.touchTimers.has(e))return;const s=setInterval(()=>{this.touchLock(e)},r);s.unref(),this.touchTimers.set(e,s)}stopTouchTimer(e){const r=this.touchTimers.get(e);r&&(clearInterval(r),this.touchTimers.delete(e))}isStale(e,r){try{if(!(0,t.existsSync)(e))return!1;const s=(0,t.statSync)(e),i=Math.floor((Date.now()-s.mtime.getTime())/1e3),m=Math.floor(r/1e3);return i>m}catch{return!1}}async acquire(e,r={}){const{baseDelayMs:s=100,maxDelayMs:i=1e3,retries:m=3,staleMs:l=5e3,touchIntervalMs:y=2e3}=r;return this.ensureExitHandler(),await(0,p.pRetry)(async()=>{try{if((0,t.existsSync)(e)&&this.isStale(e,l)){d.logger.log(`Removing stale lock: ${e}`);try{(0,u.safeDeleteSync)(e,{recursive:!0})}catch{}}return(0,t.mkdirSync)(e,{recursive:!1}),this.activeLocks.add(e),this.startTouchTimer(e,y),()=>this.release(e)}catch(o){const a=o.code;if(a==="EEXIST")throw this.isStale(e,l)?new Error(`Stale lock detected: ${e}`):new Error(`Lock already exists: ${e}`);if(a==="EACCES"||a==="EPERM")throw new Error(`Permission denied creating lock: ${e}. Check directory permissions or run with appropriate access.`,{cause:o});if(a==="EROFS")throw new Error(`Cannot create lock on read-only filesystem: ${e}`,{cause:o});if(a==="ENOTDIR"){const c=e.slice(0,e.lastIndexOf("/"));throw new Error(`Cannot create lock directory: ${e}
2
+ var f=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var g=(n,e)=>{for(var r in e)f(n,r,{get:e[r],enumerable:!0})},T=(n,e,r,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of v(e))!E.call(n,s)&&s!==r&&f(n,s,{get:()=>e[s],enumerable:!(i=w(e,s))||i.enumerable});return n};var x=n=>T(f({},"__esModule",{value:!0}),n);var L={};g(L,{processLock:()=>$});module.exports=x(L);var t=require("node:fs"),u=require("./fs"),d=require("./logger"),p=require("./promises"),y=require("./signal-exit");class S{activeLocks=new Set;touchTimers=new Map;exitHandlerRegistered=!1;ensureExitHandler(){this.exitHandlerRegistered||((0,y.onExit)(()=>{for(const e of this.touchTimers.values())clearInterval(e);this.touchTimers.clear();for(const e of this.activeLocks)try{(0,t.existsSync)(e)&&(0,u.safeDeleteSync)(e,{recursive:!0})}catch{}}),this.exitHandlerRegistered=!0)}touchLock(e){try{if((0,t.existsSync)(e)){const r=new Date;(0,t.utimesSync)(e,r,r)}}catch(r){d.logger.warn(`Failed to touch lock ${e}: ${r instanceof Error?r.message:String(r)}`)}}startTouchTimer(e,r){if(r<=0||this.touchTimers.has(e))return;const i=setInterval(()=>{this.touchLock(e)},r);i.unref(),this.touchTimers.set(e,i)}stopTouchTimer(e){const r=this.touchTimers.get(e);r&&(clearInterval(r),this.touchTimers.delete(e))}isStale(e,r){try{if(!(0,t.existsSync)(e))return!1;const i=(0,t.statSync)(e),s=Math.floor((Date.now()-i.mtime.getTime())/1e3),m=Math.floor(r/1e3);return s>m}catch{return!1}}async acquire(e,r={}){const{baseDelayMs:i=100,maxDelayMs:s=1e3,retries:m=3,staleMs:l=5e3,touchIntervalMs:h=2e3}=r;return this.ensureExitHandler(),await(0,p.pRetry)(async()=>{try{if((0,t.existsSync)(e)&&this.isStale(e,l)){d.logger.log(`Removing stale lock: ${e}`);try{(0,u.safeDeleteSync)(e,{recursive:!0})}catch{}}if((0,t.existsSync)(e))throw new Error(`Lock already exists: ${e}`);return(0,t.mkdirSync)(e,{recursive:!0}),this.activeLocks.add(e),this.startTouchTimer(e,h),()=>this.release(e)}catch(o){const a=o.code;if(a==="EEXIST")throw this.isStale(e,l)?new Error(`Stale lock detected: ${e}`):new Error(`Lock already exists: ${e}`);if(a==="EACCES"||a==="EPERM")throw new Error(`Permission denied creating lock: ${e}. Check directory permissions or run with appropriate access.`,{cause:o});if(a==="EROFS")throw new Error(`Cannot create lock on read-only filesystem: ${e}`,{cause:o});if(a==="ENOTDIR"){const c=e.slice(0,e.lastIndexOf("/"));throw new Error(`Cannot create lock directory: ${e}
3
3
  A path component is a file when it should be a directory.
4
4
  Parent path: ${c}
5
5
  To resolve:
@@ -10,5 +10,5 @@ Parent directory does not exist: ${c}
10
10
  To resolve:
11
11
  1. Ensure the parent directory "${c}" exists
12
12
  2. Create the directory structure: mkdir -p "${c}"
13
- 3. Check filesystem permissions allow directory creation`,{cause:o})}throw new Error(`Failed to acquire lock: ${e}`,{cause:o})}},{retries:m,baseDelayMs:s,maxDelayMs:i,jitter:!0})}release(e){this.stopTouchTimer(e);try{(0,t.existsSync)(e)&&(0,u.safeDeleteSync)(e,{recursive:!0}),this.activeLocks.delete(e)}catch(r){d.logger.warn(`Failed to release lock ${e}: ${r instanceof Error?r.message:String(r)}`)}}async withLock(e,r,s){const i=await this.acquire(e,s);try{return await r()}finally{i()}}}const $=new x;0&&(module.exports={processLock});
13
+ 3. Check filesystem permissions allow directory creation`,{cause:o})}throw new Error(`Failed to acquire lock: ${e}`,{cause:o})}},{retries:m,baseDelayMs:i,maxDelayMs:s,jitter:!0})}release(e){this.stopTouchTimer(e);try{(0,t.existsSync)(e)&&(0,u.safeDeleteSync)(e,{recursive:!0}),this.activeLocks.delete(e)}catch(r){d.logger.warn(`Failed to release lock ${e}: ${r instanceof Error?r.message:String(r)}`)}}async withLock(e,r,i){const s=await this.acquire(e,i);try{return await r()}finally{s()}}}const $=new S;0&&(module.exports={processLock});
14
14
  //# sourceMappingURL=process-lock.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/process-lock.ts"],
4
- "sourcesContent": ["/**\n * @fileoverview Process locking utilities with stale detection and exit cleanup.\n * Provides cross-platform inter-process synchronization using directory-based locks.\n * Aligned with npm's npx locking strategy (5-second stale timeout, periodic touching).\n *\n * ## Why directories instead of files?\n *\n * This implementation uses `mkdir()` to create lock directories (not files) because:\n *\n * 1. **Atomic guarantee**: `mkdir()` is guaranteed atomic across ALL filesystems,\n * including NFS. Only ONE process can successfully create the directory. If it\n * exists, `mkdir()` fails with EEXIST instantly with no race conditions.\n *\n * 2. **File-based locking issues**:\n * - `writeFile()` with `flag: 'wx'` - atomicity can fail on NFS\n * - `open()` with `O_EXCL` - not guaranteed atomic on older NFS\n * - Traditional lockfiles - can have race conditions on network filesystems\n *\n * 3. **Simplicity**: No need to write/read file content, track PIDs, or manage\n * file descriptors. Just create/delete directory and check mtime.\n *\n * 4. **Historical precedent**: Well-known Unix locking pattern used by package\n * managers for decades. Git uses similar approach for `.git/index.lock`.\n *\n * ## The mtime trick\n *\n * We periodically update the lock directory's mtime (modification time) by\n * \"touching\" it to signal \"I'm still actively working\". This prevents other\n * processes from treating the lock as stale and removing it.\n *\n * **The lock directory remains empty** - it's just a sentinel that signals\n * \"locked\". The mtime is the only data needed to track lock freshness.\n *\n * ## npm npx compatibility\n *\n * This implementation matches npm npx's concurrency.lock approach:\n * - Lock created via `mkdir(path.join(installDir, 'concurrency.lock'))`\n * - 5-second stale timeout (if mtime is older than 5s, lock is stale)\n * - 2-second touching interval (updates mtime every 2s to keep lock fresh)\n * - Automatic cleanup on process exit\n */\n\nimport { existsSync, mkdirSync, statSync, utimesSync } from 'node:fs'\n\nimport { safeDeleteSync } from './fs'\nimport { logger } from './logger'\nimport { pRetry } from './promises'\nimport { onExit } from './signal-exit'\n\n/**\n * Lock acquisition options.\n */\nexport interface ProcessLockOptions {\n /**\n * Maximum number of retry attempts.\n * @default 3\n */\n retries?: number | undefined\n\n /**\n * Base delay between retries in milliseconds.\n * @default 100\n */\n baseDelayMs?: number | undefined\n\n /**\n * Maximum delay between retries in milliseconds.\n * @default 1000\n */\n maxDelayMs?: number | undefined\n\n /**\n * Stale lock timeout in milliseconds.\n * Locks older than this are considered abandoned and can be reclaimed.\n * Aligned with npm's npx locking strategy (5 seconds).\n * @default 5000 (5 seconds)\n */\n staleMs?: number | undefined\n\n /**\n * Interval for touching lock file to keep it fresh in milliseconds.\n * Set to 0 to disable periodic touching.\n * @default 2000 (2 seconds)\n */\n touchIntervalMs?: number | undefined\n}\n\n/**\n * Process lock manager with stale detection and exit cleanup.\n * Provides cross-platform inter-process synchronization using file-system\n * based locks.\n */\nclass ProcessLockManager {\n private activeLocks = new Set<string>()\n private touchTimers = new Map<string, NodeJS.Timeout>()\n private exitHandlerRegistered = false\n\n /**\n * Ensure process exit handler is registered for cleanup.\n * Registers a handler that cleans up all active locks when the process exits.\n */\n private ensureExitHandler() {\n if (this.exitHandlerRegistered) {\n return\n }\n\n onExit(() => {\n // Clear all touch timers.\n for (const timer of this.touchTimers.values()) {\n clearInterval(timer)\n }\n this.touchTimers.clear()\n\n // Clean up all active locks.\n for (const lockPath of this.activeLocks) {\n try {\n if (existsSync(lockPath)) {\n safeDeleteSync(lockPath, { recursive: true })\n }\n } catch {\n // Ignore cleanup errors during exit.\n }\n }\n })\n\n this.exitHandlerRegistered = true\n }\n\n /**\n * Touch a lock file to update its mtime.\n * This prevents the lock from being detected as stale during long operations.\n *\n * @param lockPath - Path to the lock directory\n */\n private touchLock(lockPath: string): void {\n try {\n if (existsSync(lockPath)) {\n const now = new Date()\n utimesSync(lockPath, now, now)\n }\n } catch (error) {\n logger.warn(\n `Failed to touch lock ${lockPath}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n\n /**\n * Start periodic touching of a lock file.\n * Aligned with npm npx strategy to prevent false stale detection.\n *\n * @param lockPath - Path to the lock directory\n * @param intervalMs - Touch interval in milliseconds\n */\n private startTouchTimer(lockPath: string, intervalMs: number): void {\n if (intervalMs <= 0 || this.touchTimers.has(lockPath)) {\n return\n }\n\n const timer = setInterval(() => {\n this.touchLock(lockPath)\n }, intervalMs)\n\n // Prevent timer from keeping process alive.\n timer.unref()\n\n this.touchTimers.set(lockPath, timer)\n }\n\n /**\n * Stop periodic touching of a lock file.\n *\n * @param lockPath - Path to the lock directory\n */\n private stopTouchTimer(lockPath: string): void {\n const timer = this.touchTimers.get(lockPath)\n if (timer) {\n clearInterval(timer)\n this.touchTimers.delete(lockPath)\n }\n }\n\n /**\n * Check if a lock is stale based on mtime.\n * Uses second-level granularity to avoid APFS floating-point precision issues.\n * Aligned with npm's npx locking strategy.\n *\n * @param lockPath - Path to the lock directory\n * @param staleMs - Stale timeout in milliseconds\n * @returns True if lock exists and is stale\n */\n private isStale(lockPath: string, staleMs: number): boolean {\n try {\n if (!existsSync(lockPath)) {\n return false\n }\n\n const stats = statSync(lockPath)\n // Use second-level granularity to avoid APFS issues.\n const ageSeconds = Math.floor((Date.now() - stats.mtime.getTime()) / 1000)\n const staleSeconds = Math.floor(staleMs / 1000)\n return ageSeconds > staleSeconds\n } catch {\n return false\n }\n }\n\n /**\n * Acquire a lock using mkdir for atomic operation.\n * Handles stale locks and includes exit cleanup.\n *\n * This method attempts to create a lock directory atomically. If the lock\n * already exists, it checks if it's stale and removes it before retrying.\n * Uses exponential backoff with jitter for retry attempts.\n *\n * @param lockPath - Path to the lock directory\n * @param options - Lock acquisition options\n * @returns Release function to unlock\n * @throws Error if lock cannot be acquired after all retries\n *\n * @example\n * ```typescript\n * const release = await processLock.acquire('/tmp/my-lock')\n * try {\n * // Critical section\n * } finally {\n * release()\n * }\n * ```\n */\n async acquire(\n lockPath: string,\n options: ProcessLockOptions = {},\n ): Promise<() => void> {\n const {\n baseDelayMs = 100,\n maxDelayMs = 1000,\n retries = 3,\n staleMs = 5000,\n touchIntervalMs = 2000,\n } = options\n\n // Ensure exit handler is registered before any lock acquisition.\n this.ensureExitHandler()\n\n return await pRetry(\n async () => {\n try {\n // Check for stale lock and remove if necessary.\n if (existsSync(lockPath) && this.isStale(lockPath, staleMs)) {\n logger.log(`Removing stale lock: ${lockPath}`)\n try {\n safeDeleteSync(lockPath, { recursive: true })\n } catch {\n // Ignore errors removing stale lock - will retry.\n }\n }\n\n // Atomic lock acquisition via mkdir.\n mkdirSync(lockPath, { recursive: false })\n\n // Track lock for cleanup.\n this.activeLocks.add(lockPath)\n\n // Start periodic touching to prevent stale detection.\n this.startTouchTimer(lockPath, touchIntervalMs)\n\n // Return release function.\n return () => this.release(lockPath)\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code\n\n // Handle lock contention - lock already exists.\n if (code === 'EEXIST') {\n if (this.isStale(lockPath, staleMs)) {\n throw new Error(`Stale lock detected: ${lockPath}`)\n }\n throw new Error(`Lock already exists: ${lockPath}`)\n }\n\n // Handle permission errors - not retryable.\n if (code === 'EACCES' || code === 'EPERM') {\n throw new Error(\n `Permission denied creating lock: ${lockPath}. ` +\n 'Check directory permissions or run with appropriate access.',\n { cause: error },\n )\n }\n\n // Handle read-only filesystem - not retryable.\n if (code === 'EROFS') {\n throw new Error(\n `Cannot create lock on read-only filesystem: ${lockPath}`,\n { cause: error },\n )\n }\n\n // Handle parent path issues - not retryable.\n if (code === 'ENOTDIR') {\n const parentDir = lockPath.slice(0, lockPath.lastIndexOf('/'))\n throw new Error(\n `Cannot create lock directory: ${lockPath}\\n` +\n 'A path component is a file when it should be a directory.\\n' +\n `Parent path: ${parentDir}\\n` +\n 'To resolve:\\n' +\n ` 1. Check if \"${parentDir}\" contains a file instead of a directory\\n` +\n ' 2. Remove any conflicting files in the path\\n' +\n ' 3. Ensure the full parent directory structure exists',\n { cause: error },\n )\n }\n\n if (code === 'ENOENT') {\n const parentDir = lockPath.slice(0, lockPath.lastIndexOf('/'))\n throw new Error(\n `Cannot create lock directory: ${lockPath}\\n` +\n `Parent directory does not exist: ${parentDir}\\n` +\n 'To resolve:\\n' +\n ` 1. Ensure the parent directory \"${parentDir}\" exists\\n` +\n ` 2. Create the directory structure: mkdir -p \"${parentDir}\"\\n` +\n ' 3. Check filesystem permissions allow directory creation',\n { cause: error },\n )\n }\n\n // Re-throw other errors with context.\n throw new Error(`Failed to acquire lock: ${lockPath}`, {\n cause: error,\n })\n }\n },\n {\n retries,\n baseDelayMs,\n maxDelayMs,\n jitter: true,\n },\n )\n }\n\n /**\n * Release a lock and remove from tracking.\n * Stops periodic touching and removes the lock directory.\n *\n * @param lockPath - Path to the lock directory\n *\n * @example\n * ```typescript\n * processLock.release('/tmp/my-lock')\n * ```\n */\n release(lockPath: string): void {\n // Stop periodic touching.\n this.stopTouchTimer(lockPath)\n\n try {\n if (existsSync(lockPath)) {\n safeDeleteSync(lockPath, { recursive: true })\n }\n this.activeLocks.delete(lockPath)\n } catch (error) {\n logger.warn(\n `Failed to release lock ${lockPath}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n\n /**\n * Execute a function with exclusive lock protection.\n * Automatically handles lock acquisition, execution, and cleanup.\n *\n * This is the recommended way to use process locks, as it guarantees\n * cleanup even if the callback throws an error.\n *\n * @param lockPath - Path to the lock directory\n * @param fn - Function to execute while holding the lock\n * @param options - Lock acquisition options\n * @returns Result of the callback function\n * @throws Error from callback or lock acquisition failure\n *\n * @example\n * ```typescript\n * const result = await processLock.withLock('/tmp/my-lock', async () => {\n * // Critical section\n * return someValue\n * })\n * ```\n */\n async withLock<T>(\n lockPath: string,\n fn: () => Promise<T>,\n options?: ProcessLockOptions,\n ): Promise<T> {\n const release = await this.acquire(lockPath, options)\n try {\n return await fn()\n } finally {\n release()\n }\n }\n}\n\n// Export singleton instance.\nexport const processLock = new ProcessLockManager()\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * @fileoverview Process locking utilities with stale detection and exit cleanup.\n * Provides cross-platform inter-process synchronization using directory-based locks.\n * Aligned with npm's npx locking strategy (5-second stale timeout, periodic touching).\n *\n * ## Why directories instead of files?\n *\n * This implementation uses `mkdir()` to create lock directories (not files) because:\n *\n * 1. **Atomic guarantee**: `mkdir()` is guaranteed atomic across ALL filesystems,\n * including NFS. Only ONE process can successfully create the directory. If it\n * exists, `mkdir()` fails with EEXIST instantly with no race conditions.\n *\n * 2. **File-based locking issues**:\n * - `writeFile()` with `flag: 'wx'` - atomicity can fail on NFS\n * - `open()` with `O_EXCL` - not guaranteed atomic on older NFS\n * - Traditional lockfiles - can have race conditions on network filesystems\n *\n * 3. **Simplicity**: No need to write/read file content, track PIDs, or manage\n * file descriptors. Just create/delete directory and check mtime.\n *\n * 4. **Historical precedent**: Well-known Unix locking pattern used by package\n * managers for decades. Git uses similar approach for `.git/index.lock`.\n *\n * ## The mtime trick\n *\n * We periodically update the lock directory's mtime (modification time) by\n * \"touching\" it to signal \"I'm still actively working\". This prevents other\n * processes from treating the lock as stale and removing it.\n *\n * **The lock directory remains empty** - it's just a sentinel that signals\n * \"locked\". The mtime is the only data needed to track lock freshness.\n *\n * ## npm npx compatibility\n *\n * This implementation matches npm npx's concurrency.lock approach:\n * - Lock created via `mkdir(path.join(installDir, 'concurrency.lock'))`\n * - 5-second stale timeout (if mtime is older than 5s, lock is stale)\n * - 2-second touching interval (updates mtime every 2s to keep lock fresh)\n * - Automatic cleanup on process exit\n */\n\nimport { existsSync, mkdirSync, statSync, utimesSync } from 'node:fs'\n\nimport { safeDeleteSync } from './fs'\nimport { logger } from './logger'\nimport { pRetry } from './promises'\nimport { onExit } from './signal-exit'\n\n/**\n * Lock acquisition options.\n */\nexport interface ProcessLockOptions {\n /**\n * Maximum number of retry attempts.\n * @default 3\n */\n retries?: number | undefined\n\n /**\n * Base delay between retries in milliseconds.\n * @default 100\n */\n baseDelayMs?: number | undefined\n\n /**\n * Maximum delay between retries in milliseconds.\n * @default 1000\n */\n maxDelayMs?: number | undefined\n\n /**\n * Stale lock timeout in milliseconds.\n * Locks older than this are considered abandoned and can be reclaimed.\n * Aligned with npm's npx locking strategy (5 seconds).\n * @default 5000 (5 seconds)\n */\n staleMs?: number | undefined\n\n /**\n * Interval for touching lock file to keep it fresh in milliseconds.\n * Set to 0 to disable periodic touching.\n * @default 2000 (2 seconds)\n */\n touchIntervalMs?: number | undefined\n}\n\n/**\n * Process lock manager with stale detection and exit cleanup.\n * Provides cross-platform inter-process synchronization using file-system\n * based locks.\n */\nclass ProcessLockManager {\n private activeLocks = new Set<string>()\n private touchTimers = new Map<string, NodeJS.Timeout>()\n private exitHandlerRegistered = false\n\n /**\n * Ensure process exit handler is registered for cleanup.\n * Registers a handler that cleans up all active locks when the process exits.\n */\n private ensureExitHandler() {\n if (this.exitHandlerRegistered) {\n return\n }\n\n onExit(() => {\n // Clear all touch timers.\n for (const timer of this.touchTimers.values()) {\n clearInterval(timer)\n }\n this.touchTimers.clear()\n\n // Clean up all active locks.\n for (const lockPath of this.activeLocks) {\n try {\n if (existsSync(lockPath)) {\n safeDeleteSync(lockPath, { recursive: true })\n }\n } catch {\n // Ignore cleanup errors during exit.\n }\n }\n })\n\n this.exitHandlerRegistered = true\n }\n\n /**\n * Touch a lock file to update its mtime.\n * This prevents the lock from being detected as stale during long operations.\n *\n * @param lockPath - Path to the lock directory\n */\n private touchLock(lockPath: string): void {\n try {\n if (existsSync(lockPath)) {\n const now = new Date()\n utimesSync(lockPath, now, now)\n }\n } catch (error) {\n logger.warn(\n `Failed to touch lock ${lockPath}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n\n /**\n * Start periodic touching of a lock file.\n * Aligned with npm npx strategy to prevent false stale detection.\n *\n * @param lockPath - Path to the lock directory\n * @param intervalMs - Touch interval in milliseconds\n */\n private startTouchTimer(lockPath: string, intervalMs: number): void {\n if (intervalMs <= 0 || this.touchTimers.has(lockPath)) {\n return\n }\n\n const timer = setInterval(() => {\n this.touchLock(lockPath)\n }, intervalMs)\n\n // Prevent timer from keeping process alive.\n timer.unref()\n\n this.touchTimers.set(lockPath, timer)\n }\n\n /**\n * Stop periodic touching of a lock file.\n *\n * @param lockPath - Path to the lock directory\n */\n private stopTouchTimer(lockPath: string): void {\n const timer = this.touchTimers.get(lockPath)\n if (timer) {\n clearInterval(timer)\n this.touchTimers.delete(lockPath)\n }\n }\n\n /**\n * Check if a lock is stale based on mtime.\n * Uses second-level granularity to avoid APFS floating-point precision issues.\n * Aligned with npm's npx locking strategy.\n *\n * @param lockPath - Path to the lock directory\n * @param staleMs - Stale timeout in milliseconds\n * @returns True if lock exists and is stale\n */\n private isStale(lockPath: string, staleMs: number): boolean {\n try {\n if (!existsSync(lockPath)) {\n return false\n }\n\n const stats = statSync(lockPath)\n // Use second-level granularity to avoid APFS issues.\n const ageSeconds = Math.floor((Date.now() - stats.mtime.getTime()) / 1000)\n const staleSeconds = Math.floor(staleMs / 1000)\n return ageSeconds > staleSeconds\n } catch {\n return false\n }\n }\n\n /**\n * Acquire a lock using mkdir for atomic operation.\n * Handles stale locks and includes exit cleanup.\n *\n * This method attempts to create a lock directory atomically. If the lock\n * already exists, it checks if it's stale and removes it before retrying.\n * Uses exponential backoff with jitter for retry attempts.\n *\n * @param lockPath - Path to the lock directory\n * @param options - Lock acquisition options\n * @returns Release function to unlock\n * @throws Error if lock cannot be acquired after all retries\n *\n * @example\n * ```typescript\n * const release = await processLock.acquire('/tmp/my-lock')\n * try {\n * // Critical section\n * } finally {\n * release()\n * }\n * ```\n */\n async acquire(\n lockPath: string,\n options: ProcessLockOptions = {},\n ): Promise<() => void> {\n const {\n baseDelayMs = 100,\n maxDelayMs = 1000,\n retries = 3,\n staleMs = 5000,\n touchIntervalMs = 2000,\n } = options\n\n // Ensure exit handler is registered before any lock acquisition.\n this.ensureExitHandler()\n\n return await pRetry(\n async () => {\n try {\n // Check for stale lock and remove if necessary.\n if (existsSync(lockPath) && this.isStale(lockPath, staleMs)) {\n logger.log(`Removing stale lock: ${lockPath}`)\n try {\n safeDeleteSync(lockPath, { recursive: true })\n } catch {\n // Ignore errors removing stale lock - will retry.\n }\n }\n\n // Check if lock already exists before creating.\n if (existsSync(lockPath)) {\n throw new Error(`Lock already exists: ${lockPath}`)\n }\n\n // Atomic lock acquisition via mkdir with recursive to create parent dirs.\n mkdirSync(lockPath, { recursive: true })\n\n // Track lock for cleanup.\n this.activeLocks.add(lockPath)\n\n // Start periodic touching to prevent stale detection.\n this.startTouchTimer(lockPath, touchIntervalMs)\n\n // Return release function.\n return () => this.release(lockPath)\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code\n\n // Handle lock contention - lock already exists.\n if (code === 'EEXIST') {\n if (this.isStale(lockPath, staleMs)) {\n throw new Error(`Stale lock detected: ${lockPath}`)\n }\n throw new Error(`Lock already exists: ${lockPath}`)\n }\n\n // Handle permission errors - not retryable.\n if (code === 'EACCES' || code === 'EPERM') {\n throw new Error(\n `Permission denied creating lock: ${lockPath}. ` +\n 'Check directory permissions or run with appropriate access.',\n { cause: error },\n )\n }\n\n // Handle read-only filesystem - not retryable.\n if (code === 'EROFS') {\n throw new Error(\n `Cannot create lock on read-only filesystem: ${lockPath}`,\n { cause: error },\n )\n }\n\n // Handle parent path issues - not retryable.\n if (code === 'ENOTDIR') {\n const parentDir = lockPath.slice(0, lockPath.lastIndexOf('/'))\n throw new Error(\n `Cannot create lock directory: ${lockPath}\\n` +\n 'A path component is a file when it should be a directory.\\n' +\n `Parent path: ${parentDir}\\n` +\n 'To resolve:\\n' +\n ` 1. Check if \"${parentDir}\" contains a file instead of a directory\\n` +\n ' 2. Remove any conflicting files in the path\\n' +\n ' 3. Ensure the full parent directory structure exists',\n { cause: error },\n )\n }\n\n if (code === 'ENOENT') {\n const parentDir = lockPath.slice(0, lockPath.lastIndexOf('/'))\n throw new Error(\n `Cannot create lock directory: ${lockPath}\\n` +\n `Parent directory does not exist: ${parentDir}\\n` +\n 'To resolve:\\n' +\n ` 1. Ensure the parent directory \"${parentDir}\" exists\\n` +\n ` 2. Create the directory structure: mkdir -p \"${parentDir}\"\\n` +\n ' 3. Check filesystem permissions allow directory creation',\n { cause: error },\n )\n }\n\n // Re-throw other errors with context.\n throw new Error(`Failed to acquire lock: ${lockPath}`, {\n cause: error,\n })\n }\n },\n {\n retries,\n baseDelayMs,\n maxDelayMs,\n jitter: true,\n },\n )\n }\n\n /**\n * Release a lock and remove from tracking.\n * Stops periodic touching and removes the lock directory.\n *\n * @param lockPath - Path to the lock directory\n *\n * @example\n * ```typescript\n * processLock.release('/tmp/my-lock')\n * ```\n */\n release(lockPath: string): void {\n // Stop periodic touching.\n this.stopTouchTimer(lockPath)\n\n try {\n if (existsSync(lockPath)) {\n safeDeleteSync(lockPath, { recursive: true })\n }\n this.activeLocks.delete(lockPath)\n } catch (error) {\n logger.warn(\n `Failed to release lock ${lockPath}: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n\n /**\n * Execute a function with exclusive lock protection.\n * Automatically handles lock acquisition, execution, and cleanup.\n *\n * This is the recommended way to use process locks, as it guarantees\n * cleanup even if the callback throws an error.\n *\n * @param lockPath - Path to the lock directory\n * @param fn - Function to execute while holding the lock\n * @param options - Lock acquisition options\n * @returns Result of the callback function\n * @throws Error from callback or lock acquisition failure\n *\n * @example\n * ```typescript\n * const result = await processLock.withLock('/tmp/my-lock', async () => {\n * // Critical section\n * return someValue\n * })\n * ```\n */\n async withLock<T>(\n lockPath: string,\n fn: () => Promise<T>,\n options?: ProcessLockOptions,\n ): Promise<T> {\n const release = await this.acquire(lockPath, options)\n try {\n return await fn()\n } finally {\n release()\n }\n }\n}\n\n// Export singleton instance.\nexport const processLock = new ProcessLockManager()\n"],
5
+ "mappings": "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",
6
6
  "names": ["process_lock_exports", "__export", "processLock", "__toCommonJS", "import_node_fs", "import_fs", "import_logger", "import_promises", "import_signal_exit", "ProcessLockManager", "timer", "lockPath", "now", "error", "intervalMs", "staleMs", "stats", "ageSeconds", "staleSeconds", "options", "baseDelayMs", "maxDelayMs", "retries", "touchIntervalMs", "code", "parentDir", "fn", "release"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socketsecurity/lib",
3
- "version": "2.9.1",
3
+ "version": "2.10.1",
4
4
  "license": "MIT",
5
5
  "description": "Core utilities and infrastructure for Socket.dev security tools",
6
6
  "keywords": [