{"version":3,"sources":["/home/x/core/projects/battle/dist/chunk-6R5FTRMQ.cjs","../src/index.ts","../src/Runner/index.ts","../src/Runner/constructor.ts","../src/Runner/addSuite.ts","../src/Runner/addTest.ts","../src/Runner/run.ts","../src/Runner/report.ts","../src/Silent/index.ts","../src/types/index.ts"],"names":["test","fs","Battle"],"mappings":"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","file":"/home/x/core/projects/battle/dist/chunk-6R5FTRMQ.cjs","sourcesContent":[null,"/**\n * Battle - Universal Terminal Testing Framework\n * Test any terminal application with real PTY emulation\n */\n\nexport { Battle } from './Battle/index.js'\nexport { Runner } from './Runner/index.js'\nexport { Silent } from './Silent/index.js'\nexport { Replay } from './Replay/index.js'\nexport * from './types/index.js'\n\n// Export security utilities\nexport * as Security from './security/index.js'\nexport { SecurityLevel, SafeCommandMode } from './security/SafeCommandMode.js'\nexport { CircularBuffer, TerminalOutputBuffer } from './utils/CircularBuffer.js'\nexport { ResourceManager, PTYLifecycleManager, EventListenerTracker } from './utils/ResourceManager.js'\n\n// Quick test function\nexport async function test(\n    name: string,\n    command: string,\n    interactions?: Array<{ expect: string | RegExp; respond: string }>,\n    expectations?: Array<string | RegExp>\n) {\n    const { Battle } = await import('./Battle/index.js')\n    const battle = new Battle({ verbose: false })\n    \n    const result = await battle.run(async (b) => {\n        b.spawn(command)\n        \n        if (interactions) {\n            for (const interaction of interactions) {\n                await b.interact(async (data, output) => {\n                    const clean = output.replace(/\\x1b\\[[0-9;]*[mGKJH]/g, '')\n                    \n                    if (typeof interaction.expect === 'string') {\n                        if (clean.includes(interaction.expect)) {\n                            return interaction.respond\n                        }\n                    } else if (interaction.expect.test(clean)) {\n                        return interaction.respond\n                    }\n                    \n                    return null\n                })\n            }\n        }\n        \n        if (expectations) {\n            for (const expectation of expectations) {\n                b.expect(expectation)\n            }\n        }\n    })\n    \n    return result\n}","/**\n * Battle Test Runner\n * Executes test suites and reports results\n */\n\nimport { constructor } from './constructor.js'\nimport { addSuite } from './addSuite.js'\nimport { addTest } from './addTest.js'\nimport { run } from './run.js'\nimport { report } from './report.js'\nimport type { TestSuite, TestCase } from '../types/index.js'\n\nexport class Runner {\n    suites!: TestSuite[]\n    results!: any[]\n    options!: any\n    \n    constructor(options: any = {}) {\n        constructor.call(this, options)\n    }\n    \n    suite(name: string, tests: TestCase[]) {\n        return addSuite.call(this, name, tests)\n    }\n    \n    test(name: string, testCase: TestCase) {\n        return addTest.call(this, name, testCase)\n    }\n    \n    async run() {\n        return run.call(this)\n    }\n    \n    report() {\n        return report.call(this)\n    }\n}\n\nexport default Runner","export function constructor(this: any, options: any = {}) {\n    this.suites = []\n    this.results = []\n    this.options = {\n        parallel: false,\n        verbose: false,\n        bail: false,\n        ...options\n    }\n}","import type { TestSuite, TestCase } from '../types/index.js'\n\nexport function addSuite(this: any, name: string, tests: TestCase[]): void {\n    const suite: TestSuite = {\n        name,\n        tests\n    }\n    this.suites.push(suite)\n}","import type { TestCase } from '../types/index.js'\n\nexport function addTest(this: any, name: string, testCase: TestCase): void {\n    // Add to default suite\n    if (this.suites.length === 0) {\n        this.suites.push({\n            name: 'Default',\n            tests: []\n        })\n    }\n    \n    this.suites[this.suites.length - 1].tests.push({\n        ...testCase,\n        name\n    })\n}","import { Battle } from '../Battle/index.js'\nimport { color as chalk } from '../utils/colors.js'\n\nexport async function run(this: any): Promise<{ total: number; passed: number; failed: number }> {\n    console.log(chalk.blue('\\nBattle Test Runner\\n'))\n    \n    let totalTests = 0\n    let passedTests = 0\n    let failedTests = 0\n    \n    for (const suite of this.suites) {\n        console.log(chalk.yellow(`\\nSuite: ${suite.name}`))\n        \n        if (suite.beforeAll) await suite.beforeAll()\n        \n        for (const test of suite.tests) {\n            totalTests++\n            \n            if (suite.beforeEach) await suite.beforeEach()\n            \n            const battle = new Battle({\n                ...this.options,\n                timeout: test.timeout || 10000\n            })\n            \n            try {\n                // Spawn the process\n                await battle.spawn(test.command, test.args)\n                \n                // Wait for process to start\n                await battle.wait(200)\n                \n                // Handle interactions\n                if (test.interactions) {\n                    for (const interaction of test.interactions) {\n                        // Give time for prompt to appear\n                        await battle.wait(100)\n                        \n                        await battle.interact(async (data, output) => {\n                            const clean = output.replace(/\\x1b\\[[0-9;]*[mGKJH]/g, '')\n                            \n                            if (typeof interaction.expect === 'string') {\n                                if (clean.includes(interaction.expect)) {\n                                    return interaction.respond\n                                }\n                            } else if (interaction.expect.test(clean)) {\n                                return interaction.respond\n                            }\n                            \n                            return null\n                        })\n                    }\n                }\n                \n                // Check expectations\n                if (test.expectations) {\n                    for (const expectation of test.expectations) {\n                        await battle.expect(expectation)\n                    }\n                }\n                \n                console.log(chalk.green(`  PASS ${test.name}`))\n                passedTests++\n                \n            } catch (error: any) {\n                console.log(chalk.red(`  FAIL ${test.name}`))\n                console.log(chalk.gray(`    ${error.message}`))\n                failedTests++\n                \n                if (this.options.bail) {\n                    break\n                }\n            } finally {\n                battle.cleanup()\n            }\n            \n            if (suite.afterEach) await suite.afterEach()\n        }\n        \n        if (suite.afterAll) await suite.afterAll()\n    }\n    \n    // Summary\n    console.log(chalk.blue('\\nResults:'))\n    console.log(`  Total: ${totalTests}`)\n    console.log(chalk.green(`  Passed: ${passedTests}`))\n    if (failedTests > 0) {\n        console.log(chalk.red(`  Failed: ${failedTests}`))\n    }\n    \n    return {\n        total: totalTests,\n        passed: passedTests,\n        failed: failedTests\n    }\n}","import fs from 'fs'\nimport path from 'path'\n\nexport function report(this: any): void {\n    const reportPath = path.join(this.options.logDir || './logs', 'battle-report.json')\n    \n    const report = {\n        timestamp: new Date().toISOString(),\n        suites: this.suites,\n        results: this.results,\n        summary: {\n            total: this.results.length,\n            passed: this.results.filter((r: any) => r.success).length,\n            failed: this.results.filter((r: any) => !r.success).length\n        }\n    }\n    \n    fs.mkdirSync(path.dirname(reportPath), { recursive: true })\n    fs.writeFileSync(reportPath, JSON.stringify(report, null, 2))\n    \n    console.log(`Report saved to: ${reportPath}`)\n}","/**\n * Silent - Testing for non-interactive/system apps\n * Tests commands that don't need PTY emulation\n */\n\nimport { execSync, spawn } from 'child_process'\nimport fs from 'fs'\nimport path from 'path'\nimport { CommandSanitizer, SecureErrorHandler, PathSecurity } from '../security/index.js'\n\nexport class Silent {\n    private logs: string[] = []\n    private startTime = Date.now()\n    \n    /**\n     * Run a command and capture output\n     * WARNING: This is inherently unsafe - use Battle.spawn() for secure command execution\n     */\n    exec(command: string, options: any = {}): {\n        success: boolean\n        stdout: string\n        stderr: string\n        exitCode: number | null\n    } {\n        // Security warning\n        this.log('warn', 'Silent.exec() bypasses PTY security - use Battle.spawn() for secure execution')\n        \n        // Basic validation to prevent obvious attacks\n        const validation = CommandSanitizer.validate(command)\n        if (!validation.valid) {\n            this.log('error', `Security validation failed: ${validation.error}`)\n            return {\n                success: false,\n                stdout: '',\n                stderr: `Security error: ${validation.error}`,\n                exitCode: 1\n            }\n        }\n        \n        try {\n            const stdout = execSync(command, {\n                encoding: 'utf8',\n                timeout: 30000, // 30 second timeout\n                maxBuffer: 1024 * 1024, // 1MB max buffer\n                ...options\n            })\n            \n            this.log('info', `Executed: ${command.slice(0, 100)}${command.length > 100 ? '...' : ''}`)\n            this.log('output', stdout.slice(0, 500) + (stdout.length > 500 ? '...' : ''))\n            \n            return {\n                success: true,\n                stdout,\n                stderr: '',\n                exitCode: 0\n            }\n        } catch (error: any) {\n            const sanitizedError = SecureErrorHandler.sanitize(error)\n            this.log('error', `Failed: ${command.slice(0, 100)}${command.length > 100 ? '...' : ''}`)\n            this.log('error', sanitizedError)\n            \n            return {\n                success: false,\n                stdout: error.stdout || '',\n                stderr: error.stderr || sanitizedError,\n                exitCode: error.status || 1\n            }\n        }\n    }\n    \n    /**\n     * Check if a process is running (DEPRECATED - security risk)\n     * @deprecated Use process monitoring libraries instead of shell commands\n     */\n    isRunning(pattern: string): boolean {\n        // Validate pattern to prevent injection\n        if (!pattern || pattern.includes('`') || pattern.includes('$') || pattern.includes(';') || pattern.includes('|')) {\n            this.log('error', 'Invalid process pattern - potential security risk')\n            return false\n        }\n        \n        // Limit pattern length\n        const safePattern = pattern.replace(/[\"'\\\\]/g, '').slice(0, 50)\n        \n        try {\n            // Use ps with grep to check for running processes\n            // The grep -v grep excludes the grep process itself\n            const command = process.platform === 'win32'\n                ? `tasklist | findstr /i \"${safePattern}\"`\n                : `ps aux | grep -v grep | grep \"${safePattern}\"`;\n            \n            const result = execSync(command, {\n                encoding: 'utf8',\n                stdio: 'pipe',\n                timeout: 5000\n            })\n            \n            // Check if we got any real results\n            return result.trim().length > 0\n        } catch (error) {\n            SecureErrorHandler.log(error, 'Process check error')\n            return false\n        }\n    }\n    \n    /**\n     * Check if a port is open\n     */\n    isPortOpen(port: number, host = 'localhost'): boolean {\n        // Validate inputs\n        if (!Number.isInteger(port) || port < 1 || port > 65535) {\n            this.log('error', 'Invalid port number')\n            return false\n        }\n        \n        // Sanitize host to prevent injection\n        const safeHost = host.replace(/[^a-zA-Z0-9.-]/g, '').slice(0, 255)\n        if (!safeHost || safeHost !== host) {\n            this.log('error', 'Invalid host format')\n            return false\n        }\n        \n        try {\n            // First check if nc is available\n            try {\n                execSync('which nc', { encoding: 'utf8', stdio: 'pipe', timeout: 1000 })\n                // If nc exists, use it with safe parameters\n                execSync(`nc -z ${safeHost} ${port}`, {\n                    encoding: 'utf8',\n                    stdio: 'pipe',\n                    timeout: 5000\n                })\n                return true\n            } catch {\n                // nc not available or port closed, try alternative\n            }\n            \n            // Fallback: try using safer commands\n            const testCmd = process.platform === 'win32' \n                ? `powershell -Command \"Test-NetConnection -ComputerName ${safeHost} -Port ${port} -InformationLevel Quiet -WarningAction SilentlyContinue\"`\n                : `timeout 1 bash -c \"exec 3<>/dev/tcp/${safeHost}/${port} && echo 'open' >&3\" 2>/dev/null`\n            \n            execSync(testCmd, { encoding: 'utf8', stdio: 'pipe', timeout: 5000 })\n            return true\n        } catch (error) {\n            SecureErrorHandler.log(error, 'Port check error')\n            return false\n        }\n    }\n    \n    /**\n     * Check if a file exists\n     */\n    fileExists(filepath: string): boolean {\n        return fs.existsSync(filepath)\n    }\n    \n    /**\n     * Read file content with security validation\n     */\n    readFile(filepath: string): string {\n        // Validate file path for security\n        const pathValidation = PathSecurity.validatePath(filepath, process.cwd())\n        if (!pathValidation.valid) {\n            throw new Error(`Invalid file path: ${pathValidation.error}`)\n        }\n        \n        try {\n            const stats = fs.statSync(pathValidation.normalized!)\n            \n            // Check file size limit (10MB max)\n            if (stats.size > 10 * 1024 * 1024) {\n                throw new Error('File too large (max 10MB)')\n            }\n            \n            return fs.readFileSync(pathValidation.normalized!, 'utf8')\n        } catch (error: any) {\n            const sanitizedError = SecureErrorHandler.sanitize(error)\n            throw new Error(`Failed to read file: ${sanitizedError}`)\n        }\n    }\n    \n    /**\n     * Check system resources (DEPRECATED - security risk)\n     * @deprecated Use Node.js process.cpuUsage() and process.memoryUsage() instead\n     */\n    checkResources(): {\n        cpu: number\n        memory: number\n        disk: number\n    } {\n        this.log('warn', 'checkResources() is deprecated due to security risks - use Node.js built-in process APIs')\n        \n        try {\n            // Use safer Node.js APIs where possible\n            const memUsage = process.memoryUsage()\n            const memUsageMB = memUsage.rss / 1024 / 1024\n            \n            // Only return basic Node.js process info - avoid shell commands\n            return {\n                cpu: 0, // Cannot safely determine CPU usage without shell commands\n                memory: memUsageMB,\n                disk: 0  // Cannot safely determine disk usage without shell commands\n            }\n        } catch (error) {\n            SecureErrorHandler.log(error, 'Resource check error')\n            return {\n                cpu: 0,\n                memory: 0,\n                disk: 0\n            }\n        }\n    }\n    \n    /**\n     * Wait for condition\n     */\n    async waitFor(\n        condition: () => boolean,\n        timeout = 5000,\n        interval = 100\n    ): Promise<boolean> {\n        const start = Date.now()\n        \n        while (Date.now() - start < timeout) {\n            if (condition()) {\n                return true\n            }\n            await new Promise(resolve => setTimeout(resolve, interval))\n        }\n        \n        return false\n    }\n    \n    private log(level: string, message: string): void {\n        const timestamp = new Date().toISOString()\n        const entry = `[${timestamp}] [${level.toUpperCase()}] ${message}`\n        this.logs.push(entry)\n    }\n    \n    getLogs(): string[] {\n        return this.logs\n    }\n}\n\nexport default Silent","import { SecurityLevel } from '../security/SafeCommandMode.js'\n\nexport interface BattleOptions {\n    command?: string\n    args?: string[]\n    cols?: number\n    rows?: number\n    cwd?: string\n    env?: NodeJS.ProcessEnv\n    timeout?: number\n    screenshotDir?: string\n    logDir?: string\n    verbose?: boolean\n    // Security options\n    securityLevel?: SecurityLevel\n    safeCommandMode?: boolean\n}\n\nexport interface TestResult {\n    success: boolean\n    duration: number\n    output: string\n    screenshots: string[]\n    logs: string[]\n    error: string | null\n    replayPath?: string\n}\n\nexport type InteractionHandler = (\n    data: string,\n    fullOutput: string\n) => Promise<string | null> | string | null\n\nexport interface TestCase {\n    name: string\n    command: string\n    args?: string[]\n    interactions?: Array<{\n        expect: string | RegExp\n        respond: string\n    }>\n    expectations?: Array<string | RegExp>\n    timeout?: number\n}\n\nexport interface TestSuite {\n    name: string\n    tests: TestCase[]\n    beforeAll?: () => Promise<void> | void\n    afterAll?: () => Promise<void> | void\n    beforeEach?: () => Promise<void> | void\n    afterEach?: () => Promise<void> | void\n}\n\nexport interface ReplayEvent {\n    type: 'spawn' | 'output' | 'input' | 'resize' | 'key' | 'screenshot' | 'expect' | 'exit'\n    timestamp: number\n    data: any\n}\n\nexport interface ReplayData {\n    version: string\n    timestamp: string\n    duration: number\n    events: ReplayEvent[]\n    metadata: {\n        cols: number\n        rows: number\n        command: string\n        args: string[]\n        env: Record<string, string>\n    }\n}"]}