@socketsecurity/lib 1.3.3 → 1.3.5

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,44 @@ 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
+ ## [1.3.5](https://github.com/SocketDev/socket-lib/releases/tag/v1.3.5) - 2025-10-26
9
+
10
+ ### Added
11
+
12
+ - Added `createEnvProxy()` utility function to `env` module for Windows-compatible environment variable access
13
+ - Provides case-insensitive environment variable access (e.g., PATH, Path, path all work)
14
+ - Smart priority system: overrides > exact match > case-insensitive fallback
15
+ - Full Proxy implementation with proper handlers for get, set, has, ownKeys, getOwnPropertyDescriptor
16
+ - Opt-in helper for users who need Windows env var compatibility
17
+ - Well-documented with usage examples and performance notes
18
+ - Added `findCaseInsensitiveEnvKey()` utility function to `env` module
19
+ - Searches for environment variable keys using case-insensitive matching
20
+ - Optimized with length fast path to minimize expensive `toUpperCase()` calls
21
+ - Useful for cross-platform env var access where case may vary (e.g., PATH vs Path vs path)
22
+ - Added comprehensive test suite for `env` module with 71 tests
23
+ - Covers `envAsBoolean()`, `envAsNumber()`, `envAsString()` conversion utilities
24
+ - Tests `createEnvProxy()` with Windows environment variables and edge cases
25
+ - Validates `findCaseInsensitiveEnvKey()` optimization and behavior
26
+
27
+ ### Fixed
28
+
29
+ - Fixed `spawn` module to preserve Windows `process.env` Proxy behavior
30
+ - When no custom environment variables are provided, use `process.env` directly instead of spreading it
31
+ - Preserves Windows case-insensitive environment variable access (PATH vs Path)
32
+ - Fixes empty CLI output issue on Windows CI runners
33
+ - Only spreads `process.env` when merging custom environment variables
34
+
35
+ ## [1.3.4](https://github.com/SocketDev/socket-lib/releases/tag/v1.3.4) - 2025-10-26
36
+
37
+ ### Added
38
+
39
+ - Added Node.js SIGUSR1 signal handler prevention utilities in `constants/node` module
40
+ - `supportsNodeDisableSigusr1Flag()`: Detects if Node supports `--disable-sigusr1` flag (v22.14+, v23.7+, v24.8+)
41
+ - `getNodeDisableSigusr1Flags()`: Returns appropriate flags to prevent debugger attachment
42
+ - Returns `['--disable-sigusr1']` on supported versions (prevents Signal I/O Thread creation)
43
+ - Falls back to `['--no-inspect']` on Node 18+ (blocks debugger but still creates thread)
44
+ - Enables production CLI environments to prevent SIGUSR1 debugger signal handling for security
45
+
8
46
  ## [1.3.3](https://github.com/SocketDev/socket-lib/releases/tag/v1.3.3) - 2025-10-24
9
47
 
10
48
  ### Fixed
@@ -1,6 +1,3 @@
1
- /**
2
- * Node.js runtime: versions, features, flags, and capabilities.
3
- */
4
1
  // Version detection.
5
2
  export declare function getNodeVersion(): string;
6
3
  export declare function getNodeMajorVersion(): number;
@@ -17,6 +14,8 @@ export declare function supportsNodeDisableWarningFlag(): boolean;
17
14
  export declare function supportsNodePermissionFlag(): boolean;
18
15
  export declare function supportsNodeRequireModule(): boolean;
19
16
  export declare function supportsNodeRun(): boolean;
17
+ export declare function supportsNodeDisableSigusr1Flag(): boolean;
18
+ export declare function getNodeDisableSigusr1Flags(): string[];
20
19
  export declare function supportsProcessSend(): boolean;
21
20
  export declare function getNodeDebugFlags(): string[];
22
21
  export declare function getNodeHardenFlags(): string[];
@@ -1,3 +1,3 @@
1
1
  /* Socket Lib - Built with esbuild */
2
- var a=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var m=(e,n)=>{for(var t in n)a(e,t,{get:n[t],enumerable:!0})},x=(e,n,t,l)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of g(n))!f.call(e,r)&&r!==t&&a(e,r,{get:()=>n[r],enumerable:!(l=d(n,r))||l.enumerable});return e};var b=e=>x(a({},"__esModule",{value:!0}),e);var I={};m(I,{ESNEXT:()=>A,NODE_SEA_FUSE:()=>O,getExecPath:()=>C,getMaintainedNodeVersions:()=>j,getNodeDebugFlags:()=>S,getNodeHardenFlags:()=>V,getNodeMajorVersion:()=>o,getNodeNoWarningsFlags:()=>D,getNodePermissionFlags:()=>w,getNodeVersion:()=>N,supportsNodeCompileCacheApi:()=>F,supportsNodeCompileCacheEnvVar:()=>_,supportsNodeDisableWarningFlag:()=>E,supportsNodePermissionFlag:()=>h,supportsNodeRequireModule:()=>v,supportsNodeRun:()=>y,supportsProcessSend:()=>P});module.exports=b(I);function N(){return process.version}function o(){return Number.parseInt(process.version.slice(1).split(".")[0]||"0",10)}let s;function j(){if(s===void 0)try{s=require("../lib/maintained-node-versions")}catch{s=Object.freeze(Object.assign([],{current:"",last:"",next:"",previous:""}))}return s}function F(){return o()>=24}function _(){return o()>=22}function E(){return o()>=21}function h(){return o()>=20}function v(){const e=o();return e>=23||e===22&&Number.parseInt(process.version.split(".")[1]||"0",10)>=12}function y(){const e=o();return e>=23||e===22&&Number.parseInt(process.version.split(".")[1]||"0",10)>=11}function P(){return typeof process.send=="function"}let p;function S(){return p===void 0&&(p=["--inspect","--inspect-brk","--inspect-port","--inspect-publish-uid"]),p}let u;function V(){if(u===void 0){const e=o(),n=["--disable-proto=delete",e>=24?"--permission":"--experimental-permission","--force-node-api-uncaught-exceptions-policy"];e<24&&n.push("--experimental-policy"),u=n}return u}let i;function w(){return i===void 0&&(o()>=24?i=["--allow-fs-read=*","--allow-fs-write=*","--allow-child-process"]:i=[]),i}let c;function D(){return c===void 0&&(c=["--no-warnings","--no-deprecation"]),c}function C(){return process.execPath}const O="NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2",A="esnext";0&&(module.exports={ESNEXT,NODE_SEA_FUSE,getExecPath,getMaintainedNodeVersions,getNodeDebugFlags,getNodeHardenFlags,getNodeMajorVersion,getNodeNoWarningsFlags,getNodePermissionFlags,getNodeVersion,supportsNodeCompileCacheApi,supportsNodeCompileCacheEnvVar,supportsNodeDisableWarningFlag,supportsNodePermissionFlag,supportsNodeRequireModule,supportsNodeRun,supportsProcessSend});
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});
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\n// Version detection.\nexport function getNodeVersion(): string {\n return process.version\n}\n\nexport function getNodeMajorVersion(): number {\n return Number.parseInt(process.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(process.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(process.version.split('.')[1] || '0', 10) >= 11)\n )\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": ";4ZAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,YAAAE,EAAA,kBAAAC,EAAA,gBAAAC,EAAA,8BAAAC,EAAA,sBAAAC,EAAA,uBAAAC,EAAA,wBAAAC,EAAA,2BAAAC,EAAA,2BAAAC,EAAA,mBAAAC,EAAA,gCAAAC,EAAA,mCAAAC,EAAA,mCAAAC,EAAA,+BAAAC,EAAA,8BAAAC,EAAA,oBAAAC,EAAA,wBAAAC,IAAA,eAAAC,EAAAnB,GAKO,SAASW,GAAyB,CACvC,OAAO,QAAQ,OACjB,CAEO,SAASH,GAA8B,CAC5C,OAAO,OAAO,SAAS,QAAQ,QAAQ,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,GAAK,IAAK,EAAE,CAC1E,CAGA,IAAIY,EAQG,SAASf,GAA4B,CAC1C,GAAIe,IAA4B,OAC9B,GAAI,CACFA,EAA0B,QAAQ,iCAAiC,CACrE,MAAQ,CACNA,EAA0B,OAAO,OAC/B,OAAO,OAAO,CAAC,EAAG,CAChB,QAAS,GACT,KAAM,GACN,KAAM,GACN,SAAU,EACZ,CAAC,CACH,CACF,CAEF,OAAOA,CACT,CAGO,SAASR,GAAuC,CAErD,OADcJ,EAAoB,GAClB,EAClB,CAEO,SAASK,GAA0C,CAExD,OADcL,EAAoB,GAClB,EAClB,CAEO,SAASM,GAA0C,CAExD,OADcN,EAAoB,GAClB,EAClB,CAEO,SAASO,GAAsC,CAEpD,OADcP,EAAoB,GAClB,EAClB,CAEO,SAASQ,GAAqC,CACnD,MAAMK,EAAQb,EAAoB,EAClC,OACEa,GAAS,IACRA,IAAU,IACT,OAAO,SAAS,QAAQ,QAAQ,MAAM,GAAG,EAAE,CAAC,GAAK,IAAK,EAAE,GAAK,EAEnE,CAEO,SAASJ,GAA2B,CACzC,MAAMI,EAAQb,EAAoB,EAClC,OACEa,GAAS,IACRA,IAAU,IACT,OAAO,SAAS,QAAQ,QAAQ,MAAM,GAAG,EAAE,CAAC,GAAK,IAAK,EAAE,GAAK,EAEnE,CAEO,SAASH,GAA+B,CAC7C,OAAO,OAAO,QAAQ,MAAS,UACjC,CAGA,IAAII,EACG,SAAShB,GAA8B,CAC5C,OAAIgB,IAAoB,SACtBA,EAAkB,CAChB,YACA,gBACA,iBACA,uBACF,GAEKA,CACT,CAEA,IAAIC,EACG,SAAShB,GAA+B,CAC7C,GAAIgB,IAAqB,OAAW,CAClC,MAAMF,EAAQb,EAAoB,EAC5BgB,EAAQ,CACZ,yBAGAH,GAAS,GAAK,eAAiB,4BAE/B,6CACF,EAGIA,EAAQ,IACVG,EAAM,KAAK,uBAAuB,EAEpCD,EAAmBC,CACrB,CACA,OAAOD,CACT,CAEA,IAAIE,EACG,SAASf,GAAmC,CACjD,OAAIe,IAAyB,SACbjB,EAAoB,GAGrB,GACXiB,EAAuB,CAErB,oBAEA,qBAEA,uBACF,EAIAA,EAAuB,CAAC,GAGrBA,CACT,CAEA,IAAIC,EACG,SAASjB,GAAmC,CACjD,OAAIiB,IAAyB,SAC3BA,EAAuB,CAAC,gBAAiB,kBAAkB,GAEtDA,CACT,CAGO,SAAStB,GAAsB,CACpC,OAAO,QAAQ,QACjB,CAGO,MAAMD,EAAgB,iDAChBD,EAAS",
6
- "names": ["node_exports", "__export", "ESNEXT", "NODE_SEA_FUSE", "getExecPath", "getMaintainedNodeVersions", "getNodeDebugFlags", "getNodeHardenFlags", "getNodeMajorVersion", "getNodeNoWarningsFlags", "getNodePermissionFlags", "getNodeVersion", "supportsNodeCompileCacheApi", "supportsNodeCompileCacheEnvVar", "supportsNodeDisableWarningFlag", "supportsNodePermissionFlag", "supportsNodeRequireModule", "supportsNodeRun", "supportsProcessSend", "__toCommonJS", "_maintainedNodeVersions", "major", "_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 _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"]
7
7
  }
package/dist/env.d.ts CHANGED
@@ -1,3 +1,38 @@
1
+ /**
2
+ * Create a case-insensitive environment variable Proxy for Windows compatibility.
3
+ * On Windows, environment variables are case-insensitive (PATH vs Path vs path).
4
+ * This Proxy provides consistent access regardless of case, with priority given
5
+ * to exact matches, then case-insensitive matches for known vars.
6
+ *
7
+ * **Use Cases:**
8
+ * - Cross-platform test environments needing consistent env var access
9
+ * - Windows compatibility when passing env to child processes
10
+ * - Merging environment overrides while preserving case-insensitive lookups
11
+ *
12
+ * **Performance Note:**
13
+ * Proxy operations have runtime overhead. Only use when Windows case-insensitive
14
+ * access is required. For most use cases, process.env directly is sufficient.
15
+ *
16
+ * @param base - Base environment object (usually process.env)
17
+ * @param overrides - Optional overrides to merge
18
+ * @returns Proxy that handles case-insensitive env var access
19
+ *
20
+ * @example
21
+ * // Create a Proxy with overrides
22
+ * const env = createEnvProxy(process.env, { NODE_ENV: 'test' })
23
+ * console.log(env.PATH) // Works with any case: PATH, Path, path
24
+ * console.log(env.NODE_ENV) // 'test'
25
+ *
26
+ * @example
27
+ * // Pass to child process spawn
28
+ * import { createEnvProxy } from '@socketsecurity/lib/env'
29
+ * import { spawn } from '@socketsecurity/lib/spawn'
30
+ *
31
+ * spawn('node', ['script.js'], {
32
+ * env: createEnvProxy(process.env, { NODE_ENV: 'test' })
33
+ * })
34
+ */
35
+ export declare function createEnvProxy(base: NodeJS.ProcessEnv, overrides?: Record<string, string | undefined>): NodeJS.ProcessEnv;
1
36
  /**
2
37
  * Convert an environment variable value to a boolean.
3
38
  */
@@ -13,3 +48,35 @@ export declare function envAsNumber(value: unknown, defaultValue?: number): numb
13
48
  */
14
49
  /*@__NO_SIDE_EFFECTS__*/
15
50
  export declare function envAsString(value: unknown, defaultValue?: string): string;
51
+ /**
52
+ * Find a case-insensitive environment variable key match.
53
+ * Searches for an environment variable key that matches the given uppercase name,
54
+ * using optimized fast-path checks to minimize expensive toUpperCase() calls.
55
+ *
56
+ * **Use Cases:**
57
+ * - Finding PATH when env object has "Path" or "path"
58
+ * - Cross-platform env var access where case may vary
59
+ * - Custom case-insensitive env lookups
60
+ *
61
+ * **Performance:**
62
+ * - Fast path: Checks length first (O(1)) before toUpperCase (expensive)
63
+ * - Only converts to uppercase when length matches
64
+ * - Early exit on first match
65
+ *
66
+ * @param env - Environment object or env-like record to search
67
+ * @param upperEnvVarName - Uppercase environment variable name to find (e.g., 'PATH')
68
+ * @returns The actual key from env that matches (e.g., 'Path'), or undefined
69
+ *
70
+ * @example
71
+ * // Find PATH regardless of case
72
+ * const envObj = { Path: 'C:\\Windows', NODE_ENV: 'test' }
73
+ * const key = findCaseInsensitiveEnvKey(envObj, 'PATH')
74
+ * console.log(key) // 'Path'
75
+ * console.log(envObj[key]) // 'C:\\Windows'
76
+ *
77
+ * @example
78
+ * // Not found returns undefined
79
+ * const key = findCaseInsensitiveEnvKey({}, 'MISSING')
80
+ * console.log(key) // undefined
81
+ */
82
+ export declare function findCaseInsensitiveEnvKey(env: Record<string, string | undefined>, upperEnvVarName: string): string | undefined;
package/dist/env.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /* Socket Lib - Built with esbuild */
2
- var i=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var c=(n,r)=>{for(var t in r)i(n,t,{get:r[t],enumerable:!0})},f=(n,r,t,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let e of u(r))!N.call(n,e)&&e!==t&&i(n,e,{get:()=>r[e],enumerable:!(o=m(r,e))||o.enumerable});return n};var b=n=>f(i({},"__esModule",{value:!0}),n);var k={};c(k,{envAsBoolean:()=>y,envAsNumber:()=>S,envAsString:()=>d});module.exports=b(k);const g=Number,p=Number.isFinite,w=Number.parseInt,s=String;function y(n,r=!1){if(typeof n=="string"){const t=n.trim();return t==="1"||t.toLowerCase()==="true"}return n==null?!!r:!!n}function S(n,r=0){const t=w(String(n),10);return(p(t)?t:g(r))||0}function d(n,r=""){return typeof n=="string"?n.trim():n==null?r===""?r:s(r).trim():s(n).trim()}0&&(module.exports={envAsBoolean,envAsNumber,envAsString});
2
+ var f=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var m=(t,n)=>{for(var r in n)f(t,r,{get:n[r],enumerable:!0})},N=(t,n,r,e)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of y(n))!P.call(t,i)&&i!==r&&f(t,i,{get:()=>n[i],enumerable:!(e=d(n,i))||e.enumerable});return t};var S=t=>N(f({},"__esModule",{value:!0}),t);var b={};m(b,{createEnvProxy:()=>k,envAsBoolean:()=>a,envAsNumber:()=>w,envAsString:()=>C,findCaseInsensitiveEnvKey:()=>u});module.exports=S(b);const A=Number,E=Number.isFinite,O=Number.parseInt,c=String,g=new Set(["APPDATA","COMSPEC","HOME","LOCALAPPDATA","PATH","PATHEXT","PROGRAMFILES","SYSTEMROOT","TEMP","TMP","USERPROFILE","WINDIR"]);function k(t,n){return new Proxy({},{get(r,e){if(typeof e!="string")return;if(n&&e in n)return n[e];if(e in t)return t[e];const i=e.toUpperCase();if(g.has(i)){if(n){const o=u(n,i);if(o!==void 0)return n[o]}const s=u(t,i);if(s!==void 0)return t[s]}},ownKeys(r){return[...new Set([...Object.keys(t),...n?Object.keys(n):[]])]},getOwnPropertyDescriptor(r,e){if(typeof e!="string")return;const i=this.get?.(r,e,r);return i!==void 0?{enumerable:!0,configurable:!0,writable:!0,value:i}:void 0},has(r,e){if(typeof e!="string")return!1;if(n&&e in n||e in t)return!0;const i=e.toUpperCase();return!!(g.has(i)&&(n&&u(n,i)!==void 0||u(t,i)!==void 0))},set(r,e,i){return typeof e=="string"&&n?(n[e]=i,!0):!1}})}function a(t,n=!1){if(typeof t=="string"){const r=t.trim();return r==="1"||r.toLowerCase()==="true"}return t==null?!!n:!!t}function w(t,n=0){const r=O(String(t),10);return(E(r)?r:A(n))||0}function C(t,n=""){return typeof t=="string"?t.trim():t==null?n===""?n:c(n).trim():c(t).trim()}function u(t,n){const r=n.length;for(const e of Object.keys(t))if(e.length===r&&e.toUpperCase()===n)return e}0&&(module.exports={createEnvProxy,envAsBoolean,envAsNumber,envAsString,findCaseInsensitiveEnvKey});
3
3
  //# sourceMappingURL=env.js.map
package/dist/env.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/env.ts"],
4
- "sourcesContent": ["/**\n * @fileoverview Environment variable parsing and conversion utilities.\n * Provides type-safe conversion functions for boolean, number, and string values.\n */\n\nconst NumberCtor = Number\n// IMPORTANT: Do not use destructuring here - use direct assignment instead.\n// tsgo has a bug that incorrectly transpiles destructured exports, resulting in\n// `exports.SomeName = void 0;` which causes runtime errors.\n// See: https://github.com/SocketDev/socket-packageurl-js/issues/3\nconst NumberIsFinite = Number.isFinite\nconst NumberParseInt = Number.parseInt\nconst StringCtor = String\n\n/**\n * Convert an environment variable value to a boolean.\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function envAsBoolean(value: unknown, defaultValue = false): boolean {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed === '1' || trimmed.toLowerCase() === 'true'\n }\n if (value === null || value === undefined) {\n return !!defaultValue\n }\n return !!value\n}\n\n/**\n * Convert an environment variable value to a number.\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function envAsNumber(value: unknown, defaultValue = 0): number {\n const numOrNaN = NumberParseInt(String(value), 10)\n const numMayBeNegZero = NumberIsFinite(numOrNaN)\n ? numOrNaN\n : NumberCtor(defaultValue)\n // Ensure -0 is treated as 0.\n return numMayBeNegZero || 0\n}\n\n/**\n * Convert an environment variable value to a trimmed string.\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function envAsString(value: unknown, defaultValue = ''): string {\n if (typeof value === 'string') {\n return value.trim()\n }\n if (value === null || value === undefined) {\n return defaultValue === '' ? defaultValue : StringCtor(defaultValue).trim()\n }\n return StringCtor(value).trim()\n}\n"],
5
- "mappings": ";4ZAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kBAAAE,EAAA,gBAAAC,EAAA,gBAAAC,IAAA,eAAAC,EAAAL,GAKA,MAAMM,EAAa,OAKbC,EAAiB,OAAO,SACxBC,EAAiB,OAAO,SACxBC,EAAa,OAMZ,SAASP,EAAaQ,EAAgBC,EAAe,GAAgB,CAC1E,GAAI,OAAOD,GAAU,SAAU,CAC7B,MAAME,EAAUF,EAAM,KAAK,EAC3B,OAAOE,IAAY,KAAOA,EAAQ,YAAY,IAAM,MACtD,CACA,OAAIF,GAAU,KACL,CAAC,CAACC,EAEJ,CAAC,CAACD,CACX,CAMO,SAASP,EAAYO,EAAgBC,EAAe,EAAW,CACpE,MAAME,EAAWL,EAAe,OAAOE,CAAK,EAAG,EAAE,EAKjD,OAJwBH,EAAeM,CAAQ,EAC3CA,EACAP,EAAWK,CAAY,IAED,CAC5B,CAMO,SAASP,EAAYM,EAAgBC,EAAe,GAAY,CACrE,OAAI,OAAOD,GAAU,SACZA,EAAM,KAAK,EAEhBA,GAAU,KACLC,IAAiB,GAAKA,EAAeF,EAAWE,CAAY,EAAE,KAAK,EAErEF,EAAWC,CAAK,EAAE,KAAK,CAChC",
6
- "names": ["env_exports", "__export", "envAsBoolean", "envAsNumber", "envAsString", "__toCommonJS", "NumberCtor", "NumberIsFinite", "NumberParseInt", "StringCtor", "value", "defaultValue", "trimmed", "numOrNaN"]
4
+ "sourcesContent": ["/**\n * @fileoverview Environment variable parsing and conversion utilities.\n * Provides type-safe conversion functions for boolean, number, and string values.\n */\n\nconst NumberCtor = Number\n// IMPORTANT: Do not use destructuring here - use direct assignment instead.\n// tsgo has a bug that incorrectly transpiles destructured exports, resulting in\n// `exports.SomeName = void 0;` which causes runtime errors.\n// See: https://github.com/SocketDev/socket-packageurl-js/issues/3\nconst NumberIsFinite = Number.isFinite\nconst NumberParseInt = Number.parseInt\nconst StringCtor = String\n\n// Common environment variables that have case sensitivity issues on Windows.\n// These are checked with case-insensitive matching when exact matches fail.\nconst caseInsensitiveKeys = new Set([\n 'APPDATA',\n 'COMSPEC',\n 'HOME',\n 'LOCALAPPDATA',\n 'PATH',\n 'PATHEXT',\n 'PROGRAMFILES',\n 'SYSTEMROOT',\n 'TEMP',\n 'TMP',\n 'USERPROFILE',\n 'WINDIR',\n])\n\n/**\n * Create a case-insensitive environment variable Proxy for Windows compatibility.\n * On Windows, environment variables are case-insensitive (PATH vs Path vs path).\n * This Proxy provides consistent access regardless of case, with priority given\n * to exact matches, then case-insensitive matches for known vars.\n *\n * **Use Cases:**\n * - Cross-platform test environments needing consistent env var access\n * - Windows compatibility when passing env to child processes\n * - Merging environment overrides while preserving case-insensitive lookups\n *\n * **Performance Note:**\n * Proxy operations have runtime overhead. Only use when Windows case-insensitive\n * access is required. For most use cases, process.env directly is sufficient.\n *\n * @param base - Base environment object (usually process.env)\n * @param overrides - Optional overrides to merge\n * @returns Proxy that handles case-insensitive env var access\n *\n * @example\n * // Create a Proxy with overrides\n * const env = createEnvProxy(process.env, { NODE_ENV: 'test' })\n * console.log(env.PATH) // Works with any case: PATH, Path, path\n * console.log(env.NODE_ENV) // 'test'\n *\n * @example\n * // Pass to child process spawn\n * import { createEnvProxy } from '@socketsecurity/lib/env'\n * import { spawn } from '@socketsecurity/lib/spawn'\n *\n * spawn('node', ['script.js'], {\n * env: createEnvProxy(process.env, { NODE_ENV: 'test' })\n * })\n */\nexport function createEnvProxy(\n base: NodeJS.ProcessEnv,\n overrides?: Record<string, string | undefined>,\n): NodeJS.ProcessEnv {\n return new Proxy(\n {},\n {\n get(_target, prop) {\n if (typeof prop !== 'string') {\n return undefined\n }\n\n // Priority 1: Check overrides for exact match.\n if (overrides && prop in overrides) {\n return overrides[prop]\n }\n\n // Priority 2: Check base for exact match.\n if (prop in base) {\n return base[prop]\n }\n\n // Priority 3: Case-insensitive lookup for known keys.\n const upperProp = prop.toUpperCase()\n if (caseInsensitiveKeys.has(upperProp)) {\n // Check overrides with case variations.\n if (overrides) {\n const key = findCaseInsensitiveEnvKey(overrides, upperProp)\n if (key !== undefined) {\n return overrides[key]\n }\n }\n // Check base with case variations.\n const key = findCaseInsensitiveEnvKey(base, upperProp)\n if (key !== undefined) {\n return base[key]\n }\n }\n\n return undefined\n },\n\n ownKeys(_target) {\n const keys = new Set<string>([\n ...Object.keys(base),\n ...(overrides ? Object.keys(overrides) : []),\n ])\n return [...keys]\n },\n\n getOwnPropertyDescriptor(_target, prop) {\n if (typeof prop !== 'string') {\n return undefined\n }\n\n // Use the same lookup logic as get().\n const value = this.get?.(_target, prop, _target)\n return value !== undefined\n ? {\n enumerable: true,\n configurable: true,\n writable: true,\n value,\n }\n : undefined\n },\n\n has(_target, prop) {\n if (typeof prop !== 'string') {\n return false\n }\n\n // Check overrides.\n if (overrides && prop in overrides) {\n return true\n }\n\n // Check base.\n if (prop in base) {\n return true\n }\n\n // Case-insensitive check.\n const upperProp = prop.toUpperCase()\n if (caseInsensitiveKeys.has(upperProp)) {\n if (\n overrides &&\n findCaseInsensitiveEnvKey(overrides, upperProp) !== undefined\n ) {\n return true\n }\n if (findCaseInsensitiveEnvKey(base, upperProp) !== undefined) {\n return true\n }\n }\n\n return false\n },\n\n set(_target, prop, value) {\n if (typeof prop === 'string' && overrides) {\n overrides[prop] = value\n return true\n }\n return false\n },\n },\n ) as NodeJS.ProcessEnv\n}\n\n/**\n * Convert an environment variable value to a boolean.\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function envAsBoolean(value: unknown, defaultValue = false): boolean {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed === '1' || trimmed.toLowerCase() === 'true'\n }\n if (value === null || value === undefined) {\n return !!defaultValue\n }\n return !!value\n}\n\n/**\n * Convert an environment variable value to a number.\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function envAsNumber(value: unknown, defaultValue = 0): number {\n const numOrNaN = NumberParseInt(String(value), 10)\n const numMayBeNegZero = NumberIsFinite(numOrNaN)\n ? numOrNaN\n : NumberCtor(defaultValue)\n // Ensure -0 is treated as 0.\n return numMayBeNegZero || 0\n}\n\n/**\n * Convert an environment variable value to a trimmed string.\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function envAsString(value: unknown, defaultValue = ''): string {\n if (typeof value === 'string') {\n return value.trim()\n }\n if (value === null || value === undefined) {\n return defaultValue === '' ? defaultValue : StringCtor(defaultValue).trim()\n }\n return StringCtor(value).trim()\n}\n\n/**\n * Find a case-insensitive environment variable key match.\n * Searches for an environment variable key that matches the given uppercase name,\n * using optimized fast-path checks to minimize expensive toUpperCase() calls.\n *\n * **Use Cases:**\n * - Finding PATH when env object has \"Path\" or \"path\"\n * - Cross-platform env var access where case may vary\n * - Custom case-insensitive env lookups\n *\n * **Performance:**\n * - Fast path: Checks length first (O(1)) before toUpperCase (expensive)\n * - Only converts to uppercase when length matches\n * - Early exit on first match\n *\n * @param env - Environment object or env-like record to search\n * @param upperEnvVarName - Uppercase environment variable name to find (e.g., 'PATH')\n * @returns The actual key from env that matches (e.g., 'Path'), or undefined\n *\n * @example\n * // Find PATH regardless of case\n * const envObj = { Path: 'C:\\\\Windows', NODE_ENV: 'test' }\n * const key = findCaseInsensitiveEnvKey(envObj, 'PATH')\n * console.log(key) // 'Path'\n * console.log(envObj[key]) // 'C:\\\\Windows'\n *\n * @example\n * // Not found returns undefined\n * const key = findCaseInsensitiveEnvKey({}, 'MISSING')\n * console.log(key) // undefined\n */\nexport function findCaseInsensitiveEnvKey(\n env: Record<string, string | undefined>,\n upperEnvVarName: string,\n): string | undefined {\n const targetLength = upperEnvVarName.length\n for (const key of Object.keys(env)) {\n // Fast path: bail early if lengths don't match.\n if (key.length !== targetLength) {\n continue\n }\n // Only call toUpperCase if length matches.\n if (key.toUpperCase() === upperEnvVarName) {\n return key\n }\n }\n return undefined\n}\n"],
5
+ "mappings": ";4ZAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,oBAAAE,EAAA,iBAAAC,EAAA,gBAAAC,EAAA,gBAAAC,EAAA,8BAAAC,IAAA,eAAAC,EAAAP,GAKA,MAAMQ,EAAa,OAKbC,EAAiB,OAAO,SACxBC,EAAiB,OAAO,SACxBC,EAAa,OAIbC,EAAsB,IAAI,IAAI,CAClC,UACA,UACA,OACA,eACA,OACA,UACA,eACA,aACA,OACA,MACA,cACA,QACF,CAAC,EAoCM,SAASV,EACdW,EACAC,EACmB,CACnB,OAAO,IAAI,MACT,CAAC,EACD,CACE,IAAIC,EAASC,EAAM,CACjB,GAAI,OAAOA,GAAS,SAClB,OAIF,GAAIF,GAAaE,KAAQF,EACvB,OAAOA,EAAUE,CAAI,EAIvB,GAAIA,KAAQH,EACV,OAAOA,EAAKG,CAAI,EAIlB,MAAMC,EAAYD,EAAK,YAAY,EACnC,GAAIJ,EAAoB,IAAIK,CAAS,EAAG,CAEtC,GAAIH,EAAW,CACb,MAAMI,EAAMZ,EAA0BQ,EAAWG,CAAS,EAC1D,GAAIC,IAAQ,OACV,OAAOJ,EAAUI,CAAG,CAExB,CAEA,MAAMA,EAAMZ,EAA0BO,EAAMI,CAAS,EACrD,GAAIC,IAAQ,OACV,OAAOL,EAAKK,CAAG,CAEnB,CAGF,EAEA,QAAQH,EAAS,CAKf,MAAO,CAAC,GAJK,IAAI,IAAY,CAC3B,GAAG,OAAO,KAAKF,CAAI,EACnB,GAAIC,EAAY,OAAO,KAAKA,CAAS,EAAI,CAAC,CAC5C,CAAC,CACc,CACjB,EAEA,yBAAyBC,EAASC,EAAM,CACtC,GAAI,OAAOA,GAAS,SAClB,OAIF,MAAMG,EAAQ,KAAK,MAAMJ,EAASC,EAAMD,CAAO,EAC/C,OAAOI,IAAU,OACb,CACE,WAAY,GACZ,aAAc,GACd,SAAU,GACV,MAAAA,CACF,EACA,MACN,EAEA,IAAIJ,EAASC,EAAM,CACjB,GAAI,OAAOA,GAAS,SAClB,MAAO,GAST,GALIF,GAAaE,KAAQF,GAKrBE,KAAQH,EACV,MAAO,GAIT,MAAMI,EAAYD,EAAK,YAAY,EACnC,MAAI,GAAAJ,EAAoB,IAAIK,CAAS,IAEjCH,GACAR,EAA0BQ,EAAWG,CAAS,IAAM,QAIlDX,EAA0BO,EAAMI,CAAS,IAAM,QAMvD,EAEA,IAAIF,EAASC,EAAMG,EAAO,CACxB,OAAI,OAAOH,GAAS,UAAYF,GAC9BA,EAAUE,CAAI,EAAIG,EACX,IAEF,EACT,CACF,CACF,CACF,CAMO,SAAShB,EAAagB,EAAgBC,EAAe,GAAgB,CAC1E,GAAI,OAAOD,GAAU,SAAU,CAC7B,MAAME,EAAUF,EAAM,KAAK,EAC3B,OAAOE,IAAY,KAAOA,EAAQ,YAAY,IAAM,MACtD,CACA,OAAIF,GAAU,KACL,CAAC,CAACC,EAEJ,CAAC,CAACD,CACX,CAMO,SAASf,EAAYe,EAAgBC,EAAe,EAAW,CACpE,MAAME,EAAWZ,EAAe,OAAOS,CAAK,EAAG,EAAE,EAKjD,OAJwBV,EAAea,CAAQ,EAC3CA,EACAd,EAAWY,CAAY,IAED,CAC5B,CAMO,SAASf,EAAYc,EAAgBC,EAAe,GAAY,CACrE,OAAI,OAAOD,GAAU,SACZA,EAAM,KAAK,EAEhBA,GAAU,KACLC,IAAiB,GAAKA,EAAeT,EAAWS,CAAY,EAAE,KAAK,EAErET,EAAWQ,CAAK,EAAE,KAAK,CAChC,CAiCO,SAASb,EACdiB,EACAC,EACoB,CACpB,MAAMC,EAAeD,EAAgB,OACrC,UAAWN,KAAO,OAAO,KAAKK,CAAG,EAE/B,GAAIL,EAAI,SAAWO,GAIfP,EAAI,YAAY,IAAMM,EACxB,OAAON,CAIb",
6
+ "names": ["env_exports", "__export", "createEnvProxy", "envAsBoolean", "envAsNumber", "envAsString", "findCaseInsensitiveEnvKey", "__toCommonJS", "NumberCtor", "NumberIsFinite", "NumberParseInt", "StringCtor", "caseInsensitiveKeys", "base", "overrides", "_target", "prop", "upperProp", "key", "value", "defaultValue", "trimmed", "numOrNaN", "env", "upperEnvVarName", "targetLength"]
7
7
  }
package/dist/spawn.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /* Socket Lib - Built with esbuild */
2
- var R=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var H=Object.prototype.hasOwnProperty;var q=(e,n)=>{for(var r in n)R(e,r,{get:n[r],enumerable:!0})},F=(e,n,r,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of j(n))!H.call(e,o)&&o!==r&&R(e,o,{get:()=>n[o],enumerable:!(i=I(n,o))||i.enumerable});return e};var L=e=>F(R({},"__esModule",{value:!0}),e);var K={};q(K,{isSpawnError:()=>$,isStdioType:()=>v,spawn:()=>D,spawnSync:()=>G});module.exports=L(K);var w=require("#constants/process"),J=require("./arrays"),f=require("./objects"),k=require("./strings");const M=(0,w.getAbortSignal)(),z=(0,w.getSpinner)(),B=/\.(?:cmd|bat|ps1)$/i;let x;function U(){return x===void 0&&(x=require("node:child_process")),x}let E;function V(){return E===void 0&&(E=require("./external/@npmcli/promise-spawn")),E}let P;function T(){return P===void 0&&(P=require("node:path")),P}function $(e){if(e===null||typeof e!="object")return!1;const n=e;return(0,f.hasOwn)(n,"code")&&typeof n.code<"u"||(0,f.hasOwn)(n,"errno")&&typeof n.errno<"u"||(0,f.hasOwn)(n,"syscall")&&typeof n.syscall=="string"}function v(e,n){return arguments.length===1?typeof e=="string"&&["pipe","ignore","inherit","overlapped"].includes(e):e===n||e==null&&n==="pipe"||(0,J.isArray)(e)&&e.length>2&&e[0]===n&&e[1]===n&&e[2]===n}function N(e){const n=e,{stderr:r,stdout:i}=n;return typeof i=="string"&&(n.stdout=(0,k.stripAnsi)(i)),typeof r=="string"&&(n.stderr=(0,k.stripAnsi)(r)),n}function D(e,n,r,i){const o=(0,f.getOwn)(r,"shell"),l=process.platform==="win32";let u=e;if(l&&o&&B.test(u)){const t=T();u=t.basename(u,t.extname(u))}const{spinner:g=z,stripAnsi:b=!0,...s}={__proto__:null,...r},S=g,{env:m,stdio:d,stdioString:a=!0}=s,O=!!S?.isSpinning&&!v(d,"ignore")&&!v(d,"pipe"),C=O;O&&S.stop();const A=V(),W={__proto__:null,cwd:typeof s.cwd=="string"?s.cwd:void 0,env:{__proto__:null,...process.env,...m},signal:M,stdio:s.stdio,stdioString:a,shell:s.shell,timeout:s.timeout,uid:s.uid,gid:s.gid},h=A(u,n?[...n]:[],W,i),_=h;let p;return b&&a?p=h.then(t=>{const c=N(t);return"code"in c&&(c.exitCode=c.code),c}).catch(t=>{throw N(t)}):p=h.then(t=>{if("code"in t){const c=t;return c.exitCode=t.code,c}return t}),C&&(p=p.finally(()=>{S.start()})),p.process=_.process,p.stdin=_.stdin,p}function G(e,n,r){const i=(0,f.getOwn)(r,"shell"),o=process.platform==="win32";let l=e;if(o&&i&&B.test(l)){const a=T();l=a.basename(l,a.extname(l))}const{stripAnsi:u=!0,...g}={__proto__:null,...r},{stdioString:b=!0}=g,S={encoding:b?"utf8":"buffer",...g},m=S.encoding!=="buffer",d=U().spawnSync(l,n,S);if(m){const{stderr:a,stdout:y}=d;y&&(d.stdout=y.toString().trim()),a&&(d.stderr=a.toString().trim())}return u&&m?N(d):d}0&&(module.exports={isSpawnError,isStdioType,spawn,spawnSync});
2
+ var R=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var F=(e,n)=>{for(var r in n)R(e,r,{get:n[r],enumerable:!0})},L=(e,n,r,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of H(n))!q.call(e,i)&&i!==r&&R(e,i,{get:()=>n[i],enumerable:!(o=j(n,i))||o.enumerable});return e};var M=e=>L(R({},"__esModule",{value:!0}),e);var Q={};F(Q,{isSpawnError:()=>D,isStdioType:()=>v,spawn:()=>G,spawnSync:()=>K});module.exports=M(Q);var w=require("#constants/process"),J=require("./arrays"),f=require("./objects"),k=require("./strings");const U=(0,w.getAbortSignal)(),z=(0,w.getSpinner)(),T=/\.(?:cmd|bat|ps1)$/i;let x;function V(){return x===void 0&&(x=require("node:child_process")),x}let E;function $(){return E===void 0&&(E=require("./external/@npmcli/promise-spawn")),E}let P;function B(){return P===void 0&&(P=require("node:path")),P}function D(e){if(e===null||typeof e!="object")return!1;const n=e;return(0,f.hasOwn)(n,"code")&&typeof n.code<"u"||(0,f.hasOwn)(n,"errno")&&typeof n.errno<"u"||(0,f.hasOwn)(n,"syscall")&&typeof n.syscall=="string"}function v(e,n){return arguments.length===1?typeof e=="string"&&["pipe","ignore","inherit","overlapped"].includes(e):e===n||e==null&&n==="pipe"||(0,J.isArray)(e)&&e.length>2&&e[0]===n&&e[1]===n&&e[2]===n}function N(e){const n=e,{stderr:r,stdout:o}=n;return typeof o=="string"&&(n.stdout=(0,k.stripAnsi)(o)),typeof r=="string"&&(n.stderr=(0,k.stripAnsi)(r)),n}function G(e,n,r,o){const i=(0,f.getOwn)(r,"shell"),l=process.platform==="win32";let u=e;if(l&&i&&T.test(u)){const t=B();u=t.basename(u,t.extname(u))}const{spinner:m=z,stripAnsi:b=!0,...s}={__proto__:null,...r},S=m,{env:g,stdio:d,stdioString:a=!0}=s,O=!!S?.isSpinning&&!v(d,"ignore")&&!v(d,"pipe"),C=O;O&&S.stop();const A=$(),W=g?{__proto__:null,...process.env,...g}:process.env,I={__proto__:null,cwd:typeof s.cwd=="string"?s.cwd:void 0,env:W,signal:U,stdio:s.stdio,stdioString:a,shell:s.shell,timeout:s.timeout,uid:s.uid,gid:s.gid},h=A(u,n?[...n]:[],I,o),_=h;let p;return b&&a?p=h.then(t=>{const c=N(t);return"code"in c&&(c.exitCode=c.code),c}).catch(t=>{throw N(t)}):p=h.then(t=>{if("code"in t){const c=t;return c.exitCode=t.code,c}return t}),C&&(p=p.finally(()=>{S.start()})),p.process=_.process,p.stdin=_.stdin,p}function K(e,n,r){const o=(0,f.getOwn)(r,"shell"),i=process.platform==="win32";let l=e;if(i&&o&&T.test(l)){const a=B();l=a.basename(l,a.extname(l))}const{stripAnsi:u=!0,...m}={__proto__:null,...r},{stdioString:b=!0}=m,S={encoding:b?"utf8":"buffer",...m},g=S.encoding!=="buffer",d=V().spawnSync(l,n,S);if(g){const{stderr:a,stdout:y}=d;y&&(d.stdout=y.toString().trim()),a&&(d.stderr=a.toString().trim())}return u&&g?N(d):d}0&&(module.exports={isSpawnError,isStdioType,spawn,spawnSync});
3
3
  //# sourceMappingURL=spawn.js.map
package/dist/spawn.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/spawn.ts"],
4
- "sourcesContent": ["/**\n * @fileoverview Child process spawning utilities with cross-platform support.\n * Provides enhanced spawn functionality with stdio handling and error management.\n *\n * SECURITY: Array-Based Arguments Prevent Command Injection\n *\n * This module uses array-based arguments for all command execution, which is the\n * PRIMARY DEFENSE against command injection attacks. When you pass arguments as\n * an array to spawn():\n *\n * spawn('npx', ['sfw', tool, ...args], { shell: true })\n *\n * Node.js handles escaping automatically. Each argument is passed directly to the\n * OS without shell interpretation. Shell metacharacters like ; | & $ ( ) ` are\n * treated as LITERAL STRINGS, not as commands. This approach is secure even when\n * shell: true is used on Windows for .cmd/.bat file resolution.\n *\n * UNSAFE ALTERNATIVE (not used in this codebase):\n * spawn(`npx sfw ${tool} ${args.join(' ')}`, { shell: true }) // \u2716 VULNERABLE\n *\n * String concatenation allows injection. For example, if tool = \"foo; rm -rf /\",\n * the shell would execute both commands. Array-based arguments prevent this.\n *\n * References:\n * - https://nodejs.org/api/child_process.html#child_processspawncommand-args-options\n * - https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html\n */\n\nimport { getAbortSignal, getSpinner } from '#constants/process'\n\nimport { isArray } from './arrays'\n\nconst abortSignal = getAbortSignal()\nconst spinner = getSpinner()\n\nimport { getOwn, hasOwn } from './objects'\nimport { stripAnsi } from './strings'\n\n// Define BufferEncoding type for TypeScript compatibility.\ntype BufferEncoding = globalThis.BufferEncoding\n\nconst windowsScriptExtRegExp = /\\.(?:cmd|bat|ps1)$/i\n\nlet _child_process: typeof import('node:child_process') | undefined\n/**\n * Lazily load the `child_process` module to avoid Webpack bundling issues.\n *\n * @returns The Node.js `child_process` module\n *\n * @example\n * const childProcess = getChildProcess()\n * childProcess.spawnSync('ls', ['-la'])\n */\n/*@__NO_SIDE_EFFECTS__*/\nfunction getChildProcess() {\n if (_child_process === undefined) {\n // Use non-'node:' prefixed require to avoid Webpack errors.\n\n _child_process = /*@__PURE__*/ require('node:child_process')\n }\n return _child_process as typeof import('node:child_process')\n}\n\n/**\n * Options for spawning a child process with promise-based completion.\n *\n * @property {string | undefined} cwd - Current working directory for the process\n * @property {boolean | undefined} stdioString - Convert stdio output to strings (default: `true`)\n * @property {StdioType | undefined} stdio - Stdio configuration (`'pipe'`, `'ignore'`, `'inherit'`, or array)\n * @property {NodeJS.ProcessEnv | undefined} env - Environment variables for the process\n * @property {boolean | string | undefined} shell - Whether to run command in shell, or path to shell\n * @property {AbortSignal | undefined} signal - Signal to abort the process\n * @property {number | undefined} timeout - Maximum time in milliseconds before killing the process\n * @property {number | undefined} uid - User identity of the process (POSIX only)\n * @property {number | undefined} gid - Group identity of the process (POSIX only)\n */\nexport type PromiseSpawnOptions = {\n cwd?: string | undefined\n stdioString?: boolean | undefined\n stdio?: StdioType | undefined\n env?: NodeJS.ProcessEnv | undefined\n shell?: boolean | string | undefined\n signal?: AbortSignal | undefined\n timeout?: number | undefined\n uid?: number | undefined\n gid?: number | undefined\n}\n\n/**\n * Result returned by {@link spawn} when the child process completes.\n * This is a Promise that resolves with process exit information and output,\n * with additional properties for accessing the running process and stdin stream.\n *\n * @property {ChildProcessType} process - The running child process instance\n * @property {WritableStreamType | null} stdin - Writable stream for process stdin, or `null` if not piped\n *\n * @example\n * const result = spawn('echo', ['hello'])\n * result.stdin?.write('additional input\\n')\n * const { code, stdout } = await result\n * console.log(stdout) // 'hello'\n */\nexport type PromiseSpawnResult = Promise<{\n cmd: string\n args: string[] | readonly string[]\n code: number\n signal: NodeJS.Signals | null\n stdout: string | Buffer\n stderr: string | Buffer\n}> & {\n process: ChildProcessType\n stdin: WritableStreamType | null\n}\n\nlet _npmCliPromiseSpawn:\n | ((\n cmd: string,\n args: string[],\n options?: PromiseSpawnOptions | undefined,\n extra?: SpawnExtra | undefined,\n ) => PromiseSpawnResult)\n | undefined\n/**\n * Lazily load the `@npmcli/promise-spawn` module for async process spawning.\n *\n * @returns The promise-spawn module that provides Promise-based spawn functionality\n *\n * @example\n * const promiseSpawn = getNpmcliPromiseSpawn()\n * await promiseSpawn('git', ['status'])\n */\n/*@__NO_SIDE_EFFECTS__*/\nfunction getNpmcliPromiseSpawn() {\n if (_npmCliPromiseSpawn === undefined) {\n _npmCliPromiseSpawn = /*@__PURE__*/ require('./external/@npmcli/promise-spawn')\n }\n return _npmCliPromiseSpawn as unknown as typeof import('@npmcli/promise-spawn')\n}\n\nlet _path: typeof import('node:path') | undefined\n/**\n * Lazily load the `path` module to avoid Webpack bundling issues.\n *\n * @returns The Node.js `path` module\n *\n * @example\n * const path = getPath()\n * const basename = path.basename('/foo/bar.txt')\n */\n/*@__NO_SIDE_EFFECTS__*/\nfunction getPath() {\n if (_path === undefined) {\n // Use non-'node:' prefixed require to avoid Webpack errors.\n\n _path = /*@__PURE__*/ require('node:path')\n }\n return _path as typeof import('node:path')\n}\n\n/**\n * Error object thrown when a spawned process fails.\n * Extends the standard Error with process-specific information including exit code,\n * signal, command details, and captured output.\n *\n * @property {string[]} args - Arguments passed to the command\n * @property {string} cmd - Command that was executed\n * @property {number} code - Process exit code\n * @property {string} name - Error name (typically `'Error'`)\n * @property {string} message - Error message describing the failure\n * @property {NodeJS.Signals | null} signal - Signal that terminated the process, if any\n * @property {string} stack - Stack trace of the error\n * @property {string | Buffer} stderr - Standard error output from the process\n * @property {string | Buffer} stdout - Standard output from the process\n *\n * @example\n * try {\n * await spawn('exit', ['1'])\n * } catch (error) {\n * if (isSpawnError(error)) {\n * console.error(`Command failed with code ${error.code}`)\n * console.error(`stderr: ${error.stderr}`)\n * }\n * }\n */\nexport type SpawnError = {\n args: string[]\n cmd: string\n code: number\n name: string\n message: string\n signal: NodeJS.Signals | null\n stack: string\n stderr: string | Buffer\n stdout: string | Buffer\n}\n\n/**\n * Spawn error variant where stdout and stderr are guaranteed to be strings.\n * This type is used when `stdioString: true` is set in spawn options.\n *\n * @property {string} stdout - Standard output as a string\n * @property {string} stderr - Standard error as a string\n */\nexport type SpawnErrorWithOutputString = SpawnError & {\n stdout: string\n stderr: string\n}\n\n/**\n * Spawn error variant where stdout and stderr are guaranteed to be Buffers.\n * This type is used when `stdioString: false` is set in spawn options.\n *\n * @property {Buffer} stdout - Standard output as a Buffer\n * @property {Buffer} stderr - Standard error as a Buffer\n */\nexport type SpawnErrorWithOutputBuffer = SpawnError & {\n stdout: Buffer\n stderr: Buffer\n}\n\n/**\n * Extra options passed to the underlying promise-spawn implementation.\n * This is an open-ended object for passing additional metadata or configuration.\n */\nexport type SpawnExtra = Record<string, unknown>\n\n/**\n * Valid values for individual stdio streams.\n * - `'pipe'` - Creates a pipe between child and parent (default)\n * - `'ignore'` - Ignores the stream\n * - `'inherit'` - Uses parent's stream\n * - `'overlapped'` - Windows-specific overlapped I/O\n */\nexport type IOType = 'pipe' | 'ignore' | 'inherit' | 'overlapped'\n\n/**\n * Configuration for process stdio (stdin, stdout, stderr) streams.\n * Can be a single value applied to all streams, or an array specifying each stream individually.\n * - `'ipc'` - Creates an IPC channel for communication with the parent\n *\n * @example\n * // All streams piped\n * stdio: 'pipe'\n *\n * @example\n * // Custom configuration per stream: [stdin, stdout, stderr]\n * stdio: ['ignore', 'pipe', 'pipe']\n */\nexport type StdioType = IOType | 'ipc' | Array<IOType | 'ipc'>\n\n/**\n * Result object returned by {@link spawnSync} when the child process completes synchronously.\n *\n * @template T - Type of stdout/stderr (string or Buffer)\n * @property {number} pid - Process ID of the spawned child\n * @property {Array<T | null>} output - Array containing stdout/stderr values\n * @property {T} stdout - Standard output from the process\n * @property {T} stderr - Standard error from the process\n * @property {number | null} status - Exit code, or `null` if killed by signal\n * @property {NodeJS.Signals | null} signal - Signal that terminated the process, or `null`\n * @property {Error | undefined} error - Error object if the spawn failed\n */\nexport interface SpawnSyncReturns<T> {\n pid: number\n output: Array<T | null>\n stdout: T\n stderr: T\n status: number | null\n signal: NodeJS.Signals | null\n error?: Error | undefined\n}\n\n/**\n * Check if a value is a spawn error with expected error properties.\n * Tests for common error properties from child process failures.\n *\n * @param {unknown} value - Value to check\n * @returns {boolean} `true` if the value has spawn error properties\n *\n * @example\n * try {\n * await spawn('nonexistent-command')\n * } catch (error) {\n * if (isSpawnError(error)) {\n * console.error(`Spawn failed: ${error.code}`)\n * }\n * }\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function isSpawnError(value: unknown): value is SpawnError {\n if (value === null || typeof value !== 'object') {\n return false\n }\n // Check for spawn-specific error properties.\n const err = value as Record<string, unknown>\n return (\n (hasOwn(err, 'code') && typeof err['code'] !== 'undefined') ||\n (hasOwn(err, 'errno') && typeof err['errno'] !== 'undefined') ||\n (hasOwn(err, 'syscall') && typeof err['syscall'] === 'string')\n )\n}\n\n/**\n * Check if stdio configuration matches a specific type.\n * When called with one argument, validates if it's a valid stdio type.\n * When called with two arguments, checks if the stdio config matches the specified type.\n *\n * @param {string | string[]} stdio - Stdio configuration to check\n * @param {StdioType | undefined} type - Expected stdio type (optional)\n * @returns {boolean} `true` if stdio matches the type or is valid\n *\n * @example\n * // Check if valid stdio type\n * isStdioType('pipe') // true\n * isStdioType('invalid') // false\n *\n * @example\n * // Check if stdio matches specific type\n * isStdioType('pipe', 'pipe') // true\n * isStdioType(['pipe', 'pipe', 'pipe'], 'pipe') // true\n * isStdioType('ignore', 'pipe') // false\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function isStdioType(\n stdio: string | string[],\n type?: StdioType | undefined,\n): boolean {\n // If called with one argument, check if it's a valid stdio type.\n // biome-ignore lint/complexity/noArguments: Function overload detection for single vs two-arg calls.\n if (arguments.length === 1) {\n const validTypes = ['pipe', 'ignore', 'inherit', 'overlapped']\n return typeof stdio === 'string' && validTypes.includes(stdio)\n }\n // Original two-argument behavior.\n return (\n stdio === type ||\n ((stdio === null || stdio === undefined) && type === 'pipe') ||\n (isArray(stdio) &&\n stdio.length > 2 &&\n stdio[0] === type &&\n stdio[1] === type &&\n stdio[2] === type)\n )\n}\n\n/**\n * Strip ANSI escape codes from spawn result stdout and stderr.\n * Modifies the result object in place to remove color codes and formatting.\n *\n * @param {unknown} result - Spawn result object with stdout/stderr properties\n * @returns {unknown} The modified result object\n */\n/*@__NO_SIDE_EFFECTS__*/\nfunction stripAnsiFromSpawnResult(result: unknown): unknown {\n const res = result as {\n stdout?: string | Buffer\n stderr?: string | Buffer\n }\n const { stderr, stdout } = res\n if (typeof stdout === 'string') {\n res.stdout = stripAnsi(stdout)\n }\n if (typeof stderr === 'string') {\n res.stderr = stripAnsi(stderr)\n }\n return res\n}\n\n/*@__NO_SIDE_EFFECTS__*/\n// Duplicated from Node.js child_process.SpawnOptions\n// These are the options passed to child_process.spawn()\ninterface NodeSpawnOptions {\n cwd?: string | URL | undefined\n env?: NodeJS.ProcessEnv | undefined\n argv0?: string | undefined\n stdio?: any\n detached?: boolean | undefined\n uid?: number | undefined\n gid?: number | undefined\n serialization?: 'json' | 'advanced' | undefined\n shell?: boolean | string | undefined\n windowsVerbatimArguments?: boolean | undefined\n windowsHide?: boolean | undefined\n signal?: AbortSignal | undefined\n timeout?: number | undefined\n killSignal?: NodeJS.Signals | number | undefined\n}\n\n// Duplicated from Node.js child_process.ChildProcess\n// This represents a spawned child process\ninterface ChildProcessType {\n stdin: NodeJS.WritableStream | null\n stdout: NodeJS.ReadableStream | null\n stderr: NodeJS.ReadableStream | null\n readonly channel?: any\n readonly stdio: [\n NodeJS.WritableStream | null,\n NodeJS.ReadableStream | null,\n NodeJS.ReadableStream | null,\n NodeJS.ReadableStream | NodeJS.WritableStream | null | undefined,\n NodeJS.ReadableStream | NodeJS.WritableStream | null | undefined,\n ]\n readonly killed: boolean\n readonly pid?: number | undefined\n readonly connected: boolean\n readonly exitCode: number | null\n readonly signalCode: NodeJS.Signals | null\n readonly spawnargs: string[]\n readonly spawnfile: string\n kill(signal?: NodeJS.Signals | number): boolean\n send(message: any, callback?: (error: Error | null) => void): boolean\n send(\n message: any,\n sendHandle?: any | undefined,\n callback?: (error: Error | null) => void,\n ): boolean\n send(\n message: any,\n sendHandle?: any | undefined,\n options?: any | undefined,\n callback?: (error: Error | null) => void,\n ): boolean\n disconnect(): void\n unref(): void\n ref(): void\n}\n\n// Duplicated from Node.js stream.Writable\ninterface WritableStreamType {\n writable: boolean\n writableEnded: boolean\n writableFinished: boolean\n writableHighWaterMark: number\n writableLength: number\n writableObjectMode: boolean\n writableCorked: number\n destroyed: boolean\n write(\n chunk: any,\n encoding?: BufferEncoding | undefined,\n callback?: (error?: Error | null) => void,\n ): boolean\n write(chunk: any, callback?: (error?: Error | null) => void): boolean\n end(cb?: () => void): this\n end(chunk: any, cb?: () => void): this\n end(chunk: any, encoding?: BufferEncoding | undefined, cb?: () => void): this\n cork(): void\n uncork(): void\n destroy(error?: Error | undefined): this\n}\n\n/**\n * Options for spawning a child process with {@link spawn}.\n * Extends Node.js spawn options with additional Socket-specific functionality.\n *\n * @property {import('./spinner').Spinner | undefined} spinner - Spinner instance to pause during execution\n * @property {boolean | undefined} stdioString - Convert output to strings (default: `true`)\n * @property {boolean | undefined} stripAnsi - Remove ANSI codes from output (default: `true`)\n * @property {string | URL | undefined} cwd - Current working directory\n * @property {NodeJS.ProcessEnv | undefined} env - Environment variables\n * @property {StdioType | undefined} stdio - Stdio configuration\n * @property {boolean | string | undefined} shell - Run command in shell\n * @property {number | undefined} timeout - Timeout in milliseconds\n * @property {AbortSignal | undefined} signal - Abort signal\n * @property {number | undefined} uid - User identity (POSIX)\n * @property {number | undefined} gid - Group identity (POSIX)\n */\nexport type SpawnOptions = import('./objects').Remap<\n NodeSpawnOptions & {\n spinner?: import('./spinner').Spinner | undefined\n stdioString?: boolean\n stripAnsi?: boolean\n }\n>\nexport type SpawnResult = PromiseSpawnResult\n/**\n * Result object returned when a spawned process completes.\n *\n * @property {string} cmd - Command that was executed\n * @property {string[] | readonly string[]} args - Arguments passed to the command\n * @property {number} code - Process exit code\n * @property {NodeJS.Signals | null} signal - Signal that terminated the process, if any\n * @property {string | Buffer} stdout - Standard output (string if `stdioString: true`, Buffer otherwise)\n * @property {string | Buffer} stderr - Standard error (string if `stdioString: true`, Buffer otherwise)\n */\nexport type SpawnStdioResult = {\n cmd: string\n args: string[] | readonly string[]\n code: number\n signal: NodeJS.Signals | null\n stdout: string | Buffer\n stderr: string | Buffer\n}\n\n/**\n * Spawn a child process and return a promise that resolves when it completes.\n * Provides enhanced error handling, output capture, and cross-platform support.\n *\n * SECURITY: This function uses array-based arguments which prevent command injection.\n * Arguments in the `args` array are passed directly to the OS without shell\n * interpretation. Shell metacharacters (;|&$()`) are treated as literal strings,\n * not as commands or operators. This is the PRIMARY SECURITY DEFENSE.\n *\n * Even when shell: true is used (on Windows for .cmd/.bat execution), the array-based\n * approach remains secure because Node.js properly escapes each argument before passing\n * to the shell.\n *\n * @param {string} cmd - Command to execute (not user-controlled)\n * @param {string[] | readonly string[] | undefined} args - Array of arguments (safe even with user input)\n * @param {SpawnOptions | undefined} options - Spawn options for process configuration\n * @param {SpawnExtra | undefined} extra - Extra options for promise-spawn\n * @returns {SpawnResult} Promise that resolves with process exit information\n *\n * @throws {SpawnError} When the process exits with non-zero code or is terminated by signal\n *\n * @example\n * // Basic usage - spawn and wait for completion\n * const result = await spawn('git', ['status'])\n * console.log(result.stdout)\n *\n * @example\n * // With options - set working directory and environment\n * const result = await spawn('npm', ['install'], {\n * cwd: '/path/to/project',\n * env: { NODE_ENV: 'production' }\n * })\n *\n * @example\n * // \u2714 DO THIS - Array-based arguments (safe)\n * spawn('git', ['commit', '-m', userMessage])\n * // Each argument is properly escaped, even if userMessage = \"foo; rm -rf /\"\n *\n * @example\n * // \u2716 NEVER DO THIS - String concatenation (vulnerable)\n * spawn(`git commit -m \"${userMessage}\"`, { shell: true })\n * // Vulnerable to injection if userMessage = '\"; rm -rf / #'\n *\n * @example\n * // Access stdin for interactive processes\n * const result = spawn('cat', [])\n * result.stdin?.write('Hello\\n')\n * result.stdin?.end()\n * const { stdout } = await result\n * console.log(stdout) // 'Hello'\n *\n * @example\n * // Handle errors with exit codes\n * try {\n * await spawn('exit', ['1'])\n * } catch (error) {\n * if (isSpawnError(error)) {\n * console.error(`Failed with code ${error.code}`)\n * console.error(error.stderr)\n * }\n * }\n */\nexport function spawn(\n cmd: string,\n args?: string[] | readonly string[],\n options?: SpawnOptions | undefined,\n extra?: SpawnExtra | undefined,\n): SpawnResult {\n // Windows cmd.exe command resolution for .cmd/.bat/.ps1 files:\n //\n // When shell: true is used on Windows with script files (.cmd, .bat, .ps1),\n // cmd.exe can have issues executing full paths. The solution is to use just\n // the command basename without extension and let cmd.exe find it via PATH.\n //\n // How cmd.exe resolves commands:\n // 1. Searches current directory first\n // 2. Then searches each directory in PATH environment variable\n // 3. For each directory, tries extensions from PATHEXT (.COM, .EXE, .BAT, .CMD, etc.)\n // 4. Executes the first match found\n //\n // Example: Given 'C:\\pnpm\\pnpm.cmd' with shell: true\n // 1. Extract basename without extension: 'pnpm'\n // 2. cmd.exe searches PATH directories for 'pnpm'\n // 3. PATHEXT causes it to try 'pnpm.com', 'pnpm.exe', 'pnpm.bat', 'pnpm.cmd', etc.\n // 4. Finds and executes 'C:\\pnpm\\pnpm.cmd'\n //\n // This approach is consistent with how other tools handle Windows execution:\n // - npm's promise-spawn: uses which.sync() to find commands in PATH\n // - cross-spawn: spawns cmd.exe with escaped arguments\n // - execa: uses cross-spawn under the hood for Windows support\n //\n // See: https://github.com/nodejs/node/issues/3675\n const shell = getOwn(options, 'shell')\n // Inline WIN32 constant for coverage mode compatibility\n const WIN32 = process.platform === 'win32'\n let actualCmd = cmd\n if (WIN32 && shell && windowsScriptExtRegExp.test(actualCmd)) {\n const path = getPath()\n // Extract just the command name without path and extension.\n actualCmd = path.basename(actualCmd, path.extname(actualCmd))\n }\n const {\n spinner: optionsSpinner = spinner,\n stripAnsi: shouldStripAnsi = true,\n ...spawnOptions\n } = { __proto__: null, ...options } as SpawnOptions\n const spinnerInstance = optionsSpinner\n const { env, stdio, stdioString = true } = spawnOptions\n // The stdio option can be a string or an array.\n // https://nodejs.org/api/child_process.html#optionsstdio\n const wasSpinning = !!spinnerInstance?.isSpinning\n const shouldStopSpinner =\n wasSpinning && !isStdioType(stdio, 'ignore') && !isStdioType(stdio, 'pipe')\n const shouldRestartSpinner = shouldStopSpinner\n if (shouldStopSpinner) {\n spinnerInstance.stop()\n }\n const npmCliPromiseSpawn = getNpmcliPromiseSpawn()\n // Use __proto__: null to prevent prototype pollution when passing to\n // third-party code, Node.js built-ins, or JavaScript built-in methods.\n // https://github.com/npm/promise-spawn\n // https://github.com/nodejs/node/blob/v24.0.1/lib/child_process.js#L674-L678\n const promiseSpawnOpts = {\n __proto__: null,\n cwd: typeof spawnOptions.cwd === 'string' ? spawnOptions.cwd : undefined,\n env: {\n __proto__: null,\n ...process.env,\n ...env,\n } as unknown as NodeJS.ProcessEnv,\n signal: abortSignal,\n stdio: spawnOptions.stdio,\n stdioString,\n shell: spawnOptions.shell,\n timeout: spawnOptions.timeout,\n uid: spawnOptions.uid,\n gid: spawnOptions.gid,\n } as unknown as PromiseSpawnOptions\n const spawnPromise = npmCliPromiseSpawn(\n actualCmd,\n args ? [...args] : [],\n promiseSpawnOpts as Parameters<typeof npmCliPromiseSpawn>[2],\n extra,\n )\n const oldSpawnPromise = spawnPromise\n let newSpawnPromise: PromiseSpawnResult\n if (shouldStripAnsi && stdioString) {\n newSpawnPromise = spawnPromise\n .then(result => {\n const strippedResult = stripAnsiFromSpawnResult(result)\n // Add exitCode as an alias for code.\n if ('code' in (strippedResult as { code?: number })) {\n ;(strippedResult as { code: number; exitCode: number }).exitCode = (\n strippedResult as { code: number }\n ).code\n }\n return strippedResult\n })\n .catch(error => {\n throw stripAnsiFromSpawnResult(error)\n }) as PromiseSpawnResult\n } else {\n newSpawnPromise = spawnPromise.then(result => {\n // Add exitCode as an alias for code.\n if ('code' in result) {\n const res = result as typeof result & { exitCode: number }\n res.exitCode = result.code\n return res\n }\n return result\n }) as PromiseSpawnResult\n }\n if (shouldRestartSpinner) {\n newSpawnPromise = newSpawnPromise.finally(() => {\n spinnerInstance.start()\n }) as PromiseSpawnResult\n }\n // Copy process and stdin properties from original promise\n ;(newSpawnPromise as unknown as PromiseSpawnResult).process =\n oldSpawnPromise.process\n ;(newSpawnPromise as unknown as PromiseSpawnResult).stdin = (\n oldSpawnPromise as unknown as PromiseSpawnResult\n ).stdin\n return newSpawnPromise as SpawnResult\n}\n\n/*@__NO_SIDE_EFFECTS__*/\n/**\n * Options for synchronously spawning a child process with {@link spawnSync}.\n * Same as {@link SpawnOptions} but excludes the `spinner` property (not applicable for synchronous execution).\n */\nexport type SpawnSyncOptions = Omit<SpawnOptions, 'spinner'>\n\n/**\n * Synchronously spawn a child process and wait for it to complete.\n * Blocks execution until the process exits, returning all output and exit information.\n *\n * WARNING: This function blocks the event loop. Use {@link spawn} for async operations.\n *\n * @param {string} cmd - Command to execute\n * @param {string[] | readonly string[] | undefined} args - Array of arguments\n * @param {SpawnSyncOptions | undefined} options - Spawn options for process configuration\n * @returns {SpawnSyncReturns<string | Buffer>} Process result with exit code and captured output\n *\n * @example\n * // Basic synchronous spawn\n * const result = spawnSync('git', ['status'])\n * console.log(result.stdout)\n * console.log(result.status) // exit code\n *\n * @example\n * // With options\n * const result = spawnSync('npm', ['install'], {\n * cwd: '/path/to/project',\n * stdioString: true\n * })\n * if (result.status !== 0) {\n * console.error(result.stderr)\n * }\n *\n * @example\n * // Get raw buffer output\n * const result = spawnSync('cat', ['binary-file'], {\n * stdioString: false\n * })\n * console.log(result.stdout) // Buffer\n *\n * @example\n * // Handle process errors\n * const result = spawnSync('nonexistent-command')\n * if (result.error) {\n * console.error('Failed to spawn:', result.error)\n * }\n */\nexport function spawnSync(\n cmd: string,\n args?: string[] | readonly string[],\n options?: SpawnSyncOptions | undefined,\n): SpawnSyncReturns<string | Buffer> {\n // Windows cmd.exe command resolution for .cmd/.bat/.ps1 files:\n // See spawn() function above for detailed explanation of this approach.\n const shell = getOwn(options, 'shell')\n // Inline WIN32 constant for coverage mode compatibility\n const WIN32 = process.platform === 'win32'\n let actualCmd = cmd\n if (WIN32 && shell && windowsScriptExtRegExp.test(actualCmd)) {\n const path = getPath()\n // Extract just the command name without path and extension.\n actualCmd = path.basename(actualCmd, path.extname(actualCmd))\n }\n const { stripAnsi: shouldStripAnsi = true, ...rawSpawnOptions } = {\n __proto__: null,\n ...options,\n } as SpawnSyncOptions\n const { stdioString: rawStdioString = true } = rawSpawnOptions\n const rawEncoding = rawStdioString ? 'utf8' : 'buffer'\n const spawnOptions = {\n encoding: rawEncoding,\n ...rawSpawnOptions,\n } as NodeSpawnOptions & { encoding: BufferEncoding | 'buffer' }\n const stdioString = spawnOptions.encoding !== 'buffer'\n const result = getChildProcess().spawnSync(actualCmd, args, spawnOptions)\n if (stdioString) {\n const { stderr, stdout } = result\n if (stdout) {\n result.stdout = stdout.toString().trim()\n }\n if (stderr) {\n result.stderr = stderr.toString().trim()\n }\n }\n return (\n shouldStripAnsi && stdioString ? stripAnsiFromSpawnResult(result) : result\n ) as SpawnSyncReturns<string | Buffer>\n}\n"],
5
- "mappings": "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",
6
- "names": ["spawn_exports", "__export", "isSpawnError", "isStdioType", "spawn", "spawnSync", "__toCommonJS", "import_process", "import_arrays", "import_objects", "import_strings", "abortSignal", "spinner", "windowsScriptExtRegExp", "_child_process", "getChildProcess", "_npmCliPromiseSpawn", "getNpmcliPromiseSpawn", "_path", "getPath", "value", "err", "stdio", "type", "stripAnsiFromSpawnResult", "result", "res", "stderr", "stdout", "cmd", "args", "options", "extra", "shell", "WIN32", "actualCmd", "path", "optionsSpinner", "shouldStripAnsi", "spawnOptions", "spinnerInstance", "env", "stdioString", "shouldStopSpinner", "shouldRestartSpinner", "npmCliPromiseSpawn", "promiseSpawnOpts", "spawnPromise", "oldSpawnPromise", "newSpawnPromise", "strippedResult", "error", "rawSpawnOptions", "rawStdioString"]
4
+ "sourcesContent": ["/**\n * @fileoverview Child process spawning utilities with cross-platform support.\n * Provides enhanced spawn functionality with stdio handling and error management.\n *\n * SECURITY: Array-Based Arguments Prevent Command Injection\n *\n * This module uses array-based arguments for all command execution, which is the\n * PRIMARY DEFENSE against command injection attacks. When you pass arguments as\n * an array to spawn():\n *\n * spawn('npx', ['sfw', tool, ...args], { shell: true })\n *\n * Node.js handles escaping automatically. Each argument is passed directly to the\n * OS without shell interpretation. Shell metacharacters like ; | & $ ( ) ` are\n * treated as LITERAL STRINGS, not as commands. This approach is secure even when\n * shell: true is used on Windows for .cmd/.bat file resolution.\n *\n * UNSAFE ALTERNATIVE (not used in this codebase):\n * spawn(`npx sfw ${tool} ${args.join(' ')}`, { shell: true }) // \u2716 VULNERABLE\n *\n * String concatenation allows injection. For example, if tool = \"foo; rm -rf /\",\n * the shell would execute both commands. Array-based arguments prevent this.\n *\n * References:\n * - https://nodejs.org/api/child_process.html#child_processspawncommand-args-options\n * - https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html\n */\n\nimport { getAbortSignal, getSpinner } from '#constants/process'\n\nimport { isArray } from './arrays'\n\nconst abortSignal = getAbortSignal()\nconst spinner = getSpinner()\n\nimport { getOwn, hasOwn } from './objects'\nimport { stripAnsi } from './strings'\n\n// Define BufferEncoding type for TypeScript compatibility.\ntype BufferEncoding = globalThis.BufferEncoding\n\nconst windowsScriptExtRegExp = /\\.(?:cmd|bat|ps1)$/i\n\nlet _child_process: typeof import('node:child_process') | undefined\n/**\n * Lazily load the `child_process` module to avoid Webpack bundling issues.\n *\n * @returns The Node.js `child_process` module\n *\n * @example\n * const childProcess = getChildProcess()\n * childProcess.spawnSync('ls', ['-la'])\n */\n/*@__NO_SIDE_EFFECTS__*/\nfunction getChildProcess() {\n if (_child_process === undefined) {\n // Use non-'node:' prefixed require to avoid Webpack errors.\n\n _child_process = /*@__PURE__*/ require('node:child_process')\n }\n return _child_process as typeof import('node:child_process')\n}\n\n/**\n * Options for spawning a child process with promise-based completion.\n *\n * @property {string | undefined} cwd - Current working directory for the process\n * @property {boolean | undefined} stdioString - Convert stdio output to strings (default: `true`)\n * @property {StdioType | undefined} stdio - Stdio configuration (`'pipe'`, `'ignore'`, `'inherit'`, or array)\n * @property {NodeJS.ProcessEnv | undefined} env - Environment variables for the process\n * @property {boolean | string | undefined} shell - Whether to run command in shell, or path to shell\n * @property {AbortSignal | undefined} signal - Signal to abort the process\n * @property {number | undefined} timeout - Maximum time in milliseconds before killing the process\n * @property {number | undefined} uid - User identity of the process (POSIX only)\n * @property {number | undefined} gid - Group identity of the process (POSIX only)\n */\nexport type PromiseSpawnOptions = {\n cwd?: string | undefined\n stdioString?: boolean | undefined\n stdio?: StdioType | undefined\n env?: NodeJS.ProcessEnv | undefined\n shell?: boolean | string | undefined\n signal?: AbortSignal | undefined\n timeout?: number | undefined\n uid?: number | undefined\n gid?: number | undefined\n}\n\n/**\n * Result returned by {@link spawn} when the child process completes.\n * This is a Promise that resolves with process exit information and output,\n * with additional properties for accessing the running process and stdin stream.\n *\n * @property {ChildProcessType} process - The running child process instance\n * @property {WritableStreamType | null} stdin - Writable stream for process stdin, or `null` if not piped\n *\n * @example\n * const result = spawn('echo', ['hello'])\n * result.stdin?.write('additional input\\n')\n * const { code, stdout } = await result\n * console.log(stdout) // 'hello'\n */\nexport type PromiseSpawnResult = Promise<{\n cmd: string\n args: string[] | readonly string[]\n code: number\n signal: NodeJS.Signals | null\n stdout: string | Buffer\n stderr: string | Buffer\n}> & {\n process: ChildProcessType\n stdin: WritableStreamType | null\n}\n\nlet _npmCliPromiseSpawn:\n | ((\n cmd: string,\n args: string[],\n options?: PromiseSpawnOptions | undefined,\n extra?: SpawnExtra | undefined,\n ) => PromiseSpawnResult)\n | undefined\n/**\n * Lazily load the `@npmcli/promise-spawn` module for async process spawning.\n *\n * @returns The promise-spawn module that provides Promise-based spawn functionality\n *\n * @example\n * const promiseSpawn = getNpmcliPromiseSpawn()\n * await promiseSpawn('git', ['status'])\n */\n/*@__NO_SIDE_EFFECTS__*/\nfunction getNpmcliPromiseSpawn() {\n if (_npmCliPromiseSpawn === undefined) {\n _npmCliPromiseSpawn = /*@__PURE__*/ require('./external/@npmcli/promise-spawn')\n }\n return _npmCliPromiseSpawn as unknown as typeof import('@npmcli/promise-spawn')\n}\n\nlet _path: typeof import('node:path') | undefined\n/**\n * Lazily load the `path` module to avoid Webpack bundling issues.\n *\n * @returns The Node.js `path` module\n *\n * @example\n * const path = getPath()\n * const basename = path.basename('/foo/bar.txt')\n */\n/*@__NO_SIDE_EFFECTS__*/\nfunction getPath() {\n if (_path === undefined) {\n // Use non-'node:' prefixed require to avoid Webpack errors.\n\n _path = /*@__PURE__*/ require('node:path')\n }\n return _path as typeof import('node:path')\n}\n\n/**\n * Error object thrown when a spawned process fails.\n * Extends the standard Error with process-specific information including exit code,\n * signal, command details, and captured output.\n *\n * @property {string[]} args - Arguments passed to the command\n * @property {string} cmd - Command that was executed\n * @property {number} code - Process exit code\n * @property {string} name - Error name (typically `'Error'`)\n * @property {string} message - Error message describing the failure\n * @property {NodeJS.Signals | null} signal - Signal that terminated the process, if any\n * @property {string} stack - Stack trace of the error\n * @property {string | Buffer} stderr - Standard error output from the process\n * @property {string | Buffer} stdout - Standard output from the process\n *\n * @example\n * try {\n * await spawn('exit', ['1'])\n * } catch (error) {\n * if (isSpawnError(error)) {\n * console.error(`Command failed with code ${error.code}`)\n * console.error(`stderr: ${error.stderr}`)\n * }\n * }\n */\nexport type SpawnError = {\n args: string[]\n cmd: string\n code: number\n name: string\n message: string\n signal: NodeJS.Signals | null\n stack: string\n stderr: string | Buffer\n stdout: string | Buffer\n}\n\n/**\n * Spawn error variant where stdout and stderr are guaranteed to be strings.\n * This type is used when `stdioString: true` is set in spawn options.\n *\n * @property {string} stdout - Standard output as a string\n * @property {string} stderr - Standard error as a string\n */\nexport type SpawnErrorWithOutputString = SpawnError & {\n stdout: string\n stderr: string\n}\n\n/**\n * Spawn error variant where stdout and stderr are guaranteed to be Buffers.\n * This type is used when `stdioString: false` is set in spawn options.\n *\n * @property {Buffer} stdout - Standard output as a Buffer\n * @property {Buffer} stderr - Standard error as a Buffer\n */\nexport type SpawnErrorWithOutputBuffer = SpawnError & {\n stdout: Buffer\n stderr: Buffer\n}\n\n/**\n * Extra options passed to the underlying promise-spawn implementation.\n * This is an open-ended object for passing additional metadata or configuration.\n */\nexport type SpawnExtra = Record<string, unknown>\n\n/**\n * Valid values for individual stdio streams.\n * - `'pipe'` - Creates a pipe between child and parent (default)\n * - `'ignore'` - Ignores the stream\n * - `'inherit'` - Uses parent's stream\n * - `'overlapped'` - Windows-specific overlapped I/O\n */\nexport type IOType = 'pipe' | 'ignore' | 'inherit' | 'overlapped'\n\n/**\n * Configuration for process stdio (stdin, stdout, stderr) streams.\n * Can be a single value applied to all streams, or an array specifying each stream individually.\n * - `'ipc'` - Creates an IPC channel for communication with the parent\n *\n * @example\n * // All streams piped\n * stdio: 'pipe'\n *\n * @example\n * // Custom configuration per stream: [stdin, stdout, stderr]\n * stdio: ['ignore', 'pipe', 'pipe']\n */\nexport type StdioType = IOType | 'ipc' | Array<IOType | 'ipc'>\n\n/**\n * Result object returned by {@link spawnSync} when the child process completes synchronously.\n *\n * @template T - Type of stdout/stderr (string or Buffer)\n * @property {number} pid - Process ID of the spawned child\n * @property {Array<T | null>} output - Array containing stdout/stderr values\n * @property {T} stdout - Standard output from the process\n * @property {T} stderr - Standard error from the process\n * @property {number | null} status - Exit code, or `null` if killed by signal\n * @property {NodeJS.Signals | null} signal - Signal that terminated the process, or `null`\n * @property {Error | undefined} error - Error object if the spawn failed\n */\nexport interface SpawnSyncReturns<T> {\n pid: number\n output: Array<T | null>\n stdout: T\n stderr: T\n status: number | null\n signal: NodeJS.Signals | null\n error?: Error | undefined\n}\n\n/**\n * Check if a value is a spawn error with expected error properties.\n * Tests for common error properties from child process failures.\n *\n * @param {unknown} value - Value to check\n * @returns {boolean} `true` if the value has spawn error properties\n *\n * @example\n * try {\n * await spawn('nonexistent-command')\n * } catch (error) {\n * if (isSpawnError(error)) {\n * console.error(`Spawn failed: ${error.code}`)\n * }\n * }\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function isSpawnError(value: unknown): value is SpawnError {\n if (value === null || typeof value !== 'object') {\n return false\n }\n // Check for spawn-specific error properties.\n const err = value as Record<string, unknown>\n return (\n (hasOwn(err, 'code') && typeof err['code'] !== 'undefined') ||\n (hasOwn(err, 'errno') && typeof err['errno'] !== 'undefined') ||\n (hasOwn(err, 'syscall') && typeof err['syscall'] === 'string')\n )\n}\n\n/**\n * Check if stdio configuration matches a specific type.\n * When called with one argument, validates if it's a valid stdio type.\n * When called with two arguments, checks if the stdio config matches the specified type.\n *\n * @param {string | string[]} stdio - Stdio configuration to check\n * @param {StdioType | undefined} type - Expected stdio type (optional)\n * @returns {boolean} `true` if stdio matches the type or is valid\n *\n * @example\n * // Check if valid stdio type\n * isStdioType('pipe') // true\n * isStdioType('invalid') // false\n *\n * @example\n * // Check if stdio matches specific type\n * isStdioType('pipe', 'pipe') // true\n * isStdioType(['pipe', 'pipe', 'pipe'], 'pipe') // true\n * isStdioType('ignore', 'pipe') // false\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function isStdioType(\n stdio: string | string[],\n type?: StdioType | undefined,\n): boolean {\n // If called with one argument, check if it's a valid stdio type.\n // biome-ignore lint/complexity/noArguments: Function overload detection for single vs two-arg calls.\n if (arguments.length === 1) {\n const validTypes = ['pipe', 'ignore', 'inherit', 'overlapped']\n return typeof stdio === 'string' && validTypes.includes(stdio)\n }\n // Original two-argument behavior.\n return (\n stdio === type ||\n ((stdio === null || stdio === undefined) && type === 'pipe') ||\n (isArray(stdio) &&\n stdio.length > 2 &&\n stdio[0] === type &&\n stdio[1] === type &&\n stdio[2] === type)\n )\n}\n\n/**\n * Strip ANSI escape codes from spawn result stdout and stderr.\n * Modifies the result object in place to remove color codes and formatting.\n *\n * @param {unknown} result - Spawn result object with stdout/stderr properties\n * @returns {unknown} The modified result object\n */\n/*@__NO_SIDE_EFFECTS__*/\nfunction stripAnsiFromSpawnResult(result: unknown): unknown {\n const res = result as {\n stdout?: string | Buffer\n stderr?: string | Buffer\n }\n const { stderr, stdout } = res\n if (typeof stdout === 'string') {\n res.stdout = stripAnsi(stdout)\n }\n if (typeof stderr === 'string') {\n res.stderr = stripAnsi(stderr)\n }\n return res\n}\n\n/*@__NO_SIDE_EFFECTS__*/\n// Duplicated from Node.js child_process.SpawnOptions\n// These are the options passed to child_process.spawn()\ninterface NodeSpawnOptions {\n cwd?: string | URL | undefined\n env?: NodeJS.ProcessEnv | undefined\n argv0?: string | undefined\n stdio?: any\n detached?: boolean | undefined\n uid?: number | undefined\n gid?: number | undefined\n serialization?: 'json' | 'advanced' | undefined\n shell?: boolean | string | undefined\n windowsVerbatimArguments?: boolean | undefined\n windowsHide?: boolean | undefined\n signal?: AbortSignal | undefined\n timeout?: number | undefined\n killSignal?: NodeJS.Signals | number | undefined\n}\n\n// Duplicated from Node.js child_process.ChildProcess\n// This represents a spawned child process\ninterface ChildProcessType {\n stdin: NodeJS.WritableStream | null\n stdout: NodeJS.ReadableStream | null\n stderr: NodeJS.ReadableStream | null\n readonly channel?: any\n readonly stdio: [\n NodeJS.WritableStream | null,\n NodeJS.ReadableStream | null,\n NodeJS.ReadableStream | null,\n NodeJS.ReadableStream | NodeJS.WritableStream | null | undefined,\n NodeJS.ReadableStream | NodeJS.WritableStream | null | undefined,\n ]\n readonly killed: boolean\n readonly pid?: number | undefined\n readonly connected: boolean\n readonly exitCode: number | null\n readonly signalCode: NodeJS.Signals | null\n readonly spawnargs: string[]\n readonly spawnfile: string\n kill(signal?: NodeJS.Signals | number): boolean\n send(message: any, callback?: (error: Error | null) => void): boolean\n send(\n message: any,\n sendHandle?: any | undefined,\n callback?: (error: Error | null) => void,\n ): boolean\n send(\n message: any,\n sendHandle?: any | undefined,\n options?: any | undefined,\n callback?: (error: Error | null) => void,\n ): boolean\n disconnect(): void\n unref(): void\n ref(): void\n}\n\n// Duplicated from Node.js stream.Writable\ninterface WritableStreamType {\n writable: boolean\n writableEnded: boolean\n writableFinished: boolean\n writableHighWaterMark: number\n writableLength: number\n writableObjectMode: boolean\n writableCorked: number\n destroyed: boolean\n write(\n chunk: any,\n encoding?: BufferEncoding | undefined,\n callback?: (error?: Error | null) => void,\n ): boolean\n write(chunk: any, callback?: (error?: Error | null) => void): boolean\n end(cb?: () => void): this\n end(chunk: any, cb?: () => void): this\n end(chunk: any, encoding?: BufferEncoding | undefined, cb?: () => void): this\n cork(): void\n uncork(): void\n destroy(error?: Error | undefined): this\n}\n\n/**\n * Options for spawning a child process with {@link spawn}.\n * Extends Node.js spawn options with additional Socket-specific functionality.\n *\n * @property {import('./spinner').Spinner | undefined} spinner - Spinner instance to pause during execution\n * @property {boolean | undefined} stdioString - Convert output to strings (default: `true`)\n * @property {boolean | undefined} stripAnsi - Remove ANSI codes from output (default: `true`)\n * @property {string | URL | undefined} cwd - Current working directory\n * @property {NodeJS.ProcessEnv | undefined} env - Environment variables\n * @property {StdioType | undefined} stdio - Stdio configuration\n * @property {boolean | string | undefined} shell - Run command in shell\n * @property {number | undefined} timeout - Timeout in milliseconds\n * @property {AbortSignal | undefined} signal - Abort signal\n * @property {number | undefined} uid - User identity (POSIX)\n * @property {number | undefined} gid - Group identity (POSIX)\n */\nexport type SpawnOptions = import('./objects').Remap<\n NodeSpawnOptions & {\n spinner?: import('./spinner').Spinner | undefined\n stdioString?: boolean\n stripAnsi?: boolean\n }\n>\nexport type SpawnResult = PromiseSpawnResult\n/**\n * Result object returned when a spawned process completes.\n *\n * @property {string} cmd - Command that was executed\n * @property {string[] | readonly string[]} args - Arguments passed to the command\n * @property {number} code - Process exit code\n * @property {NodeJS.Signals | null} signal - Signal that terminated the process, if any\n * @property {string | Buffer} stdout - Standard output (string if `stdioString: true`, Buffer otherwise)\n * @property {string | Buffer} stderr - Standard error (string if `stdioString: true`, Buffer otherwise)\n */\nexport type SpawnStdioResult = {\n cmd: string\n args: string[] | readonly string[]\n code: number\n signal: NodeJS.Signals | null\n stdout: string | Buffer\n stderr: string | Buffer\n}\n\n/**\n * Spawn a child process and return a promise that resolves when it completes.\n * Provides enhanced error handling, output capture, and cross-platform support.\n *\n * SECURITY: This function uses array-based arguments which prevent command injection.\n * Arguments in the `args` array are passed directly to the OS without shell\n * interpretation. Shell metacharacters (;|&$()`) are treated as literal strings,\n * not as commands or operators. This is the PRIMARY SECURITY DEFENSE.\n *\n * Even when shell: true is used (on Windows for .cmd/.bat execution), the array-based\n * approach remains secure because Node.js properly escapes each argument before passing\n * to the shell.\n *\n * @param {string} cmd - Command to execute (not user-controlled)\n * @param {string[] | readonly string[] | undefined} args - Array of arguments (safe even with user input)\n * @param {SpawnOptions | undefined} options - Spawn options for process configuration\n * @param {SpawnExtra | undefined} extra - Extra options for promise-spawn\n * @returns {SpawnResult} Promise that resolves with process exit information\n *\n * @throws {SpawnError} When the process exits with non-zero code or is terminated by signal\n *\n * @example\n * // Basic usage - spawn and wait for completion\n * const result = await spawn('git', ['status'])\n * console.log(result.stdout)\n *\n * @example\n * // With options - set working directory and environment\n * const result = await spawn('npm', ['install'], {\n * cwd: '/path/to/project',\n * env: { NODE_ENV: 'production' }\n * })\n *\n * @example\n * // \u2714 DO THIS - Array-based arguments (safe)\n * spawn('git', ['commit', '-m', userMessage])\n * // Each argument is properly escaped, even if userMessage = \"foo; rm -rf /\"\n *\n * @example\n * // \u2716 NEVER DO THIS - String concatenation (vulnerable)\n * spawn(`git commit -m \"${userMessage}\"`, { shell: true })\n * // Vulnerable to injection if userMessage = '\"; rm -rf / #'\n *\n * @example\n * // Access stdin for interactive processes\n * const result = spawn('cat', [])\n * result.stdin?.write('Hello\\n')\n * result.stdin?.end()\n * const { stdout } = await result\n * console.log(stdout) // 'Hello'\n *\n * @example\n * // Handle errors with exit codes\n * try {\n * await spawn('exit', ['1'])\n * } catch (error) {\n * if (isSpawnError(error)) {\n * console.error(`Failed with code ${error.code}`)\n * console.error(error.stderr)\n * }\n * }\n */\nexport function spawn(\n cmd: string,\n args?: string[] | readonly string[],\n options?: SpawnOptions | undefined,\n extra?: SpawnExtra | undefined,\n): SpawnResult {\n // Windows cmd.exe command resolution for .cmd/.bat/.ps1 files:\n //\n // When shell: true is used on Windows with script files (.cmd, .bat, .ps1),\n // cmd.exe can have issues executing full paths. The solution is to use just\n // the command basename without extension and let cmd.exe find it via PATH.\n //\n // How cmd.exe resolves commands:\n // 1. Searches current directory first\n // 2. Then searches each directory in PATH environment variable\n // 3. For each directory, tries extensions from PATHEXT (.COM, .EXE, .BAT, .CMD, etc.)\n // 4. Executes the first match found\n //\n // Example: Given 'C:\\pnpm\\pnpm.cmd' with shell: true\n // 1. Extract basename without extension: 'pnpm'\n // 2. cmd.exe searches PATH directories for 'pnpm'\n // 3. PATHEXT causes it to try 'pnpm.com', 'pnpm.exe', 'pnpm.bat', 'pnpm.cmd', etc.\n // 4. Finds and executes 'C:\\pnpm\\pnpm.cmd'\n //\n // This approach is consistent with how other tools handle Windows execution:\n // - npm's promise-spawn: uses which.sync() to find commands in PATH\n // - cross-spawn: spawns cmd.exe with escaped arguments\n // - execa: uses cross-spawn under the hood for Windows support\n //\n // See: https://github.com/nodejs/node/issues/3675\n const shell = getOwn(options, 'shell')\n // Inline WIN32 constant for coverage mode compatibility\n const WIN32 = process.platform === 'win32'\n let actualCmd = cmd\n if (WIN32 && shell && windowsScriptExtRegExp.test(actualCmd)) {\n const path = getPath()\n // Extract just the command name without path and extension.\n actualCmd = path.basename(actualCmd, path.extname(actualCmd))\n }\n const {\n spinner: optionsSpinner = spinner,\n stripAnsi: shouldStripAnsi = true,\n ...spawnOptions\n } = { __proto__: null, ...options } as SpawnOptions\n const spinnerInstance = optionsSpinner\n const { env, stdio, stdioString = true } = spawnOptions\n // The stdio option can be a string or an array.\n // https://nodejs.org/api/child_process.html#optionsstdio\n const wasSpinning = !!spinnerInstance?.isSpinning\n const shouldStopSpinner =\n wasSpinning && !isStdioType(stdio, 'ignore') && !isStdioType(stdio, 'pipe')\n const shouldRestartSpinner = shouldStopSpinner\n if (shouldStopSpinner) {\n spinnerInstance.stop()\n }\n const npmCliPromiseSpawn = getNpmcliPromiseSpawn()\n // Use __proto__: null to prevent prototype pollution when passing to\n // third-party code, Node.js built-ins, or JavaScript built-in methods.\n // https://github.com/npm/promise-spawn\n // https://github.com/nodejs/node/blob/v24.0.1/lib/child_process.js#L674-L678\n // Preserve Windows process.env Proxy behavior when no custom env is provided.\n // On Windows, process.env is a Proxy that provides case-insensitive access\n // (PATH vs Path vs path). Spreading creates a plain object that loses this.\n // Only spread when we have custom environment variables to merge.\n const envToUse = env\n ? ({\n __proto__: null,\n ...process.env,\n ...env,\n } as unknown as NodeJS.ProcessEnv)\n : process.env\n\n const promiseSpawnOpts = {\n __proto__: null,\n cwd: typeof spawnOptions.cwd === 'string' ? spawnOptions.cwd : undefined,\n env: envToUse,\n signal: abortSignal,\n stdio: spawnOptions.stdio,\n stdioString,\n shell: spawnOptions.shell,\n timeout: spawnOptions.timeout,\n uid: spawnOptions.uid,\n gid: spawnOptions.gid,\n } as unknown as PromiseSpawnOptions\n const spawnPromise = npmCliPromiseSpawn(\n actualCmd,\n args ? [...args] : [],\n promiseSpawnOpts as Parameters<typeof npmCliPromiseSpawn>[2],\n extra,\n )\n const oldSpawnPromise = spawnPromise\n let newSpawnPromise: PromiseSpawnResult\n if (shouldStripAnsi && stdioString) {\n newSpawnPromise = spawnPromise\n .then(result => {\n const strippedResult = stripAnsiFromSpawnResult(result)\n // Add exitCode as an alias for code.\n if ('code' in (strippedResult as { code?: number })) {\n ;(strippedResult as { code: number; exitCode: number }).exitCode = (\n strippedResult as { code: number }\n ).code\n }\n return strippedResult\n })\n .catch(error => {\n throw stripAnsiFromSpawnResult(error)\n }) as PromiseSpawnResult\n } else {\n newSpawnPromise = spawnPromise.then(result => {\n // Add exitCode as an alias for code.\n if ('code' in result) {\n const res = result as typeof result & { exitCode: number }\n res.exitCode = result.code\n return res\n }\n return result\n }) as PromiseSpawnResult\n }\n if (shouldRestartSpinner) {\n newSpawnPromise = newSpawnPromise.finally(() => {\n spinnerInstance.start()\n }) as PromiseSpawnResult\n }\n // Copy process and stdin properties from original promise\n ;(newSpawnPromise as unknown as PromiseSpawnResult).process =\n oldSpawnPromise.process\n ;(newSpawnPromise as unknown as PromiseSpawnResult).stdin = (\n oldSpawnPromise as unknown as PromiseSpawnResult\n ).stdin\n return newSpawnPromise as SpawnResult\n}\n\n/*@__NO_SIDE_EFFECTS__*/\n/**\n * Options for synchronously spawning a child process with {@link spawnSync}.\n * Same as {@link SpawnOptions} but excludes the `spinner` property (not applicable for synchronous execution).\n */\nexport type SpawnSyncOptions = Omit<SpawnOptions, 'spinner'>\n\n/**\n * Synchronously spawn a child process and wait for it to complete.\n * Blocks execution until the process exits, returning all output and exit information.\n *\n * WARNING: This function blocks the event loop. Use {@link spawn} for async operations.\n *\n * @param {string} cmd - Command to execute\n * @param {string[] | readonly string[] | undefined} args - Array of arguments\n * @param {SpawnSyncOptions | undefined} options - Spawn options for process configuration\n * @returns {SpawnSyncReturns<string | Buffer>} Process result with exit code and captured output\n *\n * @example\n * // Basic synchronous spawn\n * const result = spawnSync('git', ['status'])\n * console.log(result.stdout)\n * console.log(result.status) // exit code\n *\n * @example\n * // With options\n * const result = spawnSync('npm', ['install'], {\n * cwd: '/path/to/project',\n * stdioString: true\n * })\n * if (result.status !== 0) {\n * console.error(result.stderr)\n * }\n *\n * @example\n * // Get raw buffer output\n * const result = spawnSync('cat', ['binary-file'], {\n * stdioString: false\n * })\n * console.log(result.stdout) // Buffer\n *\n * @example\n * // Handle process errors\n * const result = spawnSync('nonexistent-command')\n * if (result.error) {\n * console.error('Failed to spawn:', result.error)\n * }\n */\nexport function spawnSync(\n cmd: string,\n args?: string[] | readonly string[],\n options?: SpawnSyncOptions | undefined,\n): SpawnSyncReturns<string | Buffer> {\n // Windows cmd.exe command resolution for .cmd/.bat/.ps1 files:\n // See spawn() function above for detailed explanation of this approach.\n const shell = getOwn(options, 'shell')\n // Inline WIN32 constant for coverage mode compatibility\n const WIN32 = process.platform === 'win32'\n let actualCmd = cmd\n if (WIN32 && shell && windowsScriptExtRegExp.test(actualCmd)) {\n const path = getPath()\n // Extract just the command name without path and extension.\n actualCmd = path.basename(actualCmd, path.extname(actualCmd))\n }\n const { stripAnsi: shouldStripAnsi = true, ...rawSpawnOptions } = {\n __proto__: null,\n ...options,\n } as SpawnSyncOptions\n const { stdioString: rawStdioString = true } = rawSpawnOptions\n const rawEncoding = rawStdioString ? 'utf8' : 'buffer'\n const spawnOptions = {\n encoding: rawEncoding,\n ...rawSpawnOptions,\n } as NodeSpawnOptions & { encoding: BufferEncoding | 'buffer' }\n const stdioString = spawnOptions.encoding !== 'buffer'\n const result = getChildProcess().spawnSync(actualCmd, args, spawnOptions)\n if (stdioString) {\n const { stderr, stdout } = result\n if (stdout) {\n result.stdout = stdout.toString().trim()\n }\n if (stderr) {\n result.stderr = stderr.toString().trim()\n }\n }\n return (\n shouldStripAnsi && stdioString ? stripAnsiFromSpawnResult(result) : result\n ) as SpawnSyncReturns<string | Buffer>\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["spawn_exports", "__export", "isSpawnError", "isStdioType", "spawn", "spawnSync", "__toCommonJS", "import_process", "import_arrays", "import_objects", "import_strings", "abortSignal", "spinner", "windowsScriptExtRegExp", "_child_process", "getChildProcess", "_npmCliPromiseSpawn", "getNpmcliPromiseSpawn", "_path", "getPath", "value", "err", "stdio", "type", "stripAnsiFromSpawnResult", "result", "res", "stderr", "stdout", "cmd", "args", "options", "extra", "shell", "WIN32", "actualCmd", "path", "optionsSpinner", "shouldStripAnsi", "spawnOptions", "spinnerInstance", "env", "stdioString", "shouldStopSpinner", "shouldRestartSpinner", "npmCliPromiseSpawn", "envToUse", "promiseSpawnOpts", "spawnPromise", "oldSpawnPromise", "newSpawnPromise", "strippedResult", "error", "rawSpawnOptions", "rawStdioString"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socketsecurity/lib",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "license": "MIT",
5
5
  "description": "Core utilities and infrastructure for Socket.dev security tools",
6
6
  "keywords": [
@@ -760,8 +760,8 @@
760
760
  "@socketregistry/yocto-spinner": "1.0.19",
761
761
  "@types/node": "24.6.2",
762
762
  "@typescript/native-preview": "7.0.0-dev.20250920.1",
763
- "@vitest/coverage-v8": "3.2.4",
764
- "@vitest/ui": "3.2.4",
763
+ "@vitest/coverage-v8": "4.0.3",
764
+ "@vitest/ui": "4.0.3",
765
765
  "@yarnpkg/extensions": "2.0.6",
766
766
  "cacache": "20.0.1",
767
767
  "debug": "4.4.3",
@@ -799,7 +799,7 @@
799
799
  "typescript-eslint": "8.44.1",
800
800
  "validate-npm-package-name": "6.0.2",
801
801
  "vite-tsconfig-paths": "5.1.4",
802
- "vitest": "3.2.4",
802
+ "vitest": "4.0.3",
803
803
  "which": "5.0.0",
804
804
  "yargs-parser": "22.0.0",
805
805
  "yoctocolors-cjs": "2.1.3",