@socketsecurity/lib 2.10.0 → 2.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/constants/node.d.ts +0 -1
- package/dist/constants/node.js +1 -1
- package/dist/constants/node.js.map +3 -3
- package/dist/dlx-package.js +6 -6
- package/dist/dlx-package.js.map +3 -3
- package/dist/process-lock.js +2 -2
- package/dist/process-lock.js.map +2 -2
- package/dist/versions.js +1 -1
- package/dist/versions.js.map +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ 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.2](https://github.com/SocketDev/socket-lib/releases/tag/v2.10.2) - 2025-10-31
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Package spec parsing**: Refactored to use official `npm-package-arg` library for robust handling of all npm package specification formats (versions, ranges, tags, git URLs)
|
|
13
|
+
- Improves reliability when parsing complex package specs
|
|
14
|
+
- Better handles edge cases in version ranges and scoped packages
|
|
15
|
+
- Falls back to simple parsing if npm-package-arg fails
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Scoped package version parsing**: Fixed critical bug where parsePackageSpec was stripping the `@` prefix from scoped packages with versions
|
|
20
|
+
- Example: `@coana-tech/cli@~14.12.51` was incorrectly parsed as `coana-tech/cli@~14.12.51`
|
|
21
|
+
- Caused package installation failures for scoped packages in DLX system
|
|
22
|
+
|
|
23
|
+
## [2.10.1](https://github.com/SocketDev/socket-lib/releases/tag/v2.10.1) - 2025-10-31
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **Process lock directory creation**: Use recursive mkdir to ensure parent directories exist when creating lock directory
|
|
28
|
+
- **Node.js debug flags**: Remove buggy `getNodeDebugFlags()` function that returned debug flags without required argument values
|
|
29
|
+
|
|
8
30
|
## [2.10.0](https://github.com/SocketDev/socket-lib/releases/tag/v2.10.0) - 2025-10-30
|
|
9
31
|
|
|
10
32
|
### Added
|
package/dist/constants/node.d.ts
CHANGED
|
@@ -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[];
|
package/dist/constants/node.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/* Socket Lib - Built with esbuild */
|
|
2
|
-
var u=Object.defineProperty;var
|
|
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
|
|
5
|
-
"mappings": ";4ZAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,YAAAE,EAAA,kBAAAC,EAAA,gBAAAC,EAAA,8BAAAC,EAAA
|
|
6
|
-
"names": ["node_exports", "__export", "ESNEXT", "NODE_SEA_FUSE", "getExecPath", "getMaintainedNodeVersions", "
|
|
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
|
}
|
package/dist/dlx-package.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* Socket Lib - Built with esbuild */
|
|
2
|
-
var v=Object.create;var
|
|
3
|
-
Please check directory permissions or run with appropriate access.`,{cause:
|
|
4
|
-
Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`,{cause:
|
|
2
|
+
var v=Object.create;var y=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var C=Object.getPrototypeOf,I=Object.prototype.hasOwnProperty;var T=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},w=(n,e,t,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of $(e))!I.call(n,r)&&r!==t&&y(n,r,{get:()=>e[r],enumerable:!(a=_(e,r))||a.enumerable});return n};var A=(n,e,t)=>(t=n!=null?v(C(n)):{},w(e||!n||!n.__esModule?y(t,"default",{value:n,enumerable:!0}):t,n)),N=n=>w(y({},"__esModule",{value:!0}),n);var B={};T(B,{dlxPackage:()=>L,downloadPackage:()=>R,executePackage:()=>j});module.exports=N(B);var l=require("node:fs"),d=A(require("node:path")),P=require("./constants/platform"),E=require("./constants/packages"),x=require("./dlx"),b=require("./fs"),u=require("./path"),D=require("./paths"),O=require("./process-lock"),S=require("./spawn");let k;function J(){return k===void 0&&(k=require("./external/npm-package-arg")),k}let h;function F(){return h===void 0&&(h=require("./external/pacote")),h}const K=/[~^><=xX* ]|\|\|/;function M(n){try{const t=J()(n),a=t.type==="tag"||t.type==="version"||t.type==="range"?t.fetchSpec:void 0;return{name:t.name||n,version:a}}catch{const e=n.lastIndexOf("@");return e===-1||n.startsWith("@")?{name:n,version:void 0}:{name:n.slice(0,e),version:n.slice(e+1)}}}async function V(n,e,t){const a=(0,x.generateCacheKey)(e),r=(0,u.normalizePath)(d.default.join((0,D.getSocketDlxDir)(),a)),c=(0,u.normalizePath)(d.default.join(r,"node_modules",n));try{await l.promises.mkdir(r,{recursive:!0})}catch(i){const o=i.code;throw o==="EACCES"||o==="EPERM"?new Error(`Permission denied creating package directory: ${r}
|
|
3
|
+
Please check directory permissions or run with appropriate access.`,{cause:i}):o==="EROFS"?new Error(`Cannot create package directory on read-only filesystem: ${r}
|
|
4
|
+
Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`,{cause:i}):new Error(`Failed to create package directory: ${r}`,{cause:i})}const p=d.default.join(r,"concurrency.lock");return await O.processLock.withLock(p,async()=>{if(!t&&(0,l.existsSync)(c)){const o=d.default.join(c,"package.json");if((0,l.existsSync)(o))return{installed:!1,packageDir:r}}const i=(0,E.getPacoteCachePath)();try{await F().extract(e,c,{cache:i||d.default.join(r,".cache")})}catch(o){const s=o.code;throw s==="E404"||s==="ETARGET"?new Error(`Package not found: ${e}
|
|
5
5
|
Verify the package exists on npm registry and check the version.
|
|
6
|
-
Visit https://www.npmjs.com/package/${
|
|
7
|
-
Check your internet connection and try again.`,{cause:o}):new Error(`Failed to install package: ${
|
|
6
|
+
Visit https://www.npmjs.com/package/${n} to see available versions.`,{cause:o}):s==="ENOTFOUND"||s==="ETIMEDOUT"||s==="EAI_AGAIN"?new Error(`Network error installing ${e}
|
|
7
|
+
Check your internet connection and try again.`,{cause:o}):new Error(`Failed to install package: ${e}
|
|
8
8
|
Destination: ${c}
|
|
9
|
-
Check npm registry connectivity or package name.`,{cause:o})}return{installed:!0,packageDir:r}},{staleMs:5e3,touchIntervalMs:2e3})}function
|
|
9
|
+
Check npm registry connectivity or package name.`,{cause:o})}return{installed:!0,packageDir:r}},{staleMs:5e3,touchIntervalMs:2e3})}function q(n,e,t){const a=(0,u.normalizePath)(d.default.join(n,"node_modules",e)),r=d.default.join(a,"package.json"),p=(0,b.readJsonSync)(r).bin;let i;if(typeof p=="string")i=p;else if(typeof p=="object"&&p!==null){const o=p,s=Object.keys(o);if(s.length===1)i=o[s[0]];else{const m=e.split("/").pop(),g=[t,m,e.replace(/^@[^/]+\//,"")].filter(Boolean);for(const f of g)if(f&&o[f]){i=o[f];break}!i&&s.length>0&&(i=o[s[0]])}}if(!i)throw new Error(`No binary found for package "${e}"`);return(0,u.normalizePath)(d.default.join(a,i))}async function L(n,e,t){const a=await R(e),r=j(a.binaryPath,n,e?.spawnOptions,t);return{...a,spawnPromise:r}}async function R(n){const{binaryName:e,force:t,package:a}={__proto__:null,...n},{name:r,version:c}=M(a),p=c!==void 0&&K.test(c),i=t!==void 0?t:p,o=c?`${r}@${c}`:r,{installed:s,packageDir:m}=await V(r,o,i),g=q(m,r,e);if(!P.WIN32&&(0,l.existsSync)(g)){const{chmodSync:f}=require("node:fs");try{f(g,493)}catch{}}return{binaryPath:g,installed:s,packageDir:m}}function j(n,e,t,a){return(0,S.spawn)(n,e,t,a)}0&&(module.exports={dlxPackage,downloadPackage,executePackage});
|
|
10
10
|
//# sourceMappingURL=dlx-package.js.map
|
package/dist/dlx-package.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/dlx-package.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @fileoverview DLX package execution - Install and execute npm packages.\n *\n * This module provides functionality to install and execute npm packages\n * in the ~/.socket/_dlx directory, similar to npx but with Socket's own cache.\n *\n * Uses content-addressed storage like npm's _npx:\n * - Hash is generated from package spec (name@version)\n * - Each unique spec gets its own directory: ~/.socket/_dlx/<hash>/\n * - Allows caching multiple versions of the same package\n *\n * Concurrency protection:\n * - Uses process-lock to prevent concurrent installation corruption\n * - Lock file created at ~/.socket/_dlx/<hash>/concurrency.lock\n * - Uses npm npx's concurrency.lock naming convention (5s stale, 2s touching)\n * - Prevents multiple processes from corrupting the same package installation\n *\n * Version range handling:\n * - Exact versions (1.0.0) use cache if available\n * - Range versions (^1.0.0, ~1.0.0) auto-force to get latest within range\n * - User can override with explicit force: false\n *\n * Key difference from dlx-binary.ts:\n * - dlx-binary.ts: Downloads standalone binaries from URLs\n * - dlx-package.ts: Installs npm packages from registries\n *\n * Implementation:\n * - Uses pacote for package installation (no npm CLI required)\n * - Split into downloadPackage() and executePackage() for flexibility\n * - dlxPackage() combines both for convenience\n */\n\nimport { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport { WIN32 } from './constants/platform'\nimport { getPacoteCachePath } from './constants/packages'\nimport { generateCacheKey } from './dlx'\nimport pacote from './external/pacote'\nimport { readJsonSync } from './fs'\nimport { normalizePath } from './path'\nimport { getSocketDlxDir } from './paths'\nimport { processLock } from './process-lock'\nimport type { SpawnExtra, SpawnOptions } from './spawn'\nimport { spawn } from './spawn'\n\n/**\n * Regex to check if a version string contains range operators.\n * Matches any version with range operators: ~, ^, >, <, =, x, X, *, spaces, or ||.\n */\nconst rangeOperatorsRegExp = /[~^><=xX* ]|\\|\\|/\n\nexport interface DownloadPackageResult {\n /** Path to the installed package directory. */\n packageDir: string\n /** Path to the binary. */\n binaryPath: string\n /** Whether the package was newly installed. */\n installed: boolean\n}\n\nexport interface DlxPackageOptions {\n /**\n * Package to install (e.g., '@cyclonedx/cdxgen@10.0.0').\n */\n package: string\n /**\n * Binary name to execute (optional - auto-detected in most cases).\n *\n * Auto-detection logic:\n * 1. If package has only one binary, uses it automatically\n * 2. Tries user-provided binaryName\n * 3. Tries last segment of package name (e.g., 'cli' from '@socketsecurity/cli')\n * 4. Falls back to first binary\n *\n * Only needed when package has multiple binaries and auto-detection fails.\n *\n * @example\n * // Auto-detected (single binary)\n * { package: '@socketsecurity/cli' } // Finds 'socket' binary automatically\n *\n * // Explicit (multiple binaries)\n * { package: 'some-tool', binaryName: 'specific-tool' }\n */\n binaryName?: string | undefined\n /**\n * Force reinstallation even if package exists.\n */\n force?: boolean | undefined\n /**\n * Additional spawn options for the execution.\n */\n spawnOptions?: SpawnOptions | undefined\n}\n\nexport interface DlxPackageResult {\n /** Path to the installed package directory. */\n packageDir: string\n /** Path to the binary that was executed. */\n binaryPath: string\n /** Whether the package was newly installed. */\n installed: boolean\n /** The spawn promise for the running process. */\n spawnPromise: ReturnType<typeof spawn>\n}\n\n/**\n * Parse package spec into name and version.\n * Examples:\n * - 'lodash@4.17.21' \u2192 { name: 'lodash', version: '4.17.21' }\n * - '@scope/pkg@1.0.0' \u2192 { name: '@scope/pkg', version: '1.0.0' }\n * - 'lodash' \u2192 { name: 'lodash', version: undefined }\n */\nfunction parsePackageSpec(spec: string): {\n name: string\n version: string | undefined\n} {\n // Handle scoped packages (@scope/name@version).\n if (spec.startsWith('@')) {\n const parts = spec.split('@')\n if (parts.length === 3) {\n // @scope@version -> Invalid, but handle gracefully.\n return { name: parts[1], version: parts[2] }\n }\n if (parts.length === 2) {\n // @scope/name with no version.\n return { name: `@${parts[1]}`, version: undefined }\n }\n // @scope/name@version.\n const scopeAndName = `@${parts[1]}`\n return { name: scopeAndName, version: parts[2] }\n }\n\n // Handle unscoped packages (name@version).\n const atIndex = spec.lastIndexOf('@')\n if (atIndex === -1) {\n return { name: spec, version: undefined }\n }\n\n return {\n name: spec.slice(0, atIndex),\n version: spec.slice(atIndex + 1),\n }\n}\n\n/**\n * Install package to ~/.socket/_dlx/<hash>/ if not already installed.\n * Uses pacote for installation (no npm CLI required).\n * Protected by process lock to prevent concurrent installation corruption.\n */\nasync function ensurePackageInstalled(\n packageName: string,\n packageSpec: string,\n force: boolean,\n): Promise<{ installed: boolean; packageDir: string }> {\n const cacheKey = generateCacheKey(packageSpec)\n const packageDir = normalizePath(path.join(getSocketDlxDir(), cacheKey))\n const installedDir = normalizePath(\n path.join(packageDir, 'node_modules', packageName),\n )\n\n // Ensure package directory exists before creating lock.\n // The lock directory will be created inside this directory.\n try {\n await fs.mkdir(packageDir, { 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 package directory: ${packageDir}\\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 package directory on read-only filesystem: ${packageDir}\\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(`Failed to create package directory: ${packageDir}`, {\n cause: e,\n })\n }\n\n // Use process lock to prevent concurrent installations.\n // Uses npm npx's concurrency.lock naming convention.\n const lockPath = path.join(packageDir, 'concurrency.lock')\n\n return await processLock.withLock(\n lockPath,\n async () => {\n // Double-check if already installed (unless force).\n // Another process may have installed while waiting for lock.\n if (!force && existsSync(installedDir)) {\n // Verify package.json exists.\n const pkgJsonPath = path.join(installedDir, 'package.json')\n if (existsSync(pkgJsonPath)) {\n return { installed: false, packageDir }\n }\n }\n\n // Use pacote to extract the package.\n // Pacote leverages npm cache when available but doesn't require npm CLI.\n const pacoteCachePath = getPacoteCachePath()\n try {\n await pacote.extract(packageSpec, installedDir, {\n // Use consistent pacote cache path (respects npm cache locations when available).\n cache: pacoteCachePath || path.join(packageDir, '.cache'),\n })\n } catch (e) {\n const code = (e as any).code\n if (code === 'E404' || code === 'ETARGET') {\n throw new Error(\n `Package not found: ${packageSpec}\\n` +\n 'Verify the package exists on npm registry and check the version.\\n' +\n `Visit https://www.npmjs.com/package/${packageName} to see available versions.`,\n { cause: e },\n )\n }\n if (\n code === 'ENOTFOUND' ||\n code === 'ETIMEDOUT' ||\n code === 'EAI_AGAIN'\n ) {\n throw new Error(\n `Network error installing ${packageSpec}\\n` +\n 'Check your internet connection and try again.',\n { cause: e },\n )\n }\n throw new Error(\n `Failed to install package: ${packageSpec}\\n` +\n `Destination: ${installedDir}\\n` +\n 'Check npm registry connectivity or package name.',\n { cause: e },\n )\n }\n\n return { installed: true, packageDir }\n },\n {\n // Align with npm npx locking strategy.\n staleMs: 5000,\n touchIntervalMs: 2000,\n },\n )\n}\n\n/**\n * Find the binary path for an installed package.\n * Intelligently handles packages with single or multiple binaries.\n */\nfunction findBinaryPath(\n packageDir: string,\n packageName: string,\n binaryName?: string,\n): string {\n const installedDir = normalizePath(\n path.join(packageDir, 'node_modules', packageName),\n )\n const pkgJsonPath = path.join(installedDir, 'package.json')\n\n // Read package.json to find bin entry.\n const pkgJson = readJsonSync(pkgJsonPath) as Record<string, unknown>\n const bin = pkgJson['bin']\n\n let binPath: string | undefined\n\n if (typeof bin === 'string') {\n // Single binary - use it directly.\n binPath = bin\n } else if (typeof bin === 'object' && bin !== null) {\n const binObj = bin as Record<string, string>\n const binKeys = Object.keys(binObj)\n\n // If only one binary, use it regardless of name.\n if (binKeys.length === 1) {\n binPath = binObj[binKeys[0]!]\n } else {\n // Multiple binaries - try to find the right one:\n // 1. User-provided binaryName\n // 2. Last segment of package name (e.g., 'cli' from '@socketsecurity/cli')\n // 3. Full package name without scope (e.g., 'cli' from '@socketsecurity/cli')\n // 4. First binary as fallback\n const lastSegment = packageName.split('/').pop()\n const candidates = [\n binaryName,\n lastSegment,\n packageName.replace(/^@[^/]+\\//, ''),\n ].filter(Boolean)\n\n for (const candidate of candidates) {\n if (candidate && binObj[candidate]) {\n binPath = binObj[candidate]\n break\n }\n }\n\n // Fallback to first binary if nothing matched.\n if (!binPath && binKeys.length > 0) {\n binPath = binObj[binKeys[0]!]\n }\n }\n }\n\n if (!binPath) {\n throw new Error(`No binary found for package \"${packageName}\"`)\n }\n\n return normalizePath(path.join(installedDir, binPath))\n}\n\n/**\n * Execute a package via DLX - install if needed and run its binary.\n *\n * This is the Socket equivalent of npx/pnpm dlx/yarn dlx, but using\n * our own cache directory (~/.socket/_dlx) and installation logic.\n *\n * Auto-forces reinstall for version ranges to get latest within range.\n *\n * @example\n * ```typescript\n * // Download and execute cdxgen\n * const result = await dlxPackage(\n * ['--version'],\n * { package: '@cyclonedx/cdxgen@10.0.0' }\n * )\n * await result.spawnPromise\n * ```\n */\nexport async function dlxPackage(\n args: readonly string[] | string[],\n options?: DlxPackageOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<DlxPackageResult> {\n // Download the package.\n const downloadResult = await downloadPackage(options!)\n\n // Execute the binary.\n const spawnPromise = executePackage(\n downloadResult.binaryPath,\n args,\n options?.spawnOptions,\n spawnExtra,\n )\n\n return {\n ...downloadResult,\n spawnPromise,\n }\n}\n\n/**\n * Download and install a package without executing it.\n * This is useful for self-update or when you need the package files\n * but don't want to run the binary immediately.\n *\n * @example\n * ```typescript\n * // Install @socketsecurity/cli without running it\n * const result = await downloadPackage({\n * package: '@socketsecurity/cli@1.2.0',\n * force: true\n * })\n * console.log('Installed to:', result.packageDir)\n * console.log('Binary at:', result.binaryPath)\n * ```\n */\nexport async function downloadPackage(\n options: DlxPackageOptions,\n): Promise<DownloadPackageResult> {\n const {\n binaryName,\n force: userForce,\n package: packageSpec,\n } = {\n __proto__: null,\n ...options,\n } as DlxPackageOptions\n\n // Parse package spec.\n const { name: packageName, version: packageVersion } =\n parsePackageSpec(packageSpec)\n\n // Auto-force for version ranges to get latest within range.\n // User can still override with explicit force: false if they want cache.\n const isVersionRange =\n packageVersion !== undefined && rangeOperatorsRegExp.test(packageVersion)\n const force = userForce !== undefined ? userForce : isVersionRange\n\n // Build full package spec for installation.\n const fullPackageSpec = packageVersion\n ? `${packageName}@${packageVersion}`\n : packageName\n\n // Ensure package is installed.\n const { installed, packageDir } = await ensurePackageInstalled(\n packageName,\n fullPackageSpec,\n force,\n )\n\n // Find binary path.\n const binaryPath = findBinaryPath(packageDir, packageName, binaryName)\n\n // Make binary executable on Unix systems.\n if (!WIN32 && existsSync(binaryPath)) {\n const { chmodSync } = require('node:fs')\n try {\n chmodSync(binaryPath, 0o755)\n } catch {\n // Ignore chmod errors.\n }\n }\n\n return {\n binaryPath,\n installed,\n packageDir,\n }\n}\n\n/**\n * Execute a package's binary.\n * The package must already be installed (use downloadPackage first).\n *\n * @example\n * ```typescript\n * // Execute an already-installed package\n * const downloaded = await downloadPackage({ package: 'cowsay@1.5.0' })\n * const result = await executePackage(\n * downloaded.binaryPath,\n * ['Hello World'],\n * { stdio: 'inherit' }\n * )\n * ```\n */\nexport function executePackage(\n binaryPath: string,\n args: readonly string[] | string[],\n spawnOptions?: SpawnOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): ReturnType<typeof spawn> {\n return spawn(binaryPath, args, spawnOptions, spawnExtra)\n}\n"],
|
|
5
|
-
"mappings": ";6iBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gBAAAE,EAAA,oBAAAC,EAAA,mBAAAC,IAAA,eAAAC,EAAAL,GAgCA,IAAAM,EAA2C,mBAC3CC,EAAiB,wBAEjBC,EAAsB,gCACtBC,EAAmC,gCACnCC,EAAiC,iBACjCC,
|
|
6
|
-
"names": ["dlx_package_exports", "__export", "dlxPackage", "downloadPackage", "executePackage", "__toCommonJS", "import_node_fs", "import_node_path", "import_platform", "import_packages", "import_dlx", "
|
|
4
|
+
"sourcesContent": ["/**\n * @fileoverview DLX package execution - Install and execute npm packages.\n *\n * This module provides functionality to install and execute npm packages\n * in the ~/.socket/_dlx directory, similar to npx but with Socket's own cache.\n *\n * Uses content-addressed storage like npm's _npx:\n * - Hash is generated from package spec (name@version)\n * - Each unique spec gets its own directory: ~/.socket/_dlx/<hash>/\n * - Allows caching multiple versions of the same package\n *\n * Concurrency protection:\n * - Uses process-lock to prevent concurrent installation corruption\n * - Lock file created at ~/.socket/_dlx/<hash>/concurrency.lock\n * - Uses npm npx's concurrency.lock naming convention (5s stale, 2s touching)\n * - Prevents multiple processes from corrupting the same package installation\n *\n * Version range handling:\n * - Exact versions (1.0.0) use cache if available\n * - Range versions (^1.0.0, ~1.0.0) auto-force to get latest within range\n * - User can override with explicit force: false\n *\n * Key difference from dlx-binary.ts:\n * - dlx-binary.ts: Downloads standalone binaries from URLs\n * - dlx-package.ts: Installs npm packages from registries\n *\n * Implementation:\n * - Uses pacote for package installation (no npm CLI required)\n * - Split into downloadPackage() and executePackage() for flexibility\n * - dlxPackage() combines both for convenience\n */\n\nimport { existsSync, promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nimport { WIN32 } from './constants/platform'\nimport { getPacoteCachePath } from './constants/packages'\nimport { generateCacheKey } from './dlx'\nimport { readJsonSync } from './fs'\nimport { normalizePath } from './path'\nimport { getSocketDlxDir } from './paths'\nimport { processLock } from './process-lock'\nimport type { SpawnExtra, SpawnOptions } from './spawn'\nimport { spawn } from './spawn'\n\nlet _npmPackageArg: typeof import('npm-package-arg') | undefined\n/*@__NO_SIDE_EFFECTS__*/\nfunction getNpmPackageArg() {\n if (_npmPackageArg === undefined) {\n _npmPackageArg = /*@__PURE__*/ require('./external/npm-package-arg')\n }\n return _npmPackageArg as typeof import('npm-package-arg')\n}\n\nlet _pacote: typeof import('pacote') | undefined\n/*@__NO_SIDE_EFFECTS__*/\nfunction getPacote() {\n if (_pacote === undefined) {\n _pacote = /*@__PURE__*/ require('./external/pacote')\n }\n return _pacote as typeof import('pacote')\n}\n\n/**\n * Regex to check if a version string contains range operators.\n * Matches any version with range operators: ~, ^, >, <, =, x, X, *, spaces, or ||.\n */\nconst rangeOperatorsRegExp = /[~^><=xX* ]|\\|\\|/\n\nexport interface DownloadPackageResult {\n /** Path to the installed package directory. */\n packageDir: string\n /** Path to the binary. */\n binaryPath: string\n /** Whether the package was newly installed. */\n installed: boolean\n}\n\nexport interface DlxPackageOptions {\n /**\n * Package to install (e.g., '@cyclonedx/cdxgen@10.0.0').\n */\n package: string\n /**\n * Binary name to execute (optional - auto-detected in most cases).\n *\n * Auto-detection logic:\n * 1. If package has only one binary, uses it automatically\n * 2. Tries user-provided binaryName\n * 3. Tries last segment of package name (e.g., 'cli' from '@socketsecurity/cli')\n * 4. Falls back to first binary\n *\n * Only needed when package has multiple binaries and auto-detection fails.\n *\n * @example\n * // Auto-detected (single binary)\n * { package: '@socketsecurity/cli' } // Finds 'socket' binary automatically\n *\n * // Explicit (multiple binaries)\n * { package: 'some-tool', binaryName: 'specific-tool' }\n */\n binaryName?: string | undefined\n /**\n * Force reinstallation even if package exists.\n */\n force?: boolean | undefined\n /**\n * Additional spawn options for the execution.\n */\n spawnOptions?: SpawnOptions | undefined\n}\n\nexport interface DlxPackageResult {\n /** Path to the installed package directory. */\n packageDir: string\n /** Path to the binary that was executed. */\n binaryPath: string\n /** Whether the package was newly installed. */\n installed: boolean\n /** The spawn promise for the running process. */\n spawnPromise: ReturnType<typeof spawn>\n}\n\n/**\n * Parse package spec into name and version using npm-package-arg.\n * Examples:\n * - 'lodash@4.17.21' \u2192 { name: 'lodash', version: '4.17.21' }\n * - '@scope/pkg@1.0.0' \u2192 { name: '@scope/pkg', version: '1.0.0' }\n * - 'lodash' \u2192 { name: 'lodash', version: undefined }\n */\nfunction parsePackageSpec(spec: string): {\n name: string\n version: string | undefined\n} {\n try {\n const npa = getNpmPackageArg()\n const parsed = npa(spec)\n\n // Extract version from different types of specs.\n // For registry specs, use fetchSpec (the version/range).\n // For git/file/etc, version will be undefined.\n const version =\n parsed.type === 'tag'\n ? parsed.fetchSpec\n : parsed.type === 'version' || parsed.type === 'range'\n ? parsed.fetchSpec\n : undefined\n\n return {\n name: parsed.name || spec,\n version,\n }\n } catch {\n // Fallback to simple parsing if npm-package-arg fails.\n const atIndex = spec.lastIndexOf('@')\n if (atIndex === -1 || spec.startsWith('@')) {\n // No version or scoped package without version.\n return { name: spec, version: undefined }\n }\n return {\n name: spec.slice(0, atIndex),\n version: spec.slice(atIndex + 1),\n }\n }\n}\n\n/**\n * Install package to ~/.socket/_dlx/<hash>/ if not already installed.\n * Uses pacote for installation (no npm CLI required).\n * Protected by process lock to prevent concurrent installation corruption.\n */\nasync function ensurePackageInstalled(\n packageName: string,\n packageSpec: string,\n force: boolean,\n): Promise<{ installed: boolean; packageDir: string }> {\n const cacheKey = generateCacheKey(packageSpec)\n const packageDir = normalizePath(path.join(getSocketDlxDir(), cacheKey))\n const installedDir = normalizePath(\n path.join(packageDir, 'node_modules', packageName),\n )\n\n // Ensure package directory exists before creating lock.\n // The lock directory will be created inside this directory.\n try {\n await fs.mkdir(packageDir, { 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 package directory: ${packageDir}\\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 package directory on read-only filesystem: ${packageDir}\\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(`Failed to create package directory: ${packageDir}`, {\n cause: e,\n })\n }\n\n // Use process lock to prevent concurrent installations.\n // Uses npm npx's concurrency.lock naming convention.\n const lockPath = path.join(packageDir, 'concurrency.lock')\n\n return await processLock.withLock(\n lockPath,\n async () => {\n // Double-check if already installed (unless force).\n // Another process may have installed while waiting for lock.\n if (!force && existsSync(installedDir)) {\n // Verify package.json exists.\n const pkgJsonPath = path.join(installedDir, 'package.json')\n if (existsSync(pkgJsonPath)) {\n return { installed: false, packageDir }\n }\n }\n\n // Use pacote to extract the package.\n // Pacote leverages npm cache when available but doesn't require npm CLI.\n const pacoteCachePath = getPacoteCachePath()\n try {\n await getPacote().extract(packageSpec, installedDir, {\n // Use consistent pacote cache path (respects npm cache locations when available).\n cache: pacoteCachePath || path.join(packageDir, '.cache'),\n })\n } catch (e) {\n const code = (e as any).code\n if (code === 'E404' || code === 'ETARGET') {\n throw new Error(\n `Package not found: ${packageSpec}\\n` +\n 'Verify the package exists on npm registry and check the version.\\n' +\n `Visit https://www.npmjs.com/package/${packageName} to see available versions.`,\n { cause: e },\n )\n }\n if (\n code === 'ENOTFOUND' ||\n code === 'ETIMEDOUT' ||\n code === 'EAI_AGAIN'\n ) {\n throw new Error(\n `Network error installing ${packageSpec}\\n` +\n 'Check your internet connection and try again.',\n { cause: e },\n )\n }\n throw new Error(\n `Failed to install package: ${packageSpec}\\n` +\n `Destination: ${installedDir}\\n` +\n 'Check npm registry connectivity or package name.',\n { cause: e },\n )\n }\n\n return { installed: true, packageDir }\n },\n {\n // Align with npm npx locking strategy.\n staleMs: 5000,\n touchIntervalMs: 2000,\n },\n )\n}\n\n/**\n * Find the binary path for an installed package.\n * Intelligently handles packages with single or multiple binaries.\n */\nfunction findBinaryPath(\n packageDir: string,\n packageName: string,\n binaryName?: string,\n): string {\n const installedDir = normalizePath(\n path.join(packageDir, 'node_modules', packageName),\n )\n const pkgJsonPath = path.join(installedDir, 'package.json')\n\n // Read package.json to find bin entry.\n const pkgJson = readJsonSync(pkgJsonPath) as Record<string, unknown>\n const bin = pkgJson['bin']\n\n let binPath: string | undefined\n\n if (typeof bin === 'string') {\n // Single binary - use it directly.\n binPath = bin\n } else if (typeof bin === 'object' && bin !== null) {\n const binObj = bin as Record<string, string>\n const binKeys = Object.keys(binObj)\n\n // If only one binary, use it regardless of name.\n if (binKeys.length === 1) {\n binPath = binObj[binKeys[0]!]\n } else {\n // Multiple binaries - try to find the right one:\n // 1. User-provided binaryName\n // 2. Last segment of package name (e.g., 'cli' from '@socketsecurity/cli')\n // 3. Full package name without scope (e.g., 'cli' from '@socketsecurity/cli')\n // 4. First binary as fallback\n const lastSegment = packageName.split('/').pop()\n const candidates = [\n binaryName,\n lastSegment,\n packageName.replace(/^@[^/]+\\//, ''),\n ].filter(Boolean)\n\n for (const candidate of candidates) {\n if (candidate && binObj[candidate]) {\n binPath = binObj[candidate]\n break\n }\n }\n\n // Fallback to first binary if nothing matched.\n if (!binPath && binKeys.length > 0) {\n binPath = binObj[binKeys[0]!]\n }\n }\n }\n\n if (!binPath) {\n throw new Error(`No binary found for package \"${packageName}\"`)\n }\n\n return normalizePath(path.join(installedDir, binPath))\n}\n\n/**\n * Execute a package via DLX - install if needed and run its binary.\n *\n * This is the Socket equivalent of npx/pnpm dlx/yarn dlx, but using\n * our own cache directory (~/.socket/_dlx) and installation logic.\n *\n * Auto-forces reinstall for version ranges to get latest within range.\n *\n * @example\n * ```typescript\n * // Download and execute cdxgen\n * const result = await dlxPackage(\n * ['--version'],\n * { package: '@cyclonedx/cdxgen@10.0.0' }\n * )\n * await result.spawnPromise\n * ```\n */\nexport async function dlxPackage(\n args: readonly string[] | string[],\n options?: DlxPackageOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): Promise<DlxPackageResult> {\n // Download the package.\n const downloadResult = await downloadPackage(options!)\n\n // Execute the binary.\n const spawnPromise = executePackage(\n downloadResult.binaryPath,\n args,\n options?.spawnOptions,\n spawnExtra,\n )\n\n return {\n ...downloadResult,\n spawnPromise,\n }\n}\n\n/**\n * Download and install a package without executing it.\n * This is useful for self-update or when you need the package files\n * but don't want to run the binary immediately.\n *\n * @example\n * ```typescript\n * // Install @socketsecurity/cli without running it\n * const result = await downloadPackage({\n * package: '@socketsecurity/cli@1.2.0',\n * force: true\n * })\n * console.log('Installed to:', result.packageDir)\n * console.log('Binary at:', result.binaryPath)\n * ```\n */\nexport async function downloadPackage(\n options: DlxPackageOptions,\n): Promise<DownloadPackageResult> {\n const {\n binaryName,\n force: userForce,\n package: packageSpec,\n } = {\n __proto__: null,\n ...options,\n } as DlxPackageOptions\n\n // Parse package spec.\n const { name: packageName, version: packageVersion } =\n parsePackageSpec(packageSpec)\n\n // Auto-force for version ranges to get latest within range.\n // User can still override with explicit force: false if they want cache.\n const isVersionRange =\n packageVersion !== undefined && rangeOperatorsRegExp.test(packageVersion)\n const force = userForce !== undefined ? userForce : isVersionRange\n\n // Build full package spec for installation.\n const fullPackageSpec = packageVersion\n ? `${packageName}@${packageVersion}`\n : packageName\n\n // Ensure package is installed.\n const { installed, packageDir } = await ensurePackageInstalled(\n packageName,\n fullPackageSpec,\n force,\n )\n\n // Find binary path.\n const binaryPath = findBinaryPath(packageDir, packageName, binaryName)\n\n // Make binary executable on Unix systems.\n if (!WIN32 && existsSync(binaryPath)) {\n const { chmodSync } = require('node:fs')\n try {\n chmodSync(binaryPath, 0o755)\n } catch {\n // Ignore chmod errors.\n }\n }\n\n return {\n binaryPath,\n installed,\n packageDir,\n }\n}\n\n/**\n * Execute a package's binary.\n * The package must already be installed (use downloadPackage first).\n *\n * @example\n * ```typescript\n * // Execute an already-installed package\n * const downloaded = await downloadPackage({ package: 'cowsay@1.5.0' })\n * const result = await executePackage(\n * downloaded.binaryPath,\n * ['Hello World'],\n * { stdio: 'inherit' }\n * )\n * ```\n */\nexport function executePackage(\n binaryPath: string,\n args: readonly string[] | string[],\n spawnOptions?: SpawnOptions | undefined,\n spawnExtra?: SpawnExtra | undefined,\n): ReturnType<typeof spawn> {\n return spawn(binaryPath, args, spawnOptions, spawnExtra)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["dlx_package_exports", "__export", "dlxPackage", "downloadPackage", "executePackage", "__toCommonJS", "import_node_fs", "import_node_path", "import_platform", "import_packages", "import_dlx", "import_fs", "import_path", "import_paths", "import_process_lock", "import_spawn", "_npmPackageArg", "getNpmPackageArg", "_pacote", "getPacote", "rangeOperatorsRegExp", "parsePackageSpec", "spec", "parsed", "version", "atIndex", "ensurePackageInstalled", "packageName", "packageSpec", "force", "cacheKey", "packageDir", "path", "installedDir", "fs", "e", "code", "lockPath", "pkgJsonPath", "pacoteCachePath", "findBinaryPath", "binaryName", "bin", "binPath", "binObj", "binKeys", "lastSegment", "candidates", "candidate", "args", "options", "spawnExtra", "downloadResult", "spawnPromise", "userForce", "packageVersion", "isVersionRange", "fullPackageSpec", "installed", "binaryPath", "chmodSync", "spawnOptions"]
|
|
7
7
|
}
|
package/dist/process-lock.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Socket Lib - Built with esbuild */
|
|
2
|
-
var f=Object.defineProperty;var
|
|
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:
|
|
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
|
package/dist/process-lock.js.map
CHANGED
|
@@ -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": ";4ZAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,iBAAAE,IAAA,eAAAC,EAAAH,GA0CA,IAAAI,EAA4D,mBAE5DC,EAA+B,gBAC/BC,EAAuB,oBACvBC,EAAuB,sBACvBC,EAAuB,yBA6CvB,MAAMC,CAAmB,CACf,YAAc,IAAI,IAClB,YAAc,IAAI,IAClB,sBAAwB,GAMxB,mBAAoB,CACtB,KAAK,2BAIT,UAAO,IAAM,CAEX,UAAWC,KAAS,KAAK,YAAY,OAAO,EAC1C,cAAcA,CAAK,EAErB,KAAK,YAAY,MAAM,EAGvB,UAAWC,KAAY,KAAK,YAC1B,GAAI,IACE,cAAWA,CAAQ,MACrB,kBAAeA,EAAU,CAAE,UAAW,EAAK,CAAC,CAEhD,MAAQ,CAER,CAEJ,CAAC,EAED,KAAK,sBAAwB,GAC/B,CAQQ,UAAUA,EAAwB,CACxC,GAAI,CACF,MAAI,cAAWA,CAAQ,EAAG,CACxB,MAAMC,EAAM,IAAI,QAChB,cAAWD,EAAUC,EAAKA,CAAG,CAC/B,CACF,OAASC,EAAO,CACd,SAAO,KACL,wBAAwBF,CAAQ,KAAKE,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,CAAC,EAC7F,CACF,CACF,CASQ,gBAAgBF,EAAkBG,EAA0B,CAClE,GAAIA,GAAc,GAAK,KAAK,YAAY,IAAIH,CAAQ,EAClD,OAGF,MAAMD,EAAQ,YAAY,IAAM,CAC9B,KAAK,UAAUC,CAAQ,CACzB,EAAGG,CAAU,EAGbJ,EAAM,MAAM,EAEZ,KAAK,YAAY,IAAIC,EAAUD,CAAK,CACtC,CAOQ,eAAeC,EAAwB,CAC7C,MAAMD,EAAQ,KAAK,YAAY,IAAIC,CAAQ,EACvCD,IACF,cAAcA,CAAK,EACnB,KAAK,YAAY,OAAOC,CAAQ,EAEpC,CAWQ,QAAQA,EAAkBI,EAA0B,CAC1D,GAAI,CACF,GAAI,IAAC,cAAWJ,CAAQ,EACtB,MAAO,GAGT,MAAMK,KAAQ,YAASL,CAAQ,EAEzBM,EAAa,KAAK,OAAO,KAAK,IAAI,EAAID,EAAM,MAAM,QAAQ,GAAK,GAAI,EACnEE,EAAe,KAAK,MAAMH,EAAU,GAAI,EAC9C,OAAOE,EAAaC,CACtB,MAAQ,CACN,MAAO,EACT,CACF,CAyBA,MAAM,QACJP,EACAQ,EAA8B,CAAC,EACV,CACrB,KAAM,CACJ,YAAAC,EAAc,IACd,WAAAC,EAAa,IACb,QAAAC,EAAU,EACV,QAAAP,EAAU,IACV,gBAAAQ,EAAkB,GACpB,EAAIJ,EAGJ,YAAK,kBAAkB,EAEhB,QAAM,UACX,SAAY,CACV,GAAI,CAEF,MAAI,cAAWR,CAAQ,GAAK,KAAK,QAAQA,EAAUI,CAAO,EAAG,CAC3D,SAAO,IAAI,wBAAwBJ,CAAQ,EAAE,EAC7C,GAAI,IACF,kBAAeA,EAAU,CAAE,UAAW,EAAK,CAAC,CAC9C,MAAQ,CAER,CACF,CAGA,sBAAUA,EAAU,CAAE,UAAW,
|
|
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/dist/versions.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/* Socket Lib - Built with esbuild */
|
|
2
|
-
var
|
|
2
|
+
var s=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var f=(r,n)=>{for(var t in n)s(r,t,{get:n[t],enumerable:!0})},c=(r,n,t,u)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of d(n))!p.call(r,i)&&i!==t&&s(r,i,{get:()=>n[i],enumerable:!(u=a(n,i))||u.enumerable});return r};var g=r=>c(s({},"__esModule",{value:!0}),r);var R={};f(R,{coerceVersion:()=>m,compareVersions:()=>l,filterVersions:()=>x,getMajorVersion:()=>b,getMinorVersion:()=>h,getPatchVersion:()=>V,incrementVersion:()=>j,isEqual:()=>y,isGreaterThan:()=>v,isGreaterThanOrEqual:()=>q,isLessThan:()=>T,isLessThanOrEqual:()=>E,isValidVersion:()=>S,maxVersion:()=>D,minVersion:()=>G,parseVersion:()=>L,satisfiesVersion:()=>M,sortVersions:()=>O,sortVersionsDesc:()=>A,versionDiff:()=>P});module.exports=g(R);let o;function e(){return o===void 0&&(o=require("./external/semver")),o}function m(r){return e().coerce(r)?.version}function l(r,n){try{return e().compare(r,n)}catch{return}}function x(r,n){return r.filter(t=>e().satisfies(t,n))}function b(r){return e().parse(r)?.major}function h(r){return e().parse(r)?.minor}function V(r){return e().parse(r)?.patch}function j(r,n,t){return e().inc(r,n,t)||void 0}function y(r,n){return e().eq(r,n)}function v(r,n){return e().gt(r,n)}function q(r,n){return e().gte(r,n)}function T(r,n){return e().lt(r,n)}function E(r,n){return e().lte(r,n)}function S(r){return e().valid(r)!==null}function D(r){return e().maxSatisfying(r,"*")||void 0}function G(r){return e().minSatisfying(r,"*")||void 0}function L(r){const n=e().parse(r);if(n)return{major:n.major,minor:n.minor,patch:n.patch,prerelease:n.prerelease,build:n.build}}function M(r,n){return e().satisfies(r,n)}function O(r){return e().sort([...r])}function A(r){return e().rsort([...r])}function P(r,n){try{return e().diff(r,n)||void 0}catch{return}}0&&(module.exports={coerceVersion,compareVersions,filterVersions,getMajorVersion,getMinorVersion,getPatchVersion,incrementVersion,isEqual,isGreaterThan,isGreaterThanOrEqual,isLessThan,isLessThanOrEqual,isValidVersion,maxVersion,minVersion,parseVersion,satisfiesVersion,sortVersions,sortVersionsDesc,versionDiff});
|
|
3
3
|
//# sourceMappingURL=versions.js.map
|
package/dist/versions.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/versions.ts"],
|
|
4
|
-
"sourcesContent": ["/** @fileoverview Version comparison and validation utilities for Socket ecosystem. */\n\
|
|
5
|
-
"mappings": ";
|
|
6
|
-
"names": ["versions_exports", "__export", "coerceVersion", "compareVersions", "filterVersions", "getMajorVersion", "getMinorVersion", "getPatchVersion", "incrementVersion", "isEqual", "isGreaterThan", "isGreaterThanOrEqual", "isLessThan", "isLessThanOrEqual", "isValidVersion", "maxVersion", "minVersion", "parseVersion", "satisfiesVersion", "sortVersions", "sortVersionsDesc", "versionDiff", "__toCommonJS", "
|
|
4
|
+
"sourcesContent": ["/** @fileoverview Version comparison and validation utilities for Socket ecosystem. */\n\nlet _semver: typeof import('semver') | undefined\n/*@__NO_SIDE_EFFECTS__*/\nfunction getSemver() {\n if (_semver === undefined) {\n // The 'semver' package is browser safe.\n _semver = /*@__PURE__*/ require('./external/semver')\n }\n return _semver as typeof import('semver')\n}\n\n/**\n * Coerce a version string to valid semver format.\n */\nexport function coerceVersion(version: string): string | undefined {\n const coerced = getSemver().coerce(version)\n return coerced?.version\n}\n\n/**\n * Compare two semantic version strings.\n * @returns -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2, or undefined if invalid.\n */\nexport function compareVersions(\n v1: string,\n v2: string,\n): -1 | 0 | 1 | undefined {\n try {\n return getSemver().compare(v1, v2)\n } catch {\n return undefined\n }\n}\n\n/**\n * Get all versions from an array that satisfy a semver range.\n */\nexport function filterVersions(versions: string[], range: string): string[] {\n return versions.filter(v => getSemver().satisfies(v, range))\n}\n\n/**\n * Get the major version number from a version string.\n */\nexport function getMajorVersion(version: string): number | undefined {\n const parsed = getSemver().parse(version)\n return parsed?.major\n}\n\n/**\n * Get the minor version number from a version string.\n */\nexport function getMinorVersion(version: string): number | undefined {\n const parsed = getSemver().parse(version)\n return parsed?.minor\n}\n\n/**\n * Get the patch version number from a version string.\n */\nexport function getPatchVersion(version: string): number | undefined {\n const parsed = getSemver().parse(version)\n return parsed?.patch\n}\n\n/**\n * Increment a version by the specified release type.\n */\nexport function incrementVersion(\n version: string,\n release:\n | 'major'\n | 'minor'\n | 'patch'\n | 'premajor'\n | 'preminor'\n | 'prepatch'\n | 'prerelease',\n identifier?: string | undefined,\n): string | undefined {\n return getSemver().inc(version, release, identifier) || undefined\n}\n\n/**\n * Check if version1 equals version2.\n */\nexport function isEqual(version1: string, version2: string): boolean {\n return getSemver().eq(version1, version2)\n}\n\n/**\n * Check if version1 is greater than version2.\n */\nexport function isGreaterThan(version1: string, version2: string): boolean {\n return getSemver().gt(version1, version2)\n}\n\n/**\n * Check if version1 is greater than or equal to version2.\n */\nexport function isGreaterThanOrEqual(\n version1: string,\n version2: string,\n): boolean {\n return getSemver().gte(version1, version2)\n}\n\n/**\n * Check if version1 is less than version2.\n */\nexport function isLessThan(version1: string, version2: string): boolean {\n return getSemver().lt(version1, version2)\n}\n\n/**\n * Check if version1 is less than or equal to version2.\n */\nexport function isLessThanOrEqual(version1: string, version2: string): boolean {\n return getSemver().lte(version1, version2)\n}\n\n/**\n * Validate if a string is a valid semantic version.\n */\nexport function isValidVersion(version: string): boolean {\n return getSemver().valid(version) !== null\n}\n\n/**\n * Get the highest version from an array of versions.\n */\nexport function maxVersion(versions: string[]): string | undefined {\n return getSemver().maxSatisfying(versions, '*') || undefined\n}\n\n/**\n * Get the lowest version from an array of versions.\n */\nexport function minVersion(versions: string[]): string | undefined {\n return getSemver().minSatisfying(versions, '*') || undefined\n}\n\n/**\n * Parse a version string and return major, minor, patch components.\n */\nexport function parseVersion(version: string):\n | {\n major: number\n minor: number\n patch: number\n prerelease: ReadonlyArray<string | number>\n build: readonly string[]\n }\n | undefined {\n const parsed = getSemver().parse(version)\n if (!parsed) {\n return undefined\n }\n return {\n major: parsed.major,\n minor: parsed.minor,\n patch: parsed.patch,\n prerelease: parsed.prerelease,\n build: parsed.build,\n }\n}\n\n/**\n * Check if a version satisfies a semver range.\n */\nexport function satisfiesVersion(version: string, range: string): boolean {\n return getSemver().satisfies(version, range)\n}\n\n/**\n * Sort versions in ascending order.\n */\nexport function sortVersions(versions: string[]): string[] {\n return getSemver().sort([...versions])\n}\n\n/**\n * Sort versions in descending order.\n */\nexport function sortVersionsDesc(versions: string[]): string[] {\n return getSemver().rsort([...versions])\n}\n\n/**\n * Get the difference between two versions.\n */\nexport function versionDiff(\n version1: string,\n version2: string,\n):\n | 'major'\n | 'premajor'\n | 'minor'\n | 'preminor'\n | 'patch'\n | 'prepatch'\n | 'prerelease'\n | undefined {\n try {\n return getSemver().diff(version1, version2) || undefined\n } catch {\n return undefined\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["versions_exports", "__export", "coerceVersion", "compareVersions", "filterVersions", "getMajorVersion", "getMinorVersion", "getPatchVersion", "incrementVersion", "isEqual", "isGreaterThan", "isGreaterThanOrEqual", "isLessThan", "isLessThanOrEqual", "isValidVersion", "maxVersion", "minVersion", "parseVersion", "satisfiesVersion", "sortVersions", "sortVersionsDesc", "versionDiff", "__toCommonJS", "_semver", "getSemver", "version", "v1", "v2", "versions", "range", "v", "release", "identifier", "version1", "version2", "parsed"]
|
|
7
7
|
}
|