UNPKG

pastoralist

Version:

A tool to watch over node module resolutions and overrides

77 lines (68 loc) 28.9 kB
#!/usr/bin/env node import{$ as nt,E,I as ue,J as oe,Z as it,_ as st,b as Je,ba as Ge,c as U,d as ze,da as ae,e as Ye,f as T,g as Qe,h as Xe,i as M,j as ce,k as He,l as Ze,m as et,n as tt,o as xe,p as Re,q as Ie,r as Te,s as H,t as _e,u as Be,v as N,w as rt}from"./index-cnqzpzfz.js";var X=[{flags:["--debug"],hasValue:!1},{flags:["--dry-run"],hasValue:!1},{flags:["--outputFormat"],hasValue:!0,defaultValue:"text"},{flags:["-p","--path"],hasValue:!0,defaultValue:"package.json"},{flags:["-d","--depPaths"],hasValue:!0,isArray:!0},{flags:["--ignore"],hasValue:!0,isArray:!0},{flags:["-r","--root"],hasValue:!0},{flags:["-t","--isTestingCLI"],hasValue:!1},{flags:["--isTesting"],hasValue:!1},{flags:["--init"],hasValue:!1},{flags:["--checkSecurity"],hasValue:!1},{flags:["--forceSecurityRefactor"],hasValue:!1},{flags:["--securityProvider"],hasValue:!0,isArray:!0},{flags:["--securityProviderToken"],hasValue:!0},{flags:["--interactive"],hasValue:!1},{flags:["--hasWorkspaceSecurityChecks"],hasValue:!1},{flags:["--promptForReasons"],hasValue:!1},{flags:["--strict"],hasValue:!1},{flags:["--summary"],hasValue:!1},{flags:["-q","--quiet"],hasValue:!1},{flags:["--setup-hook"],hasValue:!1},{flags:["--remove-unused"],hasValue:!1},{flags:["--cache-dir"],hasValue:!0},{flags:["--cache-ttl"],hasValue:!0},{flags:["--no-cache"],hasValue:!1},{flags:["--refresh-cache"],hasValue:!1}],me=` Pastoralist - A utility CLI to manage your dependency overrides Usage: pastoralist [command] [options] Commands: init Initialize Pastoralist configuration interactively Options: --debug Enable debug mode --dry-run Preview changes without writing to package.json --outputFormat <format> Output format: text (default) or json -p, --path <path> Specifies a path to a package.json (default: "package.json") -d, --depPaths [paths...] Specifies glob paths to package.jsons --ignore [paths...] Specifies glob paths to ignore -r, --root <root> Specifies a root path -t, --isTestingCLI Enable CLI testing (no scripts run) --isTesting Enable testing mode (no scripts run) --init Initialize Pastoralist configuration interactively --checkSecurity Check for security vulnerabilities and generate overrides --forceSecurityRefactor Automatically apply security overrides without prompting --securityProvider <provider...> Security provider(s) to use (osv, github, snyk, npm, socket, spektion) --securityProviderToken <token> Security provider token for API access --interactive Run security checks in interactive mode --hasWorkspaceSecurityChecks Include workspace packages in security scan --promptForReasons Prompt for reasons when adding manual overrides --strict Fail on any security check errors (network failures, API errors) --summary Show summary metrics table after run -q, --quiet Quiet mode for CI (exit 1 if vulnerabilities, 0 if clean) --setup-hook Add postinstall script to run pastoralist automatically --remove-unused Remove unused overrides from package.json --cache-dir <path> Cache directory (default: node_modules/.cache/pastoralist/) --cache-ttl <seconds> Override cache TTL in seconds --no-cache Bypass cache reads and writes --refresh-cache Bypass cache reads, force refresh (still writes) `,ve=2;var pt=(e)=>X.find((t)=>t.flags.includes(e)),ke=(e)=>{return(e.flags.find((r)=>r.startsWith("--"))||e.flags[0]).replace(/^--?/,"").replace(/-([a-z])/g,(r,i)=>i.toUpperCase())},ft=(e)=>{let t=e.indexOf("=");return t>-1?{flag:e.slice(0,t),value:e.slice(t+1)}:{flag:e}},Z=(e)=>e.startsWith("-"),yt=(e,t)=>{let r=[],i=t+1;while(i<e.length&&!Z(e[i]))r.push(e[i]),i++;let s=r.length>0,n=i-t-1;return{value:s?r:void 0,consumed:n}},ht=(e,t)=>{let r=e[t+1];return r&&!Z(r)?{value:r,consumed:1}:{value:!0,consumed:0}},gt=(e,t,r)=>r.isArray?yt(e,t):ht(e,t),mt=(e)=>X.reduce((t,r)=>{let i=ke(r);return t[i]===void 0&&r.defaultValue!==void 0?Object.assign({},t,{[i]:r.defaultValue}):t},e),vt=(e,t,r)=>{let i=e[t];if(!Z(i))return{nextIndex:t+1,options:r.options,command:i};let{flag:n,value:a}=ft(i),c=pt(n);if(!c)return{nextIndex:t+1,options:r.options,command:r.command};let u=ke(c);if(a!==void 0){let O=Object.assign({},r.options,{[u]:a});return{nextIndex:t+1,options:O,command:r.command}}if(!c.hasValue){let O=Object.assign({},r.options,{[u]:!0});return{nextIndex:t+1,options:O,command:r.command}}let{value:k,consumed:p}=gt(e,t,c),m=Object.assign({},r.options,{[u]:k});return{nextIndex:t+p+1,options:m,command:r.command}},Se=(e)=>{let t=e.slice(ve),r=0,i={options:{},command:void 0};while(r<t.length){let n=vt(t,r,i);r=n.nextIndex,i={options:n.options,command:n.command}}let s=mt(i.options);return{command:i.command,options:s}},Pe=()=>{console.log(me)};import{writeFileSync as qe,existsSync as Ot}from"fs";import{resolve as Ct}from"path";import*as Ne from"readline";var ee=[],Oe=0,te=!1,Ce=!1;function kt(){if(Ce||process.stdin.isTTY)return;Ce=!0;let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",(t)=>{e+=t}),process.stdin.on("end",()=>{ee=e.trim().split(` `),te=!0})}function be(){return!process.stdin.isTTY}async function St(){if(!be())return;kt();while(!te)await new Promise((e)=>setTimeout(e,10))}function Pt(){if(!be()||!te)return null;if(Oe<ee.length)return ee[Oe++];return""}async function W(e,t,r=(i)=>i.trim()){return await St(),new Promise((i)=>{let s=Pt();if(s!==null)console.log(`${t}${s}`),i(r(s));else e.question(t,(n)=>{i(r(n))})})}class Ae{rl;constructor(){this.rl=Ne.createInterface({input:process.stdin,output:process.stdout})}close(){this.rl.close()}ensureCookedMode(){if(process.stdin.isTTY)process.stdin.setRawMode(!1)}async input(e,t){return this.ensureCookedMode(),W(this.rl,Te(e,t),(r)=>r.trim()||t||"")}async confirm(e,t=!0){return this.ensureCookedMode(),W(this.rl,xe(e,t),(r)=>{let i=r.trim().toLowerCase();if(i==="")return t;else return i==="y"||i==="yes"})}async list(e,t){return console.log(Re(e,t)),this.ensureCookedMode(),W(this.rl,Ie(),(r)=>{let i=parseInt(r.trim(),10);if(isNaN(i)||i<1||i>t.length)return console.log("Invalid choice. Please enter a number between 1 and "+t.length),t[0].value;return t[i-1].value})}async prompt(e){let{type:t="input",message:r}=e;switch(t){case"confirm":return this.confirm(r,e.default??!0);case"list":return this.list(r,e.choices);case"input":default:return this.input(r,e.default??"")}}async promptMany(e){return e.reduce(async(t,r,i)=>{let s=await t,n=`answer${i}`,a=r.type==="confirm",c=r.type==="list";if(a)s[n]=await this.prompt(r);else if(c)s[n]=await this.prompt(r);else s[n]=await this.prompt(r);return s},Promise.resolve({}))}}async function Ve(e){let t=new Ae;try{return await e(t)}finally{t.close()}}var Ee=[{name:"In package.json (recommended for simple setups)",value:"package.json"},{name:"In a separate config file (recommended for complex setups)",value:"external"}],we=[{name:".pastoralistrc.json (JSON format)",value:".pastoralistrc.json"},{name:"pastoralist.config.js (JavaScript module)",value:"pastoralist.config.js"},{name:"pastoralist.config.ts (TypeScript module)",value:"pastoralist.config.ts"}],Fe=[{name:'Use "workspace" keyword (auto-detect from package.json)',value:"workspace"},{name:"Specify custom paths",value:"custom"}],re=[{name:"OSV (Open Source Vulnerabilities - free, recommended)",value:"osv"},{name:"GitHub Advisory Database",value:"github"},{name:"Snyk (requires token)",value:"snyk"},{name:"Socket.dev (requires token)",value:"socket"}],ie=[{name:"Low (all vulnerabilities)",value:"low"},{name:"Medium (medium and above)",value:"medium"},{name:"High (high and critical only)",value:"high"},{name:"Critical (critical only)",value:"critical"}],De="packages/*/package.json",g={welcome:"This wizard will help you set up your Pastoralist configuration.",skipInfo:"You can skip any step by selecting the skip option.",existingConfigWarning:"Existing Pastoralist configuration detected. Do you want to overwrite it?",existingFileWarning:(e)=>`${e} already exists. Overwrite?`,configNotSaved:"Configuration not saved. File preserved.",configSaved:(e)=>`Configuration saved to ${e}`,initCancelled:"Initialization cancelled. Your existing configuration is preserved.",initComplete:"▪▫▪ Pastoralist initialization complete! ▪▫▪",packageJsonNotFound:"Error: package.json not found",noTokenProvided:()=>"No token provided. You can add it later to the config or use --securityProviderToken flag.",tokenCreationInfo:(e,t)=>`To create a ${e} token, visit: ${t}`,tokenRequiredWarning:(e)=>`${e} requires a token to function. Security scanning will not work without it.`,workspacesDetected:(e)=>`Detected workspaces in package.json: ${e.join(", ")}`,noWorkspacesDetected:"No workspaces detected in package.json.",savingConfig:"Saving configuration...",nextSteps:"Next Steps:"},$e={configLocation:"Step 1: Configuration Location",workspace:"Step 2: Workspace Configuration",security:"Step 3: Security Configuration"},f={configLocation:"Where would you like to store your Pastoralist configuration?",configFormat:"Choose a config file format:",setupWorkspaces:"Do you want to configure workspace dependencies?",workspaceType:"How would you like to configure workspace dependencies?",customWorkspacePaths:"Enter workspace paths (comma-separated glob patterns)",setupSecurity:"Do you want to enable security vulnerability scanning?",securityProvider:"Choose a security provider:",hasToken:(e)=>`Do you have a ${e} API token?`,enterToken:(e)=>`Enter your ${e} API token (will be stored in config)`,securityInteractive:"Enable interactive mode for security fixes? (allows you to review/approve each fix)",securityAutoFix:"Enable auto-fix mode? (automatically applies security overrides without prompting)",severityThreshold:"What severity level should trigger alerts?",hasWorkspaceSecurityChecks:"Scan workspace packages for vulnerabilities?"};function je(e){return e.split(",").map((t)=>t.trim()).filter((t)=>t.length>0)}function Le(e){let t={};if(e.setupWorkspaces){if(e.workspaceType==="workspace")t.depPaths="workspace";if(e.workspaceType==="custom"&&e.customWorkspacePaths?.length)t.depPaths=e.customWorkspacePaths}if(e.setupSecurity){if(t.checkSecurity=!0,t.security={enabled:!0,provider:e.securityProvider,interactive:e.securityInteractive,autoFix:e.securityAutoFix,severityThreshold:e.severityThreshold,hasWorkspaceSecurityChecks:e.hasWorkspaceSecurityChecks},Boolean(e.securityProviderToken))t.security.securityProviderToken=e.securityProviderToken}return t}function We(e,t){if(t.endsWith(".json"))return JSON.stringify(e,null,2)+` `;if(t.endsWith(".js"))return`module.exports = ${JSON.stringify(e,null,2)}; `;return`import type { PastoralistConfig } from 'pastoralist'; const config: PastoralistConfig = ${JSON.stringify(e,null,2)}; export default config; `}async function Me(e,t,r){if(r.print(H(1,"Configuration Location")),t.configLocation=await e.list(f.configLocation,Ee),t.configLocation==="external")t.configFormat=await e.list(f.configFormat,we)}async function bt(e){let t=await e.input(f.customWorkspacePaths,De);return je(t)}async function xt(e,t,r,i){if(!t)return"custom";return i.print(` ${g.workspacesDetected(r)}`),await e.list(f.workspaceType,Fe)}async function Ue(e,t,r,i){if(i.print(H(2,"Workspace Configuration")),t.setupWorkspaces=await e.confirm(f.setupWorkspaces,!0),!t.setupWorkspaces)return;let s=Boolean(r?.workspaces&&r.workspaces.length>0);if(!s)i.print(_e(g.noWorkspacesDetected));if(t.workspaceType=await xt(e,s,r?.workspaces||[],i),t.workspaceType==="custom")t.customWorkspacePaths=await bt(e)}async function Ke(e,t,r){let i=Rt(t);if(!i.required&&!i.optional)return;if(i.createUrl)r.print(` ${g.tokenCreationInfo(t,i.createUrl)}`);if(!await e.confirm(f.hasToken(t),!1)){if(i.required)r.print(` ${g.tokenRequiredWarning(t)}`);return}let n=await e.input(f.enterToken(t),"");if(!n){r.print(` ${g.noTokenProvided()}`);return}return n}function Rt(e){switch(e){case"github":return{required:!1,optional:!0,createUrl:"https://github.com/settings/tokens/new?description=Pastoralist%20Security&scopes=repo",scopes:["repo"]};case"snyk":return{required:!0,optional:!1,createUrl:"https://app.snyk.io/account"};case"socket":return{required:!0,optional:!1,createUrl:"https://socket.dev/dashboard/settings"};default:return{required:!1,optional:!1}}}async function It(e,t,r){if(r.print(H(3,"Security Configuration")),t.setupSecurity=await e.confirm(f.setupSecurity,!0),!t.setupSecurity)return;if(t.securityProvider=await e.list(f.securityProvider,re),t.securityProviderToken=await Ke(e,t.securityProvider,r),t.securityInteractive=await e.confirm(f.securityInteractive,!0),!t.securityInteractive)t.securityAutoFix=await e.confirm(f.securityAutoFix,!1);if(t.severityThreshold=await e.list(f.severityThreshold,ie),t.setupWorkspaces)t.hasWorkspaceSecurityChecks=await e.confirm(f.hasWorkspaceSecurityChecks,!0)}async function Tt(e,t,r,i,s=!1){if(!r){i.print(g.packageJsonNotFound);return}if(r.pastoralist=e,!s)qe(t,JSON.stringify(r,null,2)+` `);i.print(g.configSaved(t))}async function Nt(e,t,r,i,s,n=!1){let a=Ct(r,t);if(Ot(a)){if(!await i.confirm(g.existingFileWarning(t),!1)){s.print(` ${g.configNotSaved}`);return}}let d=We(e,t);if(!n)qe(a,d);s.print(g.configSaved(a))}function At(e,t){let r=[`Run ${T("pastoralist")} to check and update your dependencies`];if(e)r.push(`Run ${T("pastoralist --checkSecurity")} to scan for security vulnerabilities`);r.push("Check the documentation for advanced configuration options");let i="Pastoralist initialization complete!",s=He(i,0)+` ${U}`;t.print(Be(i,r,s))}async function Vt(e,t,r){let i=await Ge(t,!1),s=E(r);if(!(i||s?.pastoralist))return!0;return await e.confirm(g.existingConfigWarning,!1)}async function se(e={}){let t=N({file:"init/index.ts",isLogging:!0}),r=!!(e.checkSecurity||e.securityProvider),i=!!e.hasWorkspaceSecurityChecks,s=r||i;if(r)t.print(` ${M} security configuration wizard `);else if(i)t.print(` ${M} workspace configuration wizard `);else t.print(` ${M} initialization wizard `),t.print(`${g.welcome} `),t.print(`${g.skipInfo} `);let n=e.path||"package.json",a=e.root||process.cwd();await Ve(async(c)=>{if(!await Vt(c,a,n)){t.print(` ${g.initCancelled}`);return}let u=E(n),o={configLocation:"package.json",setupWorkspaces:!1,setupSecurity:!1};if(r){if(o.setupSecurity=!0,e.securityProvider)o.securityProvider=e.securityProvider}if(i)o.setupWorkspaces=!0,o.hasWorkspaceSecurityChecks=!0;if(!s)await Me(c,o,t),await Ue(c,o,u,t),await It(c,o,t);else{if(await Me(c,o,t),i)await Ue(c,o,u,t);if(r)await Et(c,o,t)}let l=Le(o);t.print(` ${g.savingConfig} `);let k=o.configLocation==="package.json",p=o.configLocation==="external"&&o.configFormat;if(k)await Tt(l,n,u,t,e.isTesting);if(p)await Nt(l,o.configFormat,a,c,t,e.isTesting);At(o.setupSecurity,t)})}async function Et(e,t,r){if(r.print(` ${$e.security}`),!t.securityProvider)t.securityProvider=await e.list(f.securityProvider,re);if(t.securityProviderToken=await Ke(e,t.securityProvider,r),t.securityInteractive=await e.confirm(f.securityInteractive,!0),!t.securityInteractive)t.securityAutoFix=await e.confirm(f.securityAutoFix,!1);if(t.severityThreshold=await e.list(f.severityThreshold,ie),t.setupWorkspaces&&!t.hasWorkspaceSecurityChecks)t.hasWorkspaceSecurityChecks=await e.confirm(f.hasWorkspaceSecurityChecks,!0)}import*as B from"fs";import{dirname as wt,resolve as ot}from"path";var Ft=N({file:"program.ts",isLogging:!1}),Dt=(e,t,r={readFileSync:B.readFileSync,writeFileSync:B.writeFileSync,resolve:ot})=>{if(e.setupHook!==!0)return!1;let s=r.resolve(e.path||"package.json");try{let n=r.readFileSync(s,"utf8"),a=JSON.parse(n),c=a.scripts||{},d=c.postinstall||"";if(d.includes("pastoralist"))return t.print("postinstall hook already configured"),!0;let o=d?`${d} && pastoralist`:"pastoralist",l={...a,scripts:{...c,postinstall:o}},k=JSON.stringify(l,null,2)+` `;return r.writeFileSync(s,k),t.print("added postinstall hook to package.json"),!0}catch(n){return t.error("Failed to setup hook","handleSetupHook",n),!1}},$t=(e,t,r)=>{if(e)return t.debug("action:options:","action",{options:r}),!0;return!1},jt=async(e,t,r,i={initCommand:se})=>{if(e)return await i.initCommand({path:t.path,root:t.root,checkSecurity:r.checkSecurity,securityProvider:r.securityProvider,hasWorkspaceSecurityChecks:r.hasWorkspaceSecurityChecks}),!0;return!1},Lt=(e)=>{if(e===void 0)return;let r=typeof e==="number",i=typeof e==="string"&&e.trim()!=="",s=Number.NaN;if(r)s=e;else if(i)s=Number(e);if(!(Number.isFinite(s)&&s>=0))throw Error("--cache-ttl must be a non-negative number of seconds");return s},Wt=(e,t,r,i)=>{let n=e.securityProvider??i??"osv",a=e.cacheTtl??t.cacheTtl,c=Lt(a);return{...t,checkSecurity:e.checkSecurity??r.enabled,forceSecurityRefactor:e.forceSecurityRefactor??r.autoFix,securityProvider:n,securityProviderToken:e.securityProviderToken??r.securityProviderToken,interactive:e.interactive??r.interactive,hasWorkspaceSecurityChecks:e.hasWorkspaceSecurityChecks??r.hasWorkspaceSecurityChecks,strict:e.strict??r.strict,cacheTtl:c}},Mt=(e)=>{let r=[["cves",e.cves?.length?e.cves:void 0],["severity",e.severity],["description",e.description],["url",e.url],["vulnerableRange",e.vulnerableRange],["patchedVersion",e.patchedVersion]].filter(([,i])=>i!==void 0).reduce((i,[s,n])=>({...i,[s]:n}),{});return{packageName:e.packageName,reason:e.reason,...r}},Ut=async(e,t,r,i,s={createSpinner:ce,SecurityChecker:nt,determineSecurityScanPaths:_t,green:T,yellow:Qe})=>{let n=s.createSpinner(Ye).start();try{let a=new s.SecurityChecker({provider:t.securityProvider,forceRefactor:t.forceSecurityRefactor,interactive:t.interactive,token:t.securityProviderToken,debug:r,strict:t.strict,cacheDir:t.cacheDir,cacheTtl:t.cacheTtl,noCache:t.noCache,refreshCache:t.refreshCache}),c=s.determineSecurityScanPaths(e,t,i),d=(p)=>{n.update(p.message)},{alerts:u,overrides:o,updates:l,packagesScanned:k}=await a.checkSecurity(e,{...t,depPaths:c,root:t.root||"./",onProgress:d,severityThreshold:e?.pastoralist?.security?.severityThreshold,excludePackages:e?.pastoralist?.security?.excludePackages});return{spinner:n,securityChecker:a,alerts:u,securityOverrides:o,updates:l,packagesScanned:k}}catch(a){if(a instanceof st){n.warn(`${s.yellow("pastoralist")} ${a.message}`);let u=new s.SecurityChecker({provider:t.securityProvider,forceRefactor:t.forceSecurityRefactor,interactive:t.interactive,token:t.securityProviderToken,debug:r,strict:t.strict,cacheTtl:t.cacheTtl});return{spinner:n,securityChecker:u,alerts:[],securityOverrides:[],updates:[],packagesScanned:0,skipped:!0}}let d=a instanceof Error?a.message:String(a);throw n.fail(`${s.yellow("pastoralist")} security check failed: ${d}`),a}},Ht=(e,t,r,i,s,n=[])=>{let a=e.length>0,c=n.length>0,d=s.forceSecurityRefactor||s.interactive,u=a&&d,o=c&&d,l=o?n.map((p)=>({packageName:p.packageName,fromVersion:p.currentOverride,toVersion:p.newerVersion,reason:p.reason,severity:"medium"})):[],k=[...t,...l];if(u||o){let p=r.generatePackageOverrides(k),m=k.filter((A)=>{let C=p[A.packageName];return typeof C==="string"&&C===A.toVersion}),O=m.map(Mt);if((u||o)&&m.length>0&&!s.dryRun)r.applyAutoFix(m,s.path);return i.stop(),{securityOverrides:p,securityOverrideDetails:O}}return i.stop(),{}},Xr=(e)=>{let t=` Security Override Updates `+"=".repeat(50)+` `,r=`Found ${e.length} existing override(s) with newer patches available: `,i=e.map((s)=>`[UPDATE] ${s.packageName} Current override: ${s.currentOverride} Newer patch: ${s.newerVersion} ${s.reason} `).join("");return t+r+i};function _t(e,t,r=Ft){let i=e?.pastoralist?.depPaths,s=Array.isArray(i),n=e?.workspaces||[],a=n.length>0,c=i==="workspace"||i==="workspaces",d=t.hasWorkspaceSecurityChecks||!1,u=t.checkSecurity||e?.pastoralist?.checkSecurity||!1,o=s&&u,p=c&&a&&u||d&&a;if(o)return r.debug(`Using depPaths configuration for security checks: ${i.join(", ")}`,"determineSecurityScanPaths"),i;if(p)return r.debug(`Using workspace configuration for security checks: ${n.join(", ")}`,"determineSecurityScanPaths"),n.map((m)=>`${m}/package.json`);return[]}var at=()=>({success:!0,hasSecurityIssues:!1,hasUnusedOverrides:!1,updated:!1,securityAlertCount:0,unusedOverrideCount:0,overrideCount:0,errors:[],securityAlerts:[],unusedOverrides:[],appliedOverrides:{}}),Bt=(e)=>{let t=e instanceof Error?e.message:String(e);return{...at(),success:!1,errors:[t]}},Gt=(e)=>({hasSecurityIssues:e.length>0,securityAlertCount:e.length,securityAlerts:e.map((t)=>({packageName:t.packageName,severity:t.severity||"unknown",cves:t.cves,description:t.description,patchedVersion:t.patchedVersion,fixAvailable:t.fixAvailable}))}),qt=(e,t,r)=>{let i=e.finalOverrides||{},s=e.finalAppendix||{},n=Object.keys(i),a=Object.fromEntries(n.filter((o)=>typeof i[o]==="string").map((o)=>[o,i[o]])),c=t?.pastoralist?.appendix||{},d=t?.overrides||t?.resolutions||t?.pnpm?.overrides||{},u=JSON.stringify(s)!==JSON.stringify(c)||JSON.stringify(i)!==JSON.stringify(d);return{overrideCount:n.length,appliedOverrides:a,updated:u&&!r}},_=(e,t)=>{if(t)console.log(JSON.stringify(e))},Kt=(e,t,r)=>({packageName:e,version:t,reason:r?.ledger?.reason,dependents:r?.dependents,patches:r?.patches,isSecurityFix:r?.ledger?.securityChecked,cves:r?.ledger?.cves,keep:r?.ledger?.keep,potentiallyFixedIn:r?.ledger?.potentiallyFixedIn}),Jt=(e,t)=>{let r=t.finalOverrides[e];if(typeof r!=="string")return null;return{pkg:e,version:r}},zt=(e,t)=>{let r=`${e.pkg}@${e.version}`,i=t.finalAppendix[r];return Kt(e.pkg,e.version,i)},Yt=(e,t)=>{Object.keys(t.finalOverrides).map((r)=>Jt(r,t)).filter((r)=>r!==null).map((r)=>zt(r,t)).forEach((r)=>e.override(r,!1))},Qt=[{label:"Packages scanned",key:"total"},{label:"Appendix entries updated",key:"appendixEntriesUpdated"},{label:"Vulnerabilities blocked",key:"vulnerabilitiesBlocked"},{label:"Overrides added",key:"overridesAdded"},{label:"Overrides removed",key:"overridesRemoved"},{label:"By severity:",key:"severityHeader"},{label:" Critical",key:"severityCritical"},{label:" High",key:"severityHigh"},{label:" Medium",key:"severityMedium"},{label:" Low",key:"severityLow"},{label:"Write status",key:"writeStatus"}],Xt=(e,t)=>{if(t==="total")return e.packagesScanned;if(t==="severityHeader")return"";if(t==="writeStatus")return e.writeSuccess?"Success":"Skipped";let r=e[t];if(typeof r==="boolean")return r?1:0;return r},Zt=(e,t,r)=>{let s=(typeof t==="number"?t:0)>0;if(e==="severityCritical"&&s)return"red";if(e==="severityHigh"&&s)return"red";if(e==="severityMedium"&&s)return"yellow";if(e==="severityLow"&&s)return"gray";if(e==="vulnerabilitiesBlocked"&&s)return"green";if(e==="overridesAdded"&&s)return"cyan";if(e==="writeStatus")return r.writeSuccess?"green":"yellow";return},er=(e)=>{let t=e.metrics;if(!t)return;let r=Qt.map((s)=>{let n=Xt(t,s.key),a=Zt(s.key,n,t);return{label:s.label,value:n,color:a}}),i=Xe(r,{title:`${U} Pastoralist Summary`});console.log(` `+i)},tr=async(e,t,r)=>{let i=e.pastoralist?.appendix||{},s=ue(i);if(s.length===0)return[];let n={...e.dependencies,...e.devDependencies,...e.peerDependencies},a=oe(s),c=Object.fromEntries(a.filter((l)=>Boolean(n[l])).map((l)=>[l,n[l]]));if(Object.keys(c).length===0)return[];let d={name:e.name,version:e.version,dependencies:c},{alerts:u}=await t.checkSecurity(d,{root:r.root||"./"}),o=new Set(u.map((l)=>l.packageName));return s.filter((l)=>{let[k]=oe([l]);return o.has(k)})},ne=(e)=>e===1?"":"s";async function rr(e={},t={createLogger:N,handleTestMode:$t,handleInitMode:jt,resolveJSON:E,buildMergedOptions:Wt,runSecurityCheck:Ut,handleSecurityResults:Ht,createSpinner:ce,green:T,update:it,createTerminalGraph:Ze,getOverrideGitDate:rt,loadConfig:ae,processExit:(r)=>process.exit(r)}){let r=Je||e.debug,i=e.outputFormat==="json",s=e.quiet===!0,n=t.createLogger({file:"program.ts",isLogging:r}),a=et({cacheDir:e.cacheDir,root:e.root});tt(a);let{isTestingCLI:c=!1,init:d=!1,...u}=e,o=t.createTerminalGraph({quiet:s}),l=at();if(t.handleTestMode(c,n,e))return _(l,i),l;if(await t.handleInitMode(d,e,u))return _(l,i),l;if(!i&&!s)o.banner();try{let p=e.path||"package.json",m=e.root&&!p.startsWith("/")?`${e.root}/${p}`:p,O=t.resolveJSON(m);if(!O)throw Error(`Unable to load package.json at ${m}`);let le=e.root||wt(ot(m)),A=await(t.loadConfig??ae)(le,O.pastoralist),C={...O,...A&&{pastoralist:A}},G=C.pastoralist??{},P=G.security,w={enabled:P?.enabled??G.checkSecurity,provider:P?.provider,autoFix:P?.autoFix,interactive:P?.interactive,securityProviderToken:P?.securityProviderToken,severityThreshold:P?.severityThreshold,excludePackages:P?.excludePackages,hasWorkspaceSecurityChecks:P?.hasWorkspaceSecurityChecks,strict:P?.strict,preferLatest:P?.preferLatest},ct=Array.isArray(w.provider)?w.provider[0]:w.provider,y={...t.buildMergedOptions(e,u,w,ct),config:C,path:m,...e.root&&{root:e.root}},q={hasSecurityIssues:!1,securityAlertCount:0,securityAlerts:[]},K=0,de=y.checkSecurity;if(de&&!i)o.startPhase("scanning","Scanning packages");if(de){let{spinner:I,securityChecker:V,alerts:R,securityOverrides:b,updates:pe,packagesScanned:z,skipped:D}=await t.runSecurityCheck(C,y,Boolean(r),n);if(K=z,q=Gt(R),y={...y,securityAlerts:R},y.removeUnused&&C){let S=await tr(C,V,y);if(S.length>0)y={...y,skipRemovalKeys:S}}if(!D){let S=t.handleSecurityResults(R,b,V,I,y,pe);y={...y,...S}}if(!D&&!i){let S=(v)=>({severity:v.severity||"unknown",packageName:v.packageName,currentVersion:v.currentVersion||"?",title:v.title||v.description||"Vulnerability",cves:v.cves,fixAvailable:v.fixAvailable,patchedVersion:v.patchedVersion,url:v.url});R.map(S).forEach((v)=>o.vulnerability(v,!1));let $=R.length,Y=$>0?`${$} vulnerabilit${$===1?"y":"ies"} found`:`No vulnerabilities in ${K} packages`;if(o.endPhase(Y),b.length>0&&(y.forceSecurityRefactor||y.interactive)){o.startPhase("resolving","Fixes applied");let v=(h)=>({packageName:h.packageName,fromVersion:h.fromVersion||"?",toVersion:h.toVersion,cves:h.cves,severity:h.severity,reason:h.reason});b.map(v).forEach((h)=>o.securityFix(h,!1));let L=b.length,he=L===1?"":"s";o.endPhase(`${L} override${he} added`)}}}let ut=await t.getOverrideGitDate(m);y={...y,addedDate:ut};let x=t.update(y),J=qt(x,C,e.dryRun||!1);if(!i){let I=x.metrics?.removedOverridePackages??[],V=I.length>0,R=!V;o.startPhase("writing","Updating overrides",R),Yt(o,{finalOverrides:x.finalOverrides??{},finalAppendix:x.finalAppendix??{}});let b=J.overrideCount,z=b>0?`${b} override${ne(b)} applied`:"No overrides to update";if(o.endPhase(z),V){o.startPhase("writing","Cleaned up stale overrides",!0),I.forEach((ge)=>{o.removedOverride({packageName:ge.packageName,version:ge.version,reason:"Override no longer needed"},!1)});let h=I.length,dt=h===1?"":"s";o.endPhase(`${h} stale override${dt} removed`)}let D=q.securityAlertCount||0,fe=I.length,ye=K;o.executiveSummary({vulnerabilitiesFixed:D,staleOverridesRemoved:fe,packagesProtected:ye});let S=x.metrics;if(o.compactSummary({severityCritical:S?.severityCritical??0,severityHigh:S?.severityHigh??0,severityMedium:S?.severityMedium??0,severityLow:S?.severityLow??0,overridesTracked:S?.appendixEntriesUpdated??0,overridesRemoved:S?.overridesRemoved??0,packagesScanned:S?.packagesScanned??0}),o.complete("The herd is safe!",` ${ze}`),J.updated)o.notice("Run an install to capture the updates!");let j=y.skipRemovalKeys||[];if(j.length>0){let h=j.length;o.notice(`${h} override${ne(h)} kept: still vulnerable at declared versions - ${j.join(", ")}`)}let Y=x.finalAppendix??{},Q=ue(Y),v=Q.length>0,L=!e.removeUnused;if(v&&L){let h=Q.length;o.notice(`${h} unused override${ne(h)} detected. Run with --remove-unused to clean up.`)}}let F=Object.assign({},l,q,J,{metrics:x.metrics});if(e.summary&&!i)er(F);if(_(F,i),s&&F.hasSecurityIssues)t.processExit(1);return F}catch(p){o.stop();let m=Bt(p);if(i)_(m,i);else n.error("action:fn","action",{error:p});return t.processExit(1),m}}var Zr=async(e=process.argv)=>{let t=Se(e),r=t.options;if(r.help||e.includes("-h")||e.includes("--help")){Pe();return}let s=N({file:"program.ts",isLogging:!1});if(Dt(r,s))return;if(t.command==="init"){await se(r);return}await rr(r)};export{Ut as runSecurityCheck,Zr as run,_ as outputResult,Lt as normalizeCacheTtl,$t as handleTestMode,Dt as handleSetupHook,Ht as handleSecurityResults,jt as handleInitMode,Xr as formatUpdateReport,er as displaySummaryTable,Yt as displayOverrides,_t as determineSecurityScanPaths,Bt as createErrorResult,at as createEmptyResult,tr as checkRemovalSafety,qt as buildUpdateResult,Gt as buildSecurityResult,Mt as buildSecurityOverrideDetail,Wt as buildMergedOptions,rr as action};