@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.
763 lines (761 loc) • 23.6 kB
text/typescript
import { Stats } from "node:fs";
import readdir from "tiny-readdir-glob";
import { minimatch } from "minimatch";
import chokidar from "chokidar";
import { exec } from "node:child_process";
import { ConsolaInstance, LogLevels } from "consola";
import { colorize, colors, getColor, stripAnsi } from "consola/utils";
import { createDefu, defu, defuArrayFn, defuFn } from "defu";
import { getRandomPort } from "get-port-please";
import { ChildProcess, ChildProcessWithoutNullStreams } from "child_process";
import { confirm, intro, isCancel, multiselect, outro, select, spinner, spinner as spinner$1, text } from "@clack/prompts";
//#region rolldown:runtime
//#endregion
//#region src/cleanup.d.ts
/**
* A function that performs cleanup operations.
* Can be synchronous or asynchronous.
*/
type CleanupFunction = () => void | Promise<void>;
/**
* Registers a cleanup function to be executed when the process exits.
*
* The cleanup function will be called on:
* - SIGINT (Ctrl+C)
* - SIGTERM (termination signal)
* - SIGQUIT
* - SIGHUP
* - Normal process exit
* - Uncaught exceptions
* - Unhandled promise rejections
*
* Multiple cleanup functions can be registered and will be executed in the order
* they were registered. If a cleanup function throws an error, it will be logged
* but won't prevent other cleanup functions from running.
*
* @param cleanupFn - The cleanup function to register
*
* @example
* ```typescript
* import { cleanupOnExit } from '@pact-toolbox/node-utils';
*
* const server = createServer();
*
* cleanupOnExit(async () => {
* await server.close();
* console.log('Server closed gracefully');
* });
* ```
*/
declare function cleanupOnExit(cleanupFn: CleanupFunction): void;
declare namespace filesystem_d_exports {
export { calculateContentHash, calculateFileHash, copyFile, ensureDir, exists, existsSync, getStats, glob, matchPattern, readFile, removeDir, removeFile, watch, writeFile };
}
import * as import_pathe from "pathe";
__reExport(filesystem_d_exports, import_pathe);
// Export only the functions we use from globbing libraries
/**
* Reads directories recursively and returns files matching glob patterns.
* Wrapper around tiny-readdir-glob for consistent API.
*
* @example
* ```typescript
* const files = await glob(['src', 'lib/*.js']);
* ```
*/
declare const glob: typeof readdir;
/**
* Watch files and directories for changes.
* Wrapper around chokidar for file system watching.
*
* @example
* ```typescript
* const watcher = watch('src/', {
* ignored: /node_modules/,
* persistent: true
* });
*
* watcher.on('change', (path) => {
* console.log(`File ${path} has been changed`);
* });
* ```
*/
declare const watch: typeof chokidar.watch;
/**
* Test if a file path matches a glob pattern.
* Wrapper around minimatch for pattern matching.
*
* @example
* ```typescript
* if (matchPattern('src/index.ts', '*.ts')) {
* console.log('This is a TypeScript file');
* }
* ```
*/
declare const matchPattern: typeof minimatch;
// File system operations
/**
* Ensures a directory exists, creating it and any parent directories if necessary.
*
* @param dirPath - The path to the directory
* @throws {Error} If directory creation fails
*
* @example
* ```typescript
* await ensureDir('/path/to/nested/directory');
* ```
*/
declare function ensureDir(dirPath: string): Promise<void>;
/**
* Writes content to a file, creating parent directories if they don't exist.
* Content is automatically trimmed before writing.
*
* @param filePath - The path to the file
* @param content - The content to write
* @throws {Error} If write operation fails
*
* @example
* ```typescript
* await writeFile('/path/to/file.txt', 'Hello, World!');
* ```
*/
declare function writeFile(filePath: string, content: string): Promise<void>;
/**
* Reads the content of a file as a string.
*
* @param filePath - The path to the file
* @param encoding - The encoding to use (default: 'utf8')
* @returns The file content as a string
* @throws {Error} If file doesn't exist or read operation fails
*
* @example
* ```typescript
* const content = await readFile('/path/to/file.txt');
* ```
*/
declare function readFile(filePath: string, encoding?: BufferEncoding): Promise<string>;
/**
* Synchronously checks if a file or directory exists.
*
* @param filePath - The path to check
* @returns true if the path exists, false otherwise
*
* @example
* ```typescript
* if (existsSync('/path/to/file')) {
* console.log('File exists');
* }
* ```
*/
declare function existsSync(filePath: string): boolean;
/**
* Asynchronously checks if a file or directory exists.
*
* @param filePath - The path to check
* @returns Promise resolving to true if the path exists, false otherwise
*
* @example
* ```typescript
* if (await exists('/path/to/file')) {
* console.log('File exists');
* }
* ```
*/
declare function exists(filePath: string): Promise<boolean>;
/**
* Gets file or directory statistics.
*
* @param filePath - The path to the file or directory
* @returns File statistics object
* @throws {Error} If the path doesn't exist
*
* @example
* ```typescript
* const stats = await getStats('/path/to/file');
* console.log(`File size: ${stats.size} bytes`);
* console.log(`Is directory: ${stats.isDirectory()}`);
* ```
*/
declare function getStats(filePath: string): Promise<Stats>;
/**
* Copies a file or directory from source to destination.
* Creates parent directories of destination if they don't exist.
*
* @param src - The source path
* @param dest - The destination path
* @throws {Error} If copy operation fails
*
* @example
* ```typescript
* await copyFile('/path/to/source.txt', '/path/to/dest.txt');
* await copyFile('/path/to/source-dir', '/path/to/dest-dir');
* ```
*/
declare function copyFile(src: string, dest: string): Promise<void>;
/**
* Removes a file. Does not throw if the file doesn't exist.
*
* @param filePath - The path to the file to remove
*
* @example
* ```typescript
* await removeFile('/path/to/file.txt');
* ```
*/
declare function removeFile(filePath: string): Promise<void>;
/**
* Removes a directory and all its contents. Does not throw if the directory doesn't exist.
*
* @param dirPath - The path to the directory to remove
*
* @example
* ```typescript
* await removeDir('/path/to/directory');
* ```
*/
declare function removeDir(dirPath: string): Promise<void>;
/**
* Calculates the SHA-256 hash of a file's contents.
*
* @param filePath - The path to the file
* @returns The SHA-256 hash as a hex string, or empty string if file read fails
*
* @example
* ```typescript
* const hash = await calculateFileHash('/path/to/file.txt');
* console.log(`File hash: ${hash}`);
* ```
*/
declare function calculateFileHash(filePath: string): Promise<string>;
/**
* Calculates the SHA-256 hash of a string content.
*
* @param content - The content to hash
* @returns The SHA-256 hash as a hex string
*
* @example
* ```typescript
* const hash = calculateContentHash('Hello, World!');
* console.log(`Content hash: ${hash}`);
* ```
*/
declare function calculateContentHash(content: string): string;
//#endregion
//#region src/helpers.d.ts
/**
* Promisified version of Node.js exec function for running shell commands.
*
* @param command - The command to execute
* @returns Promise with stdout and stderr output
* @throws {Error} If the command fails with non-zero exit code
*
* @example
* ```typescript
* import { execAsync } from '@pact-toolbox/node-utils';
*
* try {
* const { stdout, stderr } = await execAsync('ls -la');
* console.log('Output:', stdout);
* } catch (error) {
* console.error('Command failed:', error);
* }
* ```
*/
declare const execAsync: typeof exec.__promisify__;
//#endregion
//#region src/logger.d.ts
/**
* Main logger instance configured with environment-based log level.
*
* Log levels:
* - 0: silent/fatal
* - 1: error
* - 2: warn
* - 3: info/log (default)
* - 4: debug
* - 5: trace
*
* Set log level via environment variables:
* - DEBUG=1 or DEBUG=true for debug level
* - LOG_LEVEL=debug/info/warn/error/silent
*
* @example
* ```typescript
* import { logger } from '@pact-toolbox/node-utils';
*
* logger.info('Application started');
* logger.error('An error occurred', error);
* logger.debug('Debug information', { data });
* ```
*/
declare const logger: ConsolaInstance;
/**
* Type alias for logger instances.
*/
type Logger = ConsolaInstance;
// Convenience functions for common logging patterns
/** Log informational messages */
declare const info: typeof logger.info;
/** Log warning messages */
declare const warn: typeof logger.warn;
/** Log error messages */
declare const error: typeof logger.error;
/** Log debug messages (only shown when debug level is enabled) */
declare const debug: typeof logger.debug;
/** Log success messages with green styling */
declare const success: typeof logger.success;
/** Log failure messages with red styling */
declare const fail: typeof logger.fail;
/** Log ready messages (typically for server/service startup) */
declare const ready: typeof logger.ready;
/** Log start messages (typically for process/task initiation) */
declare const start: typeof logger.start;
/** Log general messages */
declare const log: typeof logger.log;
/** Display a message in a box for emphasis */
declare const box: typeof logger.box;
/**
* Creates a tagged logger for a specific package or component.
* Tagged loggers prefix all messages with the tag for easier identification.
*
* @param tag - The tag to prefix messages with
* @returns A new logger instance with the specified tag
*
* @example
* ```typescript
* const networkLogger = createLogger('network');
* networkLogger.info('Connection established'); // [network] Connection established
*
* const dbLogger = createLogger('database');
* dbLogger.error('Query failed'); // [database] Query failed
* ```
*/
declare function createLogger(tag: string): ConsolaInstance;
/**
* Logs performance metrics for operations.
* Only visible when debug level is enabled.
*
* @param operation - The name of the operation
* @param duration - The duration in milliseconds
* @param data - Optional additional data to log
*
* @example
* ```typescript
* const startTime = Date.now();
* await performOperation();
* const duration = Date.now() - startTime;
*
* logPerformance('database.query', duration, { query: 'SELECT * FROM users' });
* // [PERF] database.query completed in 123ms { query: 'SELECT * FROM users' }
* ```
*/
declare function logPerformance(operation: string, duration: number, data?: any): void;
/**
* Logs a message with explicit context/category and level.
* Useful for dynamic logging where the level is determined at runtime.
*
* @param level - The log level to use
* @param context - The context/category tag
* @param message - The message to log
* @param data - Optional additional data to log
*
* @example
* ```typescript
* function handleRequest(severity: string) {
* const level = severity === 'critical' ? 'error' : 'warn';
* logWithContext(level, 'api', 'Request failed', {
* endpoint: '/users',
* status: 500
* });
* }
* ```
*/
declare function logWithContext(level: "info" | "warn" | "error" | "debug", context: string, message: string, data?: any): void;
// Re-export color utilities for consistent styling across the package
//#endregion
//#region src/pact.d.ts
/**
* Regular expression for parsing Pact version strings.
* Matches patterns like: 4.11.0, 4.11, 4.11.0-dev
*/
declare const PACT_VERSION_REGEX: RegExp;
/**
* Checks if Pact is installed on the system.
*
* @param match - Optional string to match against the version (e.g., "4.11")
* @returns true if Pact is installed (and optionally matches the version)
*
* @example
* ```typescript
* // Check if any Pact is installed
* if (await isAnyPactInstalled()) {
* console.log('Pact is installed');
* }
*
* // Check if Pact 4.11 is installed
* if (await isAnyPactInstalled('4.11')) {
* console.log('Pact 4.11 is installed');
* }
* ```
*/
declare function isAnyPactInstalled(match?: string): Promise<boolean>;
/**
* Gets the currently installed Pact version.
*
* @returns The version string (e.g., "4.11.0") or undefined if Pact is not installed
*
* @example
* ```typescript
* const version = await getCurrentPactVersion();
* if (version) {
* console.log(`Pact version: ${version}`);
* } else {
* console.log('Pact is not installed');
* }
* ```
*/
declare function getCurrentPactVersion(): Promise<string | undefined>;
/**
* Installs Pact using pactup.
*
* @param version - Specific version to install (e.g., "4.11.0")
* @param nightly - Whether to install the nightly build
* @returns Command output with stdout and stderr
* @throws {Error} If installation fails
*
* @example
* ```typescript
* // Install latest stable version
* await installPact();
*
* // Install specific version
* await installPact('4.11.0');
*
* // Install nightly build
* await installPact(undefined, true);
* ```
*/
declare function installPact(version?: string, nightly?: boolean): Promise<{
stdout: string | Buffer;
stderr: string | Buffer;
}>;
//#endregion
//#region src/port.d.ts
interface RandomPorts {
public: number;
service: number;
onDemand: number;
stratum: number;
p2p: number;
}
/**
* Gets a series of random network ports with gaps between each.
*
* @param host - The host for which to get the ports. Defaults to '127.0.0.1'.
* @param startGap - The minimum gap between successive ports. Defaults to 10.
* @param endGap - The maximum gap between successive ports. Defaults to 100.
* @returns An object containing the random ports assigned for public, service, on-demand, stratum, and p2p services.
* @throws {Error} If it fails to find a suitable port for any of the services.
*/
declare function getRandomNetworkPorts(host?: string, startGap?: number, endGap?: number): Promise<RandomPorts>;
/**
* Checks if a specific port is already in use.
*
* @param port - The port number to check
* @returns true if the port is taken, false if available
*
* @example
* ```typescript
* if (await isPortTaken(3000)) {
* console.log('Port 3000 is already in use');
* } else {
* console.log('Port 3000 is available');
* }
* ```
*/
declare function isPortTaken(port: number | string): Promise<boolean>;
//#endregion
//#region src/process.d.ts
/**
* Options for running a binary/executable.
*/
interface RunBinOptions {
/** Whether to suppress stdout output (default: false) */
silent?: boolean;
/** Working directory for the process */
cwd?: string;
/** Environment variables for the process */
env?: NodeJS.ProcessEnv;
/** Whether to resolve the promise immediately when process starts (default: true) */
resolveOnStart?: boolean;
/** Custom condition to resolve the promise based on stdout output */
resolveIf?: (data: string) => boolean;
}
/**
* Runs a binary/executable with advanced control over process lifecycle.
* Automatically registers cleanup handlers to ensure child processes are terminated on exit.
*
* @param bin - The binary/executable to run
* @param args - Arguments to pass to the binary
* @param options - Configuration options
* @returns Promise resolving to the child process
*
* @example
* ```typescript
* // Run a simple command
* const child = await runBin('node', ['--version']);
*
* // Run with custom resolution condition
* const server = await runBin('node', ['server.js'], {
* resolveIf: (output) => output.includes('Server started on port'),
* silent: true
* });
* ```
*/
declare function runBin(bin: string, args: string[], options?: RunBinOptions): Promise<ChildProcessWithoutNullStreams>;
/**
* Kills all processes matching the given name.
* Cross-platform implementation using taskkill on Windows and pkill on Unix-like systems.
*
* @param name - The process name to kill (without .exe extension on Windows)
*
* @example
* ```typescript
* await killProcess('node');
* await killProcess('my-server');
* ```
*/
declare function killProcess(name: string): Promise<void>;
/**
* Information about a running process.
*/
interface ProcessInfo {
/** Process ID */
pid: number;
/** Command name or executable path */
command: string;
/** Process status */
status: "running" | "stopped";
}
/**
* Options for spawning a simple process.
*/
interface SimpleProcessOptions {
/** Working directory for the process */
cwd?: string;
/** Environment variables for the process */
env?: NodeJS.ProcessEnv;
/** How to handle stdio streams (default: 'pipe') */
stdio?: "pipe" | "inherit" | "ignore";
/** Whether to run the process detached from the parent (default: false) */
detached?: boolean;
/** Timeout in milliseconds (not currently implemented) */
timeout?: number;
}
/**
* Spawns a long-running process with simple monitoring.
* Automatically registers cleanup handlers to ensure child processes are terminated on exit.
*
* @param command - The command to execute
* @param args - Arguments to pass to the command
* @param options - Configuration options
* @returns The spawned child process
*
* @example
* ```typescript
* // Spawn a simple process
* const child = spawnProcess('npm', ['run', 'dev']);
*
* // Spawn with custom options
* const server = spawnProcess('node', ['server.js'], {
* cwd: '/path/to/project',
* env: { ...process.env, PORT: '3000' }
* });
*
* // Handle process output
* child.stdout?.on('data', (data) => {
* console.log(`Output: ${data}`);
* });
* ```
*/
declare function spawnProcess(command: string, args?: string[], options?: SimpleProcessOptions): ChildProcess;
/**
* Checks if a process is running by PID.
* Uses signal 0 to test process existence without actually sending a signal.
*
* @param pid - The process ID to check
* @returns true if the process is running, false otherwise
*
* @example
* ```typescript
* if (isProcessRunning(12345)) {
* console.log('Process 12345 is still running');
* }
* ```
*/
declare function isProcessRunning(pid: number): boolean;
/**
* Gets basic process information by PID.
* Cross-platform implementation using tasklist on Windows and ps on Unix-like systems.
*
* @param pid - The process ID to get information for
* @returns Process information or null if process not found
*
* @example
* ```typescript
* const info = await getProcessInfo(process.pid);
* if (info) {
* console.log(`Process ${info.pid}: ${info.command} (${info.status})`);
* }
* ```
*/
declare function getProcessInfo(pid: number): Promise<ProcessInfo | null>;
//#endregion
//#region src/prompts.d.ts
/**
* Type for spinner instances created by @clack/prompts.
*/
type Spinner = ReturnType<typeof spinner$1>;
/**
* Re-exports from @clack/prompts for interactive CLI prompts.
*
* @example
* ```typescript
* import { select, text, multiselect, intro, outro } from '@pact-toolbox/node-utils';
*
* intro('Welcome to the setup wizard!');
*
* const name = await text({
* message: 'What is your project name?',
* defaultValue: 'my-project'
* });
*
* const framework = await select({
* message: 'Pick a framework',
* options: [
* { value: 'react', label: 'React' },
* { value: 'vue', label: 'Vue' },
* { value: 'svelte', label: 'Svelte' }
* ]
* });
*
* const features = await multiselect({
* message: 'Select features',
* options: [
* { value: 'typescript', label: 'TypeScript' },
* { value: 'eslint', label: 'ESLint' },
* { value: 'testing', label: 'Testing' }
* ]
* });
*
* outro('Setup complete!');
* ```
*/
//#endregion
//#region src/ui.d.ts
type ClackSpinner = ReturnType<typeof spinner$1>;
// Spinner methods
/**
* Starts a new spinner with the given text.
* If a spinner is already running, it will be stopped first.
*
* @param text - The text to display with the spinner
* @returns The spinner instance
*
* @example
* ```typescript
* const spinner = startSpinner('Loading...');
* // Do some work
* stopSpinner(true, 'Done!');
* ```
*/
declare function startSpinner(text: string): ClackSpinner;
/**
* Stops the current spinner.
*
* @param success - Whether the operation was successful (affects log color)
* @param text - Optional text to display when stopping
*
* @example
* ```typescript
* startSpinner('Processing...');
* try {
* await doWork();
* stopSpinner(true, 'Processing complete!');
* } catch (error) {
* stopSpinner(false, 'Processing failed!');
* }
* ```
*/
declare function stopSpinner(success?: boolean, text?: string): void;
/**
* Updates the text of the current spinner.
*
* @param text - The new text to display
*
* @example
* ```typescript
* startSpinner('Processing item 1 of 10...');
* for (let i = 1; i <= 10; i++) {
* updateSpinner(`Processing item ${i} of 10...`);
* await processItem(i);
* }
* stopSpinner(true, 'All items processed!');
* ```
*/
declare function updateSpinner(text: string): void;
// Box using consola utils
/**
* Displays a message in a bordered box for emphasis.
*
* @param title - The title to display in bold
* @param content - The content lines (string or array of strings)
*
* @example
* ```typescript
* boxMessage('Important Notice', [
* 'Your configuration has been updated.',
* 'Please restart the application.'
* ]);
* ```
*/
declare function boxMessage(title: string, content: string | string[]): void;
// Simple table - since it's only used once, keep it minimal
/**
* Displays data in a simple table format.
* Long cell values are truncated with ellipsis.
*
* @param headers - The table headers
* @param rows - The table rows (2D array)
*
* @example
* ```typescript
* table(
* ['Name', 'Status', 'Port'],
* [
* ['Server 1', 'Running', '3000'],
* ['Server 2', 'Stopped', '3001'],
* ['Server 3', 'Running', '3002']
* ]
* );
* ```
*/
declare function table(headers: string[], rows: string[][]): void;
// Clear console
/**
* Clears the console screen.
*
* @example
* ```typescript
* clear();
* console.log('Fresh start!');
* ```
*/
declare function clear(): void;
declare namespace index_d_exports {
export { LogLevels, Logger, PACT_VERSION_REGEX, ProcessInfo, RunBinOptions, SimpleProcessOptions, Spinner, box, boxMessage, calculateContentHash, calculateFileHash, cleanupOnExit, clear, colorize, colors, confirm, copyFile, createDefu, createLogger, debug, defu, defuArrayFn, defuFn, ensureDir, error, execAsync, exists, existsSync, fail, getColor, getCurrentPactVersion, getProcessInfo, getRandomNetworkPorts, getRandomPort, getStats, glob, info, installPact, intro, isAnyPactInstalled, isCancel, isPortTaken, isProcessRunning, killProcess, log, logPerformance, logWithContext, logger, matchPattern, multiselect, outro, readFile, ready, removeDir, removeFile, runBin, select, spawnProcess, spinner, start, startSpinner, stopSpinner, stripAnsi, success, table, text, updateSpinner, warn, watch, writeFile };
}
__reExport(index_d_exports, filesystem_d_exports);
//#endregion
export { LogLevels, Logger, PACT_VERSION_REGEX, ProcessInfo, RunBinOptions, SimpleProcessOptions, Spinner, box, boxMessage, calculateContentHash, calculateFileHash, cleanupOnExit, clear, colorize, colors, confirm, copyFile, createDefu, createLogger, debug, defu, defuArrayFn, defuFn, ensureDir, error, execAsync, exists, existsSync, fail, getColor, getCurrentPactVersion, getProcessInfo, getRandomNetworkPorts, getRandomPort, getStats, glob, info, installPact, intro, isAnyPactInstalled, isCancel, isPortTaken, isProcessRunning, killProcess, log, logPerformance, logWithContext, logger, matchPattern, multiselect, outro, readFile, ready, removeDir, removeFile, runBin, select, spawnProcess, spinner, start, startSpinner, stopSpinner, stripAnsi, success, table, text, updateSpinner, warn, watch, writeFile };
//# sourceMappingURL=index.d.cts.map