Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ const yarnInstallLikeCommands = new Set([
*
* SECURITY: Uses array-based arguments to prevent command injection. All elements
* in the args array are properly escaped by Node.js when passed to spawn().
*
* @example
* ```typescript
* await execNpm(['install', '--save', 'lodash'])
* await execNpm(['run', 'build'], { cwd: '/tmp/project' })
* ```
*/
export function execNpm(args: string[], options?: SpawnOptions | undefined) {
const useDebug = isDebug()
Expand Down Expand Up @@ -157,6 +163,12 @@ export interface PnpmOptions extends SpawnOptions {
*
* SECURITY: Uses array-based arguments to prevent command injection. All elements
* in the args array are properly escaped by Node.js when passed to execBin().
*
* @example
* ```typescript
* await execPnpm(['install'])
* await execPnpm(['add', 'lodash'], { allowLockfileUpdate: true })
* ```
*/
export function execPnpm(args: string[], options?: PnpmOptions | undefined) {
const { allowLockfileUpdate, ...extBinOpts } = {
Expand Down Expand Up @@ -224,6 +236,12 @@ export function execPnpm(args: string[], options?: PnpmOptions | undefined) {
*
* SECURITY: Uses array-based arguments to prevent command injection. All elements
* in the args array are properly escaped by Node.js when passed to execBin().
*
* @example
* ```typescript
* await execYarn(['install'])
* await execYarn(['add', 'lodash'], { cwd: '/tmp/project' })
* ```
*/
export function execYarn(
args: string[],
Expand Down Expand Up @@ -271,6 +289,13 @@ export function execYarn(

/**
* Check if a command argument is an npm audit flag.
*
* @example
* ```typescript
* isNpmAuditFlag('--no-audit') // true
* isNpmAuditFlag('--audit') // true
* isNpmAuditFlag('--save') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isNpmAuditFlag(cmdArg: string): boolean {
Expand All @@ -279,6 +304,13 @@ export function isNpmAuditFlag(cmdArg: string): boolean {

/**
* Check if a command argument is an npm fund flag.
*
* @example
* ```typescript
* isNpmFundFlag('--no-fund') // true
* isNpmFundFlag('--fund') // true
* isNpmFundFlag('--save') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isNpmFundFlag(cmdArg: string): boolean {
Expand All @@ -287,6 +319,14 @@ export function isNpmFundFlag(cmdArg: string): boolean {

/**
* Check if a command argument is an npm loglevel flag.
*
* @example
* ```typescript
* isNpmLoglevelFlag('--loglevel') // true
* isNpmLoglevelFlag('--silent') // true
* isNpmLoglevelFlag('-s') // true
* isNpmLoglevelFlag('--save') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isNpmLoglevelFlag(cmdArg: string): boolean {
Expand All @@ -304,6 +344,13 @@ export function isNpmLoglevelFlag(cmdArg: string): boolean {

/**
* Check if a command argument is an npm node-options flag.
*
* @example
* ```typescript
* isNpmNodeOptionsFlag('--node-options') // true
* isNpmNodeOptionsFlag('--node-options=--max-old-space-size=4096') // true
* isNpmNodeOptionsFlag('--save') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isNpmNodeOptionsFlag(cmdArg: string): boolean {
Expand All @@ -313,6 +360,13 @@ export function isNpmNodeOptionsFlag(cmdArg: string): boolean {

/**
* Check if a command argument is an npm progress flag.
*
* @example
* ```typescript
* isNpmProgressFlag('--no-progress') // true
* isNpmProgressFlag('--progress') // true
* isNpmProgressFlag('--save') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isNpmProgressFlag(cmdArg: string): boolean {
Expand All @@ -321,6 +375,13 @@ export function isNpmProgressFlag(cmdArg: string): boolean {

/**
* Check if a command argument is a pnpm ignore-scripts flag.
*
* @example
* ```typescript
* isPnpmIgnoreScriptsFlag('--ignore-scripts') // true
* isPnpmIgnoreScriptsFlag('--no-ignore-scripts') // true
* isPnpmIgnoreScriptsFlag('--save') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isPnpmIgnoreScriptsFlag(cmdArg: string): boolean {
Expand All @@ -329,6 +390,13 @@ export function isPnpmIgnoreScriptsFlag(cmdArg: string): boolean {

/**
* Check if a command argument is a pnpm frozen-lockfile flag.
*
* @example
* ```typescript
* isPnpmFrozenLockfileFlag('--frozen-lockfile') // true
* isPnpmFrozenLockfileFlag('--no-frozen-lockfile') // true
* isPnpmFrozenLockfileFlag('--save') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isPnpmFrozenLockfileFlag(cmdArg: string): boolean {
Expand All @@ -337,6 +405,13 @@ export function isPnpmFrozenLockfileFlag(cmdArg: string): boolean {

/**
* Check if a command argument is a pnpm install command.
*
* @example
* ```typescript
* isPnpmInstallCommand('install') // true
* isPnpmInstallCommand('i') // true
* isPnpmInstallCommand('run') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isPnpmInstallCommand(cmdArg: string): boolean {
Expand All @@ -351,6 +426,12 @@ export const isPnpmLoglevelFlag = isNpmLoglevelFlag
/**
* Execute a package.json script using the appropriate package manager.
* Automatically detects pnpm, yarn, or npm based on lockfiles.
*
* @example
* ```typescript
* await execScript('build')
* await execScript('test', ['--coverage'], { cwd: '/tmp/project' })
* ```
*/
export interface ExecScriptOptions extends SpawnOptions {
prepost?: boolean | undefined
Expand Down
31 changes: 31 additions & 0 deletions src/archives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ function validatePathWithinBase(
*
* @param filePath - Path to archive file
* @returns Archive format or null if unknown
*
* @example
* ```typescript
* detectArchiveFormat('package.tar.gz') // 'tar.gz'
* detectArchiveFormat('archive.zip') // 'zip'
* detectArchiveFormat('data.csv') // null
* ```
*/
export function detectArchiveFormat(filePath: string): ArchiveFormat | null {
const lower = filePath.toLowerCase()
Expand All @@ -138,6 +145,12 @@ export function detectArchiveFormat(filePath: string): ArchiveFormat | null {
* @param archivePath - Path to tar file
* @param outputDir - Directory to extract to
* @param options - Extraction options
*
* @example
* ```typescript
* await extractTar('/tmp/archive.tar', '/tmp/output')
* await extractTar('/tmp/archive.tar', '/tmp/output', { strip: 1 })
* ```
*/
export async function extractTar(
archivePath: string,
Expand Down Expand Up @@ -264,6 +277,12 @@ export async function extractTar(
* @param archivePath - Path to tar.gz or tgz file
* @param outputDir - Directory to extract to
* @param options - Extraction options
*
* @example
* ```typescript
* await extractTarGz('/tmp/archive.tar.gz', '/tmp/output')
* await extractTarGz('/tmp/archive.tgz', '/tmp/output', { strip: 1 })
* ```
*/
export async function extractTarGz(
archivePath: string,
Expand Down Expand Up @@ -390,6 +409,12 @@ export async function extractTarGz(
* @param archivePath - Path to zip file
* @param outputDir - Directory to extract to
* @param options - Extraction options
*
* @example
* ```typescript
* await extractZip('/tmp/archive.zip', '/tmp/output')
* await extractZip('/tmp/archive.zip', '/tmp/output', { strip: 1 })
* ```
*/
export async function extractZip(
archivePath: string,
Expand Down Expand Up @@ -530,6 +555,12 @@ export async function extractZip(
* @param outputDir - Directory to extract to
* @param options - Extraction options
* @throws Error if archive format is not supported
*
* @example
* ```typescript
* await extractArchive('/tmp/package.tar.gz', '/tmp/output')
* await extractArchive('/tmp/release.zip', '/tmp/output', { strip: 1 })
* ```
*/
export async function extractArchive(
archivePath: string,
Expand Down
Loading