ts-ping
Version:
A modern TypeScript library for performing ICMP ping operations with type-safe results and fluent configuration.
456 lines (451 loc) • 15.8 kB
TypeScript
import { SpawnSyncReturns } from 'node:child_process';
declare class PingResultLine {
readonly rawLine: string;
readonly timeInMs: number;
constructor(line?: string, timeInMs?: number);
static fromLine(line: string): PingResultLine;
getRawLine(): string;
getTimeInMs(): number;
toArray(): {
line: string;
time_in_ms: number;
};
toString(): string;
}
declare const PingError: {
readonly HostnameNotFound: "HostnameNotFound";
readonly HostUnreachable: "HostUnreachable";
readonly PermissionDenied: "PermissionDenied";
readonly Timeout: "Timeout";
readonly UnknownError: "UnknownError";
};
type PingErrorType = typeof PingError[keyof typeof PingError];
declare const PingErrorUtils: {
from: (value: string) => PingErrorType;
};
interface PingResultOptions {
output: string[];
returnCode: number;
host: string;
timeout: number;
interval: number;
packetSize: number;
ttl: number;
ipVersion?: 4 | 6;
}
interface PingResultArray {
success: boolean;
error: PingErrorType | null;
host: string | null;
packet_loss_percentage: number;
packets_transmitted: number | null;
packets_received: number | null;
options: {
timeout_in_seconds: number | null;
interval: number;
packet_size_in_bytes: number;
ttl: number;
ip_version?: 4 | 6;
};
timings: {
minimum_time_in_ms: number | null;
maximum_time_in_ms: number | null;
average_time_in_ms: number | null;
standard_deviation_time_in_ms: number | null;
};
raw_output: string;
lines: {
line: string;
time_in_ms: number;
}[];
}
interface SuccessfulPingResult extends PingResult {
readonly success: true;
readonly error: null;
readonly host: string;
readonly numberOfPacketsTransmitted: number;
readonly numberOfPacketsReceived: number;
}
interface FailedPingResult extends PingResult {
readonly success: false;
readonly error: PingErrorType;
readonly packetLossPercentage: 100;
readonly minimumTimeInMs: null;
readonly maximumTimeInMs: null;
readonly averageTimeInMs: null;
readonly standardDeviationTimeInMs: null;
}
declare class PingResult {
readonly success: boolean;
readonly error: PingErrorType | null;
readonly host: string | null;
readonly packetLossPercentage: number;
readonly numberOfPacketsTransmitted: number | null;
readonly numberOfPacketsReceived: number | null;
readonly timeoutInSeconds: number | null;
readonly intervalInSeconds: number;
readonly packetSizeInBytes: number;
readonly ttl: number;
readonly ipVersion?: 4 | 6;
readonly minimumTimeInMs: number | null;
readonly maximumTimeInMs: number | null;
readonly averageTimeInMs: number | null;
readonly standardDeviationTimeInMs: number | null;
readonly rawOutput: string;
readonly lines: PingResultLine[];
private constructor();
isSuccess(): this is SuccessfulPingResult;
isFailure(): this is FailedPingResult;
static fromPingOutput({ output, returnCode, host, timeout, interval, packetSize, ttl, ipVersion }: PingResultOptions): PingResult;
/**
* Creates a failed PingResult from an error.
* Used when ping operations throw exceptions.
*/
static fromError(error: Error, host: string, options: {
timeout: number;
interval: number;
packetSize: number;
ttl: number;
ipVersion?: 4 | 6;
}): PingResult;
static determineErrorFromMessage(message: string): PingErrorType;
static determineErrorFromOutput(output: string): PingErrorType;
static parsePingLines(output: string[]): PingResultLine[];
static isPingResponseLine(line: string): boolean;
static calculatePacketLossPercentage(transmitted: number, received: number): number;
averageResponseTimeInMs(): number;
toArray(): PingResultArray;
toString(): string;
}
declare class Ping {
readonly hostname: string;
timeoutInSeconds: number;
count: number;
intervalInSeconds: number;
packetSizeInBytes: number;
ttl: number;
ipVersion?: 4 | 6;
/**
* Optional AbortSignal for external cancellation of ping operations.
* When the signal is aborted, all ongoing and future ping operations will be cancelled.
*
* @example
* ```typescript
* const abortController = new AbortController()
* const ping = new Ping('google.com').setAbortSignal(abortController.signal)
*
* // Cancel after 5 seconds
* setTimeout(() => abortController.abort(), 5000)
*
* // Or use AbortSignal.timeout for simpler timeout-based cancellation
* const ping2 = new Ping('google.com').setAbortSignal(AbortSignal.timeout(5000))
* ```
*/
abortSignal?: AbortSignal;
private currentCommand;
constructor(hostname: string, timeoutInSeconds?: number, count?: number, intervalInSeconds?: number, packetSizeInBytes?: number, ttl?: number);
/**
* Auto-detects the IP version based on the hostname.
* Only sets IP version for IPv6 addresses to ensure proper command selection on macOS.
* IPv4 addresses and hostnames default to undefined (system default).
*/
private autoDetectIPVersion;
run(): PingResult;
runAsync(): Promise<PingResult>;
/**
* Creates an async generator that yields ping results in real-time.
* Useful for continuous monitoring and streaming ping data.
*
* @example
* ```typescript
* const ping = new Ping('google.com').setInterval(1).setCount(0) // infinite
*
* for await (const result of ping.stream()) {
* if (result.isSuccess()) {
* console.log(`${new Date().toISOString()}: ${result.averageTimeInMs()}ms`)
* } else {
* console.error(`Ping failed: ${result.error}`)
* }
* }
* ```
*/
stream(): AsyncGenerator<PingResult, void, unknown>;
/**
* Creates an async generator that yields ping results with filtering and transformation.
*
* @param filter Optional filter function to include only specific results
* @param transform Optional transformation function to modify yielded values
*
* @example
* ```typescript
* // Only yield successful pings with latency values
* for await (const latency of ping.streamWithFilter(
* r => r.isSuccess(),
* r => r.averageTimeInMs()
* )) {
* console.log(`Latency: ${latency}ms`)
* }
* ```
*/
streamWithFilter<T = PingResult>(filter?: (result: PingResult) => boolean, transform?: (result: PingResult) => T): AsyncGenerator<T, void, unknown>;
/**
* Creates an async generator that yields batches of ping results.
* Useful for processing results in chunks or implementing backpressure.
*
* @param bufferSize Number of results to collect before yielding a batch
*
* @example
* ```typescript
* for await (const batch of ping.streamBatched(5)) {
* console.log(`Processing batch of ${batch.length} results`)
* const avgLatency = batch
* .filter(r => r.isSuccess())
* .reduce((sum, r) => sum + r.averageTimeInMs(), 0) / batch.length
* console.log(`Average latency: ${avgLatency}ms`)
* }
* ```
*/
streamBatched(bufferSize?: number): AsyncGenerator<PingResult[], void, unknown>;
/**
* Runs a single ping operation with sequence number.
* Used internally by the streaming methods.
*/
private runSinglePing;
/**
* Sleep utility for interval timing.
*/
private sleep;
executePingCommand(commandArray: string[]): SpawnSyncReturns<string>;
executePingCommandAsync(commandArray: string[]): Promise<SpawnSyncReturns<string>>;
calculateProcessTimeout(): number;
combineOutputLines(result: SpawnSyncReturns<string>): string[];
setTimeout(timeout: number): Ping;
setCount(count: number): Ping;
setInterval(interval: number): Ping;
setPacketSize(size: number): Ping;
setTtl(ttl: number): Ping;
setIPVersion(version: 4 | 6): Ping;
setIPv4(): Ping;
setIPv6(): Ping;
/**
* Sets an AbortSignal for external cancellation of ping operations.
*
* @param abortSignal The AbortSignal to use for cancellation
* @returns This Ping instance for method chaining
*
* @example
* ```typescript
* const abortController = new AbortController()
* const ping = new Ping('google.com').setAbortSignal(abortController.signal)
*
* // Cancel the operation
* abortController.abort()
*
* // Or use timeout-based cancellation
* const ping2 = new Ping('google.com').setAbortSignal(AbortSignal.timeout(5000))
* ```
*/
setAbortSignal(abortSignal: AbortSignal): Ping;
buildPingCommand(): string[];
startWithPingCommand(): Ping;
addIPVersionOption(): Ping;
getCommand(): string[];
addPacketCountOption(): Ping;
addTimeoutOption(): Ping;
addOptionalIntervalOption(): Ping;
addOptionalPacketSizeOption(): Ping;
addOptionalTtlOption(): Ping;
addTargetHostname(): Ping;
isRunningOnMacOS(): boolean;
isRunningOnWindows(): boolean;
convertTimeoutToMilliseconds(): number;
}
/**
* Statistics calculated from a window of ping results.
*/
interface PingStats {
/** Number of successful pings in the window */
count: number;
/** Average response time in milliseconds */
average: number;
/** Minimum response time in milliseconds */
minimum: number;
/** Maximum response time in milliseconds */
maximum: number;
/** Standard deviation of response times */
standardDeviation: number;
/** Network jitter (variance in response times) */
jitter: number;
/** Packet loss percentage in the window */
packetLoss: number;
/** Timestamp when the stats were calculated */
timestamp: Date;
}
/**
* Enhanced streaming utilities for ping operations.
* Provides advanced processing capabilities like windowing, statistics, and filtering.
*
* @example
* ```typescript
* const stream = new PingStream(new Ping('google.com').setInterval(0.5))
*
* // Get rolling statistics
* for await (const stats of stream.rollingStats(10)) {
* console.log(`Avg: ${stats.average}ms, Jitter: ${stats.jitter}ms`)
* }
* ```
*/
declare class PingStream {
private ping;
constructor(ping: Ping);
/**
* Takes only the first N results from the ping stream.
*
* @param n Number of results to take
*
* @example
* ```typescript
* // Take first 5 ping results
* for await (const result of stream.take(5)) {
* console.log(`Ping ${result.isSuccess() ? 'success' : 'failed'}`)
* }
* ```
*/
take(n: number): AsyncGenerator<PingResult, void, unknown>;
/**
* Skips failed ping results and only yields successful ones.
*
* @example
* ```typescript
* // Only process successful pings
* for await (const result of stream.skipFailures()) {
* console.log(`Response time: ${result.averageResponseTimeInMs()}ms`)
* }
* ```
*/
skipFailures(): AsyncGenerator<PingResult, void, unknown>;
/**
* Skips successful ping results and only yields failed ones.
* Useful for monitoring and alerting on failures.
*
* @example
* ```typescript
* // Monitor only failures
* for await (const failure of stream.skipSuccesses()) {
* console.error(`Ping failed: ${failure.error}`)
* await sendAlert(failure)
* }
* ```
*/
skipSuccesses(): AsyncGenerator<PingResult, void, unknown>;
/**
* Creates a sliding window of ping results.
* Each yield contains the last N results.
*
* @param size Size of the sliding window
*
* @example
* ```typescript
* // Process results in sliding windows of 5
* for await (const window of stream.window(5)) {
* const avgLatency = window
* .filter(r => r.isSuccess())
* .reduce((sum, r) => sum + r.averageResponseTimeInMs(), 0) / window.length
* console.log(`Window avg: ${avgLatency}ms`)
* }
* ```
*/
window(size: number): AsyncGenerator<PingResult[], void, unknown>;
/**
* Calculates rolling statistics from a sliding window of ping results.
* Only successful pings are included in the statistics.
*
* @param windowSize Size of the sliding window for calculating stats (default: 10)
*
* @example
* ```typescript
* // Monitor network performance with rolling stats
* for await (const stats of stream.rollingStats(20)) {
* if (stats.jitter > 50) {
* console.warn(`High network jitter detected: ${stats.jitter}ms`)
* }
* if (stats.packetLoss > 5) {
* console.error(`Packet loss detected: ${stats.packetLoss}%`)
* }
* }
* ```
*/
rollingStats(windowSize?: number): AsyncGenerator<PingStats, void, unknown>;
/**
* Yields results only when they meet a specific condition.
*
* @param predicate Function that determines if a result should be yielded
*
* @example
* ```typescript
* // Only yield results with high latency
* for await (const slowResult of stream.filter(r =>
* r.isSuccess() && r.averageResponseTimeInMs() > 100
* )) {
* console.warn(`Slow response: ${slowResult.averageResponseTimeInMs()}ms`)
* }
* ```
*/
filter(predicate: (result: PingResult) => boolean): AsyncGenerator<PingResult, void, unknown>;
/**
* Transforms each ping result using a mapping function.
*
* @param mapper Function to transform each result
*
* @example
* ```typescript
* // Extract only latency values
* for await (const latency of stream.map(r =>
* r.isSuccess() ? r.averageResponseTimeInMs() : null
* )) {
* if (latency !== null) {
* console.log(`Latency: ${latency}ms`)
* }
* }
* ```
*/
map<T>(mapper: (result: PingResult) => T): AsyncGenerator<T, void, unknown>;
/**
* Groups results into batches and yields when a batch is full or a timeout occurs.
*
* @param batchSize Maximum number of results per batch
* @param timeoutMs Maximum time to wait before yielding a partial batch (default: 5000ms)
*
* @example
* ```typescript
* // Process results in timed batches
* for await (const batch of stream.batchWithTimeout(10, 5000)) {
* console.log(`Processing batch of ${batch.length} results`)
* await processBatch(batch)
* }
* ```
*/
batchWithTimeout(batchSize: number, timeoutMs?: number): AsyncGenerator<PingResult[], void, unknown>;
/**
* Calculates statistics from a window of ping results.
*/
private calculateStats;
}
/**
* Utility function to combine multiple async iterators into a single stream.
* Results are yielded as soon as they become available from any iterator.
*
* @param iterators Multiple async iterators to combine
*
* @example
* ```typescript
* const ping1 = new Ping('google.com').stream()
* const ping2 = new Ping('github.com').stream()
*
* for await (const result of combineAsyncIterators(ping1, ping2)) {
* console.log(`Got result from ${result.host}`)
* }
* ```
*/
declare function combineAsyncIterators<T>(...iterators: AsyncGenerator<T, void, unknown>[]): AsyncGenerator<T, void, unknown>;
export { type FailedPingResult, Ping, PingError, type PingErrorType, PingErrorUtils, PingResult, type PingResultArray, PingResultLine, type PingStats, PingStream, type SuccessfulPingResult, combineAsyncIterators };