@pact-toolbox/node-utils
Version:
Essential Node.js utilities for building, testing, and managing applications in the Pact Toolbox ecosystem. This package provides a comprehensive set of utilities for file system operations, process management, logging, UI components, and more.
1 lines • 43.9 kB
Source Map (JSON)
{"version":3,"file":"index.node.mjs","names":["logger: ConsolaInstance","info: typeof logger.info","warn: typeof logger.warn","error: typeof logger.error","debug: typeof logger.debug","success: typeof logger.success","fail: typeof logger.fail","ready: typeof logger.ready","start: typeof logger.start","log: typeof logger.log","box: typeof logger.box","tag: string","operation: string","duration: number","data?: any","level: \"info\" | \"warn\" | \"error\" | \"debug\"","context: string","message: string","cleanupFn: CleanupFunction","signal: NodeJS.Signals | \"exit\" | \"uncaughtException\" | \"unhandledRejection\"","signals: (NodeJS.Signals | \"exit\" | \"uncaughtException\" | \"unhandledRejection\")[]","glob: typeof readdir","watch: typeof chokidar.watch","matchPattern: typeof minimatch","dirPath: string","filePath: string","content: string","encoding: BufferEncoding","src: string","dest: string","execAsync: typeof exec.__promisify__","PACT_VERSION_REGEX: RegExp","match?: string","version?: string","nightly?: boolean","host: string","startGap: number","endGap: number","error","port: number | string","bin: string","args: string[]","options: RunBinOptions","data: Buffer","err: Error","_code: number | null","_signal: NodeJS.Signals | null","name: string","command: string","options: SimpleProcessOptions","pid: number","currentSpinner: ClackSpinner | null","text: string","text","success","text?: string","title: string","content: string | string[]","headers: string[]","rows: string[][]"],"sources":["../src/logger.ts","../src/cleanup.ts","../src/filesystem.ts","../src/helpers.ts","../src/pact.ts","../src/port.ts","../src/process.ts","../src/ui.ts","../src/index.ts"],"sourcesContent":["import { createConsola, type ConsolaInstance } from \"consola\";\n\n/**\n * Determines the log level from environment variables.\n * Supports DEBUG and LOG_LEVEL environment variables.\n * \n * @returns The numeric log level (0-5)\n */\nfunction getLogLevel(): number {\n // Check for DEBUG environment variable (1 = debug level)\n if (process.env[\"DEBUG\"] === \"1\" || process.env[\"DEBUG\"] === \"true\") {\n return 4; // debug level\n }\n\n // Check for LOG_LEVEL environment variable\n if (process.env[\"LOG_LEVEL\"]) {\n const level = process.env[\"LOG_LEVEL\"].toLowerCase();\n switch (level) {\n case \"silent\":\n return 0;\n case \"fatal\":\n return 0;\n case \"error\":\n return 1;\n case \"warn\":\n return 2;\n case \"log\":\n return 3;\n case \"info\":\n return 3;\n case \"debug\":\n return 4;\n case \"trace\":\n return 5;\n default:\n return 3; // default to info\n }\n }\n\n // Default to info level (3)\n return 3;\n}\n\n/**\n * Main logger instance configured with environment-based log level.\n * \n * Log levels:\n * - 0: silent/fatal\n * - 1: error\n * - 2: warn\n * - 3: info/log (default)\n * - 4: debug\n * - 5: trace\n * \n * Set log level via environment variables:\n * - DEBUG=1 or DEBUG=true for debug level\n * - LOG_LEVEL=debug/info/warn/error/silent\n * \n * @example\n * ```typescript\n * import { logger } from '@pact-toolbox/node-utils';\n * \n * logger.info('Application started');\n * logger.error('An error occurred', error);\n * logger.debug('Debug information', { data });\n * ```\n */\nexport const logger: ConsolaInstance = createConsola({\n level: getLogLevel(),\n formatOptions: {\n columns: 80,\n colors: true,\n compact: false,\n date: false,\n },\n});\n\n/**\n * Type alias for logger instances.\n */\nexport type Logger = ConsolaInstance;\n\nexport { LogLevels } from \"consola\";\n\n// Convenience functions for common logging patterns\n\n/** Log informational messages */\nexport const info: typeof logger.info = logger.info.bind(logger);\n\n/** Log warning messages */\nexport const warn: typeof logger.warn = logger.warn.bind(logger);\n\n/** Log error messages */\nexport const error: typeof logger.error = logger.error.bind(logger);\n\n/** Log debug messages (only shown when debug level is enabled) */\nexport const debug: typeof logger.debug = logger.debug.bind(logger);\n\n/** Log success messages with green styling */\nexport const success: typeof logger.success = logger.success.bind(logger);\n\n/** Log failure messages with red styling */\nexport const fail: typeof logger.fail = logger.fail.bind(logger);\n\n/** Log ready messages (typically for server/service startup) */\nexport const ready: typeof logger.ready = logger.ready.bind(logger);\n\n/** Log start messages (typically for process/task initiation) */\nexport const start: typeof logger.start = logger.start.bind(logger);\n\n/** Log general messages */\nexport const log: typeof logger.log = logger.log.bind(logger);\n\n/** Display a message in a box for emphasis */\nexport const box: typeof logger.box = logger.box.bind(logger);\n\n/**\n * Creates a tagged logger for a specific package or component.\n * Tagged loggers prefix all messages with the tag for easier identification.\n * \n * @param tag - The tag to prefix messages with\n * @returns A new logger instance with the specified tag\n * \n * @example\n * ```typescript\n * const networkLogger = createLogger('network');\n * networkLogger.info('Connection established'); // [network] Connection established\n * \n * const dbLogger = createLogger('database');\n * dbLogger.error('Query failed'); // [database] Query failed\n * ```\n */\nexport function createLogger(tag: string): ConsolaInstance {\n return logger.withTag(tag);\n}\n\n/**\n * Logs performance metrics for operations.\n * Only visible when debug level is enabled.\n * \n * @param operation - The name of the operation\n * @param duration - The duration in milliseconds\n * @param data - Optional additional data to log\n * \n * @example\n * ```typescript\n * const startTime = Date.now();\n * await performOperation();\n * const duration = Date.now() - startTime;\n * \n * logPerformance('database.query', duration, { query: 'SELECT * FROM users' });\n * // [PERF] database.query completed in 123ms { query: 'SELECT * FROM users' }\n * ```\n */\nexport function logPerformance(operation: string, duration: number, data?: any): void {\n logger.debug(`[PERF] ${operation} completed in ${duration}ms`, data);\n}\n\n/**\n * Logs a message with explicit context/category and level.\n * Useful for dynamic logging where the level is determined at runtime.\n * \n * @param level - The log level to use\n * @param context - The context/category tag\n * @param message - The message to log\n * @param data - Optional additional data to log\n * \n * @example\n * ```typescript\n * function handleRequest(severity: string) {\n * const level = severity === 'critical' ? 'error' : 'warn';\n * logWithContext(level, 'api', 'Request failed', { \n * endpoint: '/users',\n * status: 500 \n * });\n * }\n * ```\n */\nexport function logWithContext(\n level: \"info\" | \"warn\" | \"error\" | \"debug\",\n context: string,\n message: string,\n data?: any,\n): void {\n const contextualLogger = logger.withTag(context);\n\n switch (level) {\n case \"info\":\n contextualLogger.info(message, data);\n break;\n case \"warn\":\n contextualLogger.warn(message, data);\n break;\n case \"error\":\n contextualLogger.error(message, data);\n break;\n case \"debug\":\n contextualLogger.debug(message, data);\n break;\n }\n}\n\n// Re-export color utilities for consistent styling across the package\nexport { colors, getColor, stripAnsi, colorize } from \"consola/utils\";\n","import { logger } from \"./logger\";\n\n/**\n * A function that performs cleanup operations.\n * Can be synchronous or asynchronous.\n */\ntype CleanupFunction = () => void | Promise<void>;\n\nclass CleanupHandler {\n private cleanupFunctions: Set<CleanupFunction> = new Set();\n private cleanupRegistered = false;\n private isCleaningUp = false;\n\n registerCleanupFunction(cleanupFn: CleanupFunction) {\n this.cleanupFunctions.add(cleanupFn);\n this.registerSignalHandlers();\n }\n\n private registerSignalHandlers() {\n if (this.cleanupRegistered) return;\n this.cleanupRegistered = true;\n\n const cleanup = async (signal: NodeJS.Signals | \"exit\" | \"uncaughtException\" | \"unhandledRejection\") => {\n if (this.isCleaningUp) return; // Prevent re-entry\n this.isCleaningUp = true;\n\n logger.info(`Received ${signal}, running cleanup functions...`);\n\n for (const cleanupFn of this.cleanupFunctions) {\n try {\n await cleanupFn();\n } catch (err) {\n logger.error(\"Error during cleanup:\", err);\n }\n }\n\n process.exit(signal === \"uncaughtException\" || signal === \"unhandledRejection\" ? 1 : 0);\n };\n\n const signals: (NodeJS.Signals | \"exit\" | \"uncaughtException\" | \"unhandledRejection\")[] = [\n \"SIGINT\",\n \"SIGTERM\",\n \"SIGQUIT\",\n \"SIGHUP\",\n \"exit\",\n \"uncaughtException\",\n \"unhandledRejection\",\n ];\n\n signals.forEach((signal) => {\n process.on(signal as any, async (reasonOrExitCode) => {\n if (signal === \"exit\") {\n await cleanup(signal);\n } else if (signal === \"uncaughtException\" || signal === \"unhandledRejection\") {\n logger.error(`${signal}:`, reasonOrExitCode);\n await cleanup(signal);\n } else {\n await cleanup(signal);\n }\n });\n });\n }\n}\n\nconst cleanupHandler = new CleanupHandler();\n\n/**\n * Registers a cleanup function to be executed when the process exits.\n * \n * The cleanup function will be called on:\n * - SIGINT (Ctrl+C)\n * - SIGTERM (termination signal)\n * - SIGQUIT\n * - SIGHUP\n * - Normal process exit\n * - Uncaught exceptions\n * - Unhandled promise rejections\n * \n * Multiple cleanup functions can be registered and will be executed in the order\n * they were registered. If a cleanup function throws an error, it will be logged\n * but won't prevent other cleanup functions from running.\n * \n * @param cleanupFn - The cleanup function to register\n * \n * @example\n * ```typescript\n * import { cleanupOnExit } from '@pact-toolbox/node-utils';\n * \n * const server = createServer();\n * \n * cleanupOnExit(async () => {\n * await server.close();\n * console.log('Server closed gracefully');\n * });\n * ```\n */\nexport function cleanupOnExit(cleanupFn: CleanupFunction): void {\n cleanupHandler.registerCleanupFunction(cleanupFn);\n}\n","/**\n * Unified file system utilities\n * Combines file operations, globbing, and path utilities\n */\n\nimport { mkdir, writeFile as _writeFile, access, readFile as _readFile, stat, rm, cp } from \"node:fs/promises\";\nimport { existsSync as _existsSync, Stats } from \"node:fs\";\nimport { dirname } from \"pathe\";\nimport readdir from \"tiny-readdir-glob\";\nimport { minimatch } from \"minimatch\";\nimport chokidar from \"chokidar\";\nimport { createHash } from \"node:crypto\";\n\n// Re-export path utilities\nexport * from \"pathe\";\n\n// Export only the functions we use from globbing libraries\n\n/**\n * Reads directories recursively and returns files matching glob patterns.\n * Wrapper around tiny-readdir-glob for consistent API.\n * \n * @example\n * ```typescript\n * const files = await glob(['src', 'lib/*.js']);\n * ```\n */\nexport const glob: typeof readdir = readdir;\n\n/**\n * Watch files and directories for changes.\n * Wrapper around chokidar for file system watching.\n * \n * @example\n * ```typescript\n * const watcher = watch('src/', {\n * ignored: /node_modules/,\n * persistent: true\n * });\n * \n * watcher.on('change', (path) => {\n * console.log(`File ${path} has been changed`);\n * });\n * ```\n */\nexport const watch: typeof chokidar.watch = chokidar.watch;\n\n/**\n * Test if a file path matches a glob pattern.\n * Wrapper around minimatch for pattern matching.\n * \n * @example\n * ```typescript\n * if (matchPattern('src/index.ts', '*.ts')) {\n * console.log('This is a TypeScript file');\n * }\n * ```\n */\nexport const matchPattern: typeof minimatch = minimatch;\n\n// File system operations\n\n/**\n * Ensures a directory exists, creating it and any parent directories if necessary.\n * \n * @param dirPath - The path to the directory\n * @throws {Error} If directory creation fails\n * \n * @example\n * ```typescript\n * await ensureDir('/path/to/nested/directory');\n * ```\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n if (!(await access(dirPath).catch(() => false))) {\n await mkdir(dirPath, { recursive: true });\n }\n}\n\n/**\n * Writes content to a file, creating parent directories if they don't exist.\n * Content is automatically trimmed before writing.\n * \n * @param filePath - The path to the file\n * @param content - The content to write\n * @throws {Error} If write operation fails\n * \n * @example\n * ```typescript\n * await writeFile('/path/to/file.txt', 'Hello, World!');\n * ```\n */\nexport async function writeFile(filePath: string, content: string): Promise<void> {\n await ensureDir(dirname(filePath));\n await _writeFile(filePath, content.trim());\n}\n\n/**\n * Reads the content of a file as a string.\n * \n * @param filePath - The path to the file\n * @param encoding - The encoding to use (default: 'utf8')\n * @returns The file content as a string\n * @throws {Error} If file doesn't exist or read operation fails\n * \n * @example\n * ```typescript\n * const content = await readFile('/path/to/file.txt');\n * ```\n */\nexport async function readFile(filePath: string, encoding: BufferEncoding = \"utf8\"): Promise<string> {\n return _readFile(filePath, encoding);\n}\n\n/**\n * Synchronously checks if a file or directory exists.\n * \n * @param filePath - The path to check\n * @returns true if the path exists, false otherwise\n * \n * @example\n * ```typescript\n * if (existsSync('/path/to/file')) {\n * console.log('File exists');\n * }\n * ```\n */\nexport function existsSync(filePath: string): boolean {\n return _existsSync(filePath);\n}\n\n/**\n * Asynchronously checks if a file or directory exists.\n * \n * @param filePath - The path to check\n * @returns Promise resolving to true if the path exists, false otherwise\n * \n * @example\n * ```typescript\n * if (await exists('/path/to/file')) {\n * console.log('File exists');\n * }\n * ```\n */\nexport async function exists(filePath: string): Promise<boolean> {\n return access(filePath).then(\n () => true,\n () => false,\n );\n}\n\n/**\n * Gets file or directory statistics.\n * \n * @param filePath - The path to the file or directory\n * @returns File statistics object\n * @throws {Error} If the path doesn't exist\n * \n * @example\n * ```typescript\n * const stats = await getStats('/path/to/file');\n * console.log(`File size: ${stats.size} bytes`);\n * console.log(`Is directory: ${stats.isDirectory()}`);\n * ```\n */\nexport async function getStats(filePath: string): Promise<Stats> {\n return stat(filePath);\n}\n\n/**\n * Copies a file or directory from source to destination.\n * Creates parent directories of destination if they don't exist.\n * \n * @param src - The source path\n * @param dest - The destination path\n * @throws {Error} If copy operation fails\n * \n * @example\n * ```typescript\n * await copyFile('/path/to/source.txt', '/path/to/dest.txt');\n * await copyFile('/path/to/source-dir', '/path/to/dest-dir');\n * ```\n */\nexport async function copyFile(src: string, dest: string): Promise<void> {\n await ensureDir(dirname(dest));\n await cp(src, dest, { recursive: true });\n}\n\n/**\n * Removes a file. Does not throw if the file doesn't exist.\n * \n * @param filePath - The path to the file to remove\n * \n * @example\n * ```typescript\n * await removeFile('/path/to/file.txt');\n * ```\n */\nexport async function removeFile(filePath: string): Promise<void> {\n await rm(filePath, { force: true });\n}\n\n/**\n * Removes a directory and all its contents. Does not throw if the directory doesn't exist.\n * \n * @param dirPath - The path to the directory to remove\n * \n * @example\n * ```typescript\n * await removeDir('/path/to/directory');\n * ```\n */\nexport async function removeDir(dirPath: string): Promise<void> {\n await rm(dirPath, { recursive: true, force: true });\n}\n\n/**\n * Calculates the SHA-256 hash of a file's contents.\n * \n * @param filePath - The path to the file\n * @returns The SHA-256 hash as a hex string, or empty string if file read fails\n * \n * @example\n * ```typescript\n * const hash = await calculateFileHash('/path/to/file.txt');\n * console.log(`File hash: ${hash}`);\n * ```\n */\nexport async function calculateFileHash(filePath: string): Promise<string> {\n try {\n const content = await readFile(filePath);\n return createHash(\"sha256\").update(content).digest(\"hex\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Calculates the SHA-256 hash of a string content.\n * \n * @param content - The content to hash\n * @returns The SHA-256 hash as a hex string\n * \n * @example\n * ```typescript\n * const hash = calculateContentHash('Hello, World!');\n * console.log(`Content hash: ${hash}`);\n * ```\n */\nexport function calculateContentHash(content: string): string {\n return createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n}\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\n/**\n * Promisified version of Node.js exec function for running shell commands.\n * \n * @param command - The command to execute\n * @returns Promise with stdout and stderr output\n * @throws {Error} If the command fails with non-zero exit code\n * \n * @example\n * ```typescript\n * import { execAsync } from '@pact-toolbox/node-utils';\n * \n * try {\n * const { stdout, stderr } = await execAsync('ls -la');\n * console.log('Output:', stdout);\n * } catch (error) {\n * console.error('Command failed:', error);\n * }\n * ```\n */\nexport const execAsync: typeof exec.__promisify__ = promisify(exec);","import { execAsync } from \"./helpers\";\n\n/**\n * Regular expression for parsing Pact version strings.\n * Matches patterns like: 4.11.0, 4.11, 4.11.0-dev\n */\nexport const PACT_VERSION_REGEX: RegExp = /(\\d+)\\.(\\d+)(?:\\.(\\d+))?(-[A-Za-z0-9]+)?/;\n\n/**\n * Checks if Pact is installed on the system.\n * \n * @param match - Optional string to match against the version (e.g., \"4.11\")\n * @returns true if Pact is installed (and optionally matches the version)\n * \n * @example\n * ```typescript\n * // Check if any Pact is installed\n * if (await isAnyPactInstalled()) {\n * console.log('Pact is installed');\n * }\n * \n * // Check if Pact 4.11 is installed\n * if (await isAnyPactInstalled('4.11')) {\n * console.log('Pact 4.11 is installed');\n * }\n * ```\n */\nexport async function isAnyPactInstalled(match?: string): Promise<boolean> {\n const version = await getCurrentPactVersion();\n return match ? (version?.includes(match) ?? false) : !!version;\n}\n\n/**\n * Gets the currently installed Pact version.\n * \n * @returns The version string (e.g., \"4.11.0\") or undefined if Pact is not installed\n * \n * @example\n * ```typescript\n * const version = await getCurrentPactVersion();\n * if (version) {\n * console.log(`Pact version: ${version}`);\n * } else {\n * console.log('Pact is not installed');\n * }\n * ```\n */\nexport async function getCurrentPactVersion(): Promise<string | undefined> {\n try {\n const { stdout } = await execAsync(\"pact --version\");\n const match = stdout.match(PACT_VERSION_REGEX);\n if (match) {\n return match[0];\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Installs Pact using pactup.\n * \n * @param version - Specific version to install (e.g., \"4.11.0\")\n * @param nightly - Whether to install the nightly build\n * @returns Command output with stdout and stderr\n * @throws {Error} If installation fails\n * \n * @example\n * ```typescript\n * // Install latest stable version\n * await installPact();\n * \n * // Install specific version\n * await installPact('4.11.0');\n * \n * // Install nightly build\n * await installPact(undefined, true);\n * ```\n */\nexport async function installPact(\n version?: string,\n nightly?: boolean,\n): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> {\n if (nightly) {\n return execAsync(\"npx pactup install --nightly\");\n }\n\n if (version) {\n return execAsync(`npx pactup install ${version}`);\n }\n\n return execAsync(\"npx pactup install --latest\");\n}\n","import { getPort } from \"get-port-please\";\n\ninterface RandomPorts {\n public: number;\n service: number;\n onDemand: number;\n stratum: number;\n p2p: number;\n}\n/**\n * Gets a series of random network ports with gaps between each.\n *\n * @param host - The host for which to get the ports. Defaults to '127.0.0.1'.\n * @param startGap - The minimum gap between successive ports. Defaults to 10.\n * @param endGap - The maximum gap between successive ports. Defaults to 100.\n * @returns An object containing the random ports assigned for public, service, on-demand, stratum, and p2p services.\n * @throws {Error} If it fails to find a suitable port for any of the services.\n */\nexport async function getRandomNetworkPorts(\n host: string = \"127.0.0.1\",\n startGap: number = 10,\n endGap: number = 100,\n): Promise<RandomPorts> {\n if (startGap <= 0 || endGap <= 0 || startGap > endGap || endGap > 65535) {\n throw new Error(\"Invalid port gap values provided.\");\n }\n\n try {\n const publicPort = await getPort({\n host,\n random: true,\n name: \"public\",\n });\n\n const service = await getPort({\n port: publicPort + startGap,\n host,\n portRange: [publicPort + startGap, publicPort + endGap],\n name: \"service\",\n });\n\n const onDemand = await getPort({\n port: service + startGap,\n host,\n portRange: [service + startGap, service + endGap],\n name: \"onDemand\",\n });\n\n const stratum = await getPort({\n port: onDemand + startGap,\n host,\n portRange: [onDemand + startGap, onDemand + endGap],\n name: \"stratum\",\n });\n\n const p2p = await getPort({\n port: stratum + startGap,\n host,\n portRange: [stratum + startGap, stratum + endGap],\n name: \"p2p\",\n });\n\n return {\n public: publicPort,\n service,\n onDemand,\n stratum,\n p2p,\n };\n } catch (error) {\n throw new Error(`Failed to get network ports: ${(error as Error).message}`);\n }\n}\n\n/**\n * Checks if a specific port is already in use.\n * \n * @param port - The port number to check\n * @returns true if the port is taken, false if available\n * \n * @example\n * ```typescript\n * if (await isPortTaken(3000)) {\n * console.log('Port 3000 is already in use');\n * } else {\n * console.log('Port 3000 is available');\n * }\n * ```\n */\nexport async function isPortTaken(port: number | string): Promise<boolean> {\n try {\n // Use getPort to check if the port is available\n // If getPort returns the same port, it's available\n const availablePort = await getPort({ port: Number(port), host: '127.0.0.1' });\n return availablePort !== Number(port);\n } catch {\n // If there's an error, assume the port is taken\n return true;\n }\n}\n\nexport { getRandomPort } from \"get-port-please\";\n","import { exec, spawn } from \"child_process\";\nimport type { ChildProcess, ChildProcessWithoutNullStreams } from \"child_process\";\n\nimport { cleanupOnExit } from \"./cleanup\";\nimport { logger } from \"./logger\";\nimport { execAsync } from \"./helpers\";\n\n/**\n * Options for running a binary/executable.\n */\nexport interface RunBinOptions {\n /** Whether to suppress stdout output (default: false) */\n silent?: boolean;\n /** Working directory for the process */\n cwd?: string;\n /** Environment variables for the process */\n env?: NodeJS.ProcessEnv;\n /** Whether to resolve the promise immediately when process starts (default: true) */\n resolveOnStart?: boolean;\n /** Custom condition to resolve the promise based on stdout output */\n resolveIf?: (data: string) => boolean;\n}\n\n/**\n * Runs a binary/executable with advanced control over process lifecycle.\n * Automatically registers cleanup handlers to ensure child processes are terminated on exit.\n * \n * @param bin - The binary/executable to run\n * @param args - Arguments to pass to the binary\n * @param options - Configuration options\n * @returns Promise resolving to the child process\n * \n * @example\n * ```typescript\n * // Run a simple command\n * const child = await runBin('node', ['--version']);\n * \n * // Run with custom resolution condition\n * const server = await runBin('node', ['server.js'], {\n * resolveIf: (output) => output.includes('Server started on port'),\n * silent: true\n * });\n * ```\n */\nexport function runBin(\n bin: string,\n args: string[],\n options: RunBinOptions = {},\n): Promise<ChildProcessWithoutNullStreams> {\n const { cwd = process.cwd(), silent = false, env = process.env, resolveOnStart = true, resolveIf } = options;\n\n return new Promise((resolve, reject) => {\n const child = spawn(bin, args, { cwd, env });\n\n let resolved = false;\n\n const handleStdout = (data: Buffer) => {\n const output = data.toString();\n if (!silent) {\n console.log(output);\n }\n if (resolveIf && !resolved && resolveIf(output)) {\n resolved = true;\n resolve(child);\n }\n };\n\n const handleStderr = (data: Buffer) => {\n const errorOutput = data.toString();\n logger.error(errorOutput);\n };\n\n const handleError = (err: Error) => {\n // Always log errors, regardless of the 'silent' flag\n logger.error(\"Child process error:\", err);\n if (!resolved) {\n reject(err);\n }\n };\n\n const handleExit = (_code: number | null, _signal: NodeJS.Signals | null) => {\n if (!resolved) {\n resolved = true;\n resolve(child);\n }\n };\n\n child.stdout.on(\"data\", handleStdout);\n child.stderr.on(\"data\", handleStderr);\n child.on(\"error\", handleError);\n child.on(\"exit\", handleExit);\n\n // Register cleanup function for this child process\n cleanupOnExit(() => {\n if (!child.killed) {\n child.kill(\"SIGTERM\");\n }\n });\n\n if (resolveOnStart && !resolved) {\n resolved = true;\n resolve(child);\n }\n });\n}\n\n/**\n * Kills all processes matching the given name.\n * Cross-platform implementation using taskkill on Windows and pkill on Unix-like systems.\n * \n * @param name - The process name to kill (without .exe extension on Windows)\n * \n * @example\n * ```typescript\n * await killProcess('node');\n * await killProcess('my-server');\n * ```\n */\nexport async function killProcess(name: string): Promise<void> {\n switch (process.platform) {\n case \"win32\":\n exec(\"taskkill /F /IM \" + name + \".exe /T\");\n break;\n default: //Linux + Darwin\n exec(\"pkill -f \" + name);\n break;\n }\n}\n\n/**\n * Information about a running process.\n */\nexport interface ProcessInfo {\n /** Process ID */\n pid: number;\n /** Command name or executable path */\n command: string;\n /** Process status */\n status: \"running\" | \"stopped\";\n}\n\n/**\n * Options for spawning a simple process.\n */\nexport interface SimpleProcessOptions {\n /** Working directory for the process */\n cwd?: string;\n /** Environment variables for the process */\n env?: NodeJS.ProcessEnv;\n /** How to handle stdio streams (default: 'pipe') */\n stdio?: \"pipe\" | \"inherit\" | \"ignore\";\n /** Whether to run the process detached from the parent (default: false) */\n detached?: boolean;\n /** Timeout in milliseconds (not currently implemented) */\n timeout?: number;\n}\n\n/**\n * Spawns a long-running process with simple monitoring.\n * Automatically registers cleanup handlers to ensure child processes are terminated on exit.\n * \n * @param command - The command to execute\n * @param args - Arguments to pass to the command\n * @param options - Configuration options\n * @returns The spawned child process\n * \n * @example\n * ```typescript\n * // Spawn a simple process\n * const child = spawnProcess('npm', ['run', 'dev']);\n * \n * // Spawn with custom options\n * const server = spawnProcess('node', ['server.js'], {\n * cwd: '/path/to/project',\n * env: { ...process.env, PORT: '3000' }\n * });\n * \n * // Handle process output\n * child.stdout?.on('data', (data) => {\n * console.log(`Output: ${data}`);\n * });\n * ```\n */\nexport function spawnProcess(command: string, args: string[] = [], options: SimpleProcessOptions = {}): ChildProcess {\n const { cwd = process.cwd(), env = process.env, stdio = \"pipe\", detached = false } = options;\n\n const child = spawn(command, args, {\n cwd,\n env,\n stdio,\n detached,\n shell: true,\n });\n\n // Register cleanup\n cleanupOnExit(() => {\n if (!child.killed) {\n child.kill(\"SIGTERM\");\n }\n });\n\n return child;\n}\n\n/**\n * Checks if a process is running by PID.\n * Uses signal 0 to test process existence without actually sending a signal.\n * \n * @param pid - The process ID to check\n * @returns true if the process is running, false otherwise\n * \n * @example\n * ```typescript\n * if (isProcessRunning(12345)) {\n * console.log('Process 12345 is still running');\n * }\n * ```\n */\nexport function isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Gets basic process information by PID.\n * Cross-platform implementation using tasklist on Windows and ps on Unix-like systems.\n * \n * @param pid - The process ID to get information for\n * @returns Process information or null if process not found\n * \n * @example\n * ```typescript\n * const info = await getProcessInfo(process.pid);\n * if (info) {\n * console.log(`Process ${info.pid}: ${info.command} (${info.status})`);\n * }\n * ```\n */\nexport async function getProcessInfo(pid: number): Promise<ProcessInfo | null> {\n if (!isProcessRunning(pid)) {\n return null;\n }\n\n try {\n const command = process.platform === \"win32\" ? `tasklist /fi \"pid eq ${pid}\" /fo csv /nh` : `ps -p ${pid} -o comm=`;\n\n const result = await execAsync(command);\n const output = typeof result === \"string\" ? result : result.stdout.toString();\n const commandName =\n process.platform === \"win32\" ? output.split(\",\")[0]?.replace(/\"/g, \"\") || \"unknown\" : output.trim();\n\n return {\n pid,\n command: commandName,\n status: \"running\",\n };\n } catch {\n return null;\n }\n}\n","/**\n * Simplified UI utilities using @clack/prompts and consola\n */\n\nimport { spinner as clackSpinner, log as clackLog } from \"@clack/prompts\";\nimport { box, colors } from \"consola/utils\";\n\ntype ClackSpinner = ReturnType<typeof clackSpinner>;\n\n// Spinner management\nlet currentSpinner: ClackSpinner | null = null;\n\n// Spinner methods\n\n/**\n * Starts a new spinner with the given text.\n * If a spinner is already running, it will be stopped first.\n * \n * @param text - The text to display with the spinner\n * @returns The spinner instance\n * \n * @example\n * ```typescript\n * const spinner = startSpinner('Loading...');\n * // Do some work\n * stopSpinner(true, 'Done!');\n * ```\n */\nexport function startSpinner(text: string): ClackSpinner {\n if (currentSpinner) {\n currentSpinner.stop();\n }\n currentSpinner = clackSpinner();\n currentSpinner.start(text);\n return currentSpinner;\n}\n\n/**\n * Stops the current spinner.\n * \n * @param success - Whether the operation was successful (affects log color)\n * @param text - Optional text to display when stopping\n * \n * @example\n * ```typescript\n * startSpinner('Processing...');\n * try {\n * await doWork();\n * stopSpinner(true, 'Processing complete!');\n * } catch (error) {\n * stopSpinner(false, 'Processing failed!');\n * }\n * ```\n */\nexport function stopSpinner(success = true, text?: string): void {\n if (currentSpinner) {\n if (text) {\n currentSpinner.stop(text);\n // Also log the result\n if (success) {\n clackLog.success(text);\n } else {\n clackLog.error(text);\n }\n } else {\n currentSpinner.stop();\n }\n currentSpinner = null;\n }\n}\n\n/**\n * Updates the text of the current spinner.\n * \n * @param text - The new text to display\n * \n * @example\n * ```typescript\n * startSpinner('Processing item 1 of 10...');\n * for (let i = 1; i <= 10; i++) {\n * updateSpinner(`Processing item ${i} of 10...`);\n * await processItem(i);\n * }\n * stopSpinner(true, 'All items processed!');\n * ```\n */\nexport function updateSpinner(text: string): void {\n if (currentSpinner) {\n currentSpinner.message(text);\n }\n}\n\n// Box using consola utils\n\n/**\n * Displays a message in a bordered box for emphasis.\n * \n * @param title - The title to display in bold\n * @param content - The content lines (string or array of strings)\n * \n * @example\n * ```typescript\n * boxMessage('Important Notice', [\n * 'Your configuration has been updated.',\n * 'Please restart the application.'\n * ]);\n * ```\n */\nexport function boxMessage(title: string, content: string | string[]): void {\n const lines = Array.isArray(content) ? content : [content];\n const boxContent = [colors.bold(title), \"\", ...lines].join(\"\\n\");\n console.log(box(boxContent));\n}\n\n// Simple table - since it's only used once, keep it minimal\n\n/**\n * Displays data in a simple table format.\n * Long cell values are truncated with ellipsis.\n * \n * @param headers - The table headers\n * @param rows - The table rows (2D array)\n * \n * @example\n * ```typescript\n * table(\n * ['Name', 'Status', 'Port'],\n * [\n * ['Server 1', 'Running', '3000'],\n * ['Server 2', 'Stopped', '3001'],\n * ['Server 3', 'Running', '3002']\n * ]\n * );\n * ```\n */\nexport function table(headers: string[], rows: string[][]): void {\n const columnWidths = headers.map((header, i) => {\n const maxWidth = Math.max(header.length, ...rows.map((row) => (row[i] || \"\").toString().length));\n return Math.min(maxWidth, 40);\n });\n\n // Print headers with consola colors\n const headerRow = headers.map((header, i) => header.padEnd(columnWidths[i] || 0)).join(\" │ \");\n\n console.log(colors.bold(headerRow));\n console.log(\"─\".repeat(headerRow.length));\n\n // Print rows\n for (const row of rows) {\n const rowStr = row\n .map((cell, i) => {\n const str = (cell || \"\").toString();\n return str.length > (columnWidths[i] || 0)\n ? str.substring(0, (columnWidths[i] || 0) - 3) + \"...\"\n : str.padEnd(columnWidths[i] || 0);\n })\n .join(\" │ \");\n console.log(rowStr);\n }\n}\n\n// Clear console\n\n/**\n * Clears the console screen.\n * \n * @example\n * ```typescript\n * clear();\n * console.log('Fresh start!');\n * ```\n */\nexport function clear(): void {\n console.clear();\n}\n","export * from \"./cleanup\";\nexport * from \"./filesystem\";\nexport * from \"./helpers\";\nexport * from \"./logger\";\nexport * from \"./object-utils\";\nexport * from \"./pact\";\nexport * from \"./port\";\nexport * from \"./process\";\nexport * from \"./prompts\";\nexport * from \"./ui\";\n"],"mappings":"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"}