UNPKG

ts-ping

Version:

A modern TypeScript library for performing ICMP ping operations with type-safe results and fluent configuration.

1,098 lines (854 loc) 32.8 kB
# ts-ping [![npm version](https://badge.fury.io/js/ts-ping.svg)](https://badge.fury.io/js/ts-ping) [![CI](https://github.com/hammeradam/ts-ping/workflows/CI/badge.svg)](https://github.com/hammeradam/ts-ping/actions) [![codecov](https://codecov.io/gh/hammeradam/ts-ping/branch/main/graph/badge.svg)](https://codecov.io/gh/hammeradam/ts-ping) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) A modern TypeScript library for performing ICMP ping operations with type-safe results and fluent configuration. ## Features - **Type-Safe**: Built with TypeScript and discriminated union types for reliable type checking - **Fluent Interface**: Chainable methods for easy configuration - **Cross-Platform**: Works on Windows, macOS, and Linux with platform-specific optimizations - **IPv4/IPv6 Support**: Full dual-stack support with automatic IPv6 detection and platform-specific command handling - **AbortSignal Support**: External cancellation with standard `AbortSignal` API for graceful operation control - **Comprehensive Results**: Detailed ping statistics including packet loss, timing, and error information - **Streaming Support**: Real-time ping monitoring with async generators and advanced utilities - **Live Statistics**: Rolling statistics calculation with jitter, packet loss, and performance metrics - **Memory Efficient**: Generator-based streaming that doesn't load all results into memory - **Zero Dependencies**: No external dependencies - lightweight and secure - **Well-Tested**: 90%+ test coverage with comprehensive test suite - **Modern**: Uses ES modules and modern JavaScript features ## Installation ```bash # Using pnpm (recommended) pnpm add ts-ping # Using npm npm install ts-ping # Using yarn yarn add ts-ping ``` ## Quick Start ```typescript import { Ping } from 'ts-ping' const ping = new Ping('google.com').setCount(3).setTimeout(3) const result = ping.run() if (result.isSuccess()) { console.log(`Successfully pinged ${result.host}`) console.log( `Packets: ${result.numberOfPacketsTransmitted} sent, ${result.numberOfPacketsReceived} received`, ) console.log(`Packet loss: ${result.packetLossPercentage}%`) } else if (result.isFailure()) { console.error(`Ping failed: ${result.error}`) console.error(`Host: ${result.host || 'unknown'}`) } ``` ## Streaming Support The library provides powerful async generator-based streaming for real-time ping monitoring: ```typescript import { Ping, PingStream } from 'ts-ping' // Basic streaming - continuous ping monitoring const ping = new Ping('google.com').setInterval(0.5) // Ping every 500ms for await (const result of ping.stream()) { console.log(`${result.host}: ${result.isSuccess() ? result.averageResponseTimeInMs() + 'ms' : 'failed'}`) // Break after 10 results or run indefinitely if (someCondition) break } ``` ### Stream Processing with PingStream ```typescript import { PingStream } from 'ts-ping' const ping = new Ping('example.com').setInterval(0.5) const stream = new PingStream(ping) // Get rolling statistics every 10 pings for await (const stats of stream.rollingStats(10)) { console.log(`Avg: ${stats.average.toFixed(1)}ms`) console.log(`Jitter: ${stats.jitter.toFixed(1)}ms`) console.log(`Packet Loss: ${stats.packetLoss.toFixed(1)}%`) console.log(`Std Dev: ${stats.standardDeviation.toFixed(1)}ms`) } ``` ### Advanced Streaming Examples ```typescript // Take only the first 5 successful pings for await (const result of stream.skipFailures().take(5)) { console.log(`Success: ${result.averageResponseTimeInMs()}ms`) } // Monitor only failures for alerting for await (const failure of stream.skipSuccesses()) { console.error(`Ping failed: ${failure.error}`) await sendAlert(failure) } // Process in sliding windows of 3 results for await (const window of stream.window(3)) { const avgLatency = window .filter(r => r.isSuccess()) .map(r => r.averageResponseTimeInMs()) .reduce((a, b) => a + b, 0) / window.length console.log(`Window average: ${avgLatency}ms`) } // Batch results with timeout for await (const batch of stream.batchWithTimeout(5, 2000)) { console.log(`Processed batch of ${batch.length} results`) } // External cancellation with AbortSignal const abortController = new AbortController() const cancelablePing = new Ping('google.com') .setCount(0) // infinite .setInterval(1) .setAbortSignal(abortController.signal) // Cancel after 30 seconds setTimeout(() => abortController.abort(), 30000) for await (const result of cancelablePing.stream()) { console.log(`Ping: ${result.isSuccess() ? result.averageResponseTimeInMs() + 'ms' : 'failed'}`) // Will automatically stop when aborted } ``` ## Async Support The library supports both synchronous and asynchronous execution: ```typescript import { Ping } from 'ts-ping' // Synchronous (blocks until complete) const result = new Ping('google.com').run() // Asynchronous (returns a Promise) const asyncResult = await new Ping('google.com').runAsync() // Error handling with async try { const result = await new Ping('example.com').runAsync() console.log('Ping completed:', result.isSuccess()) } catch (error) { console.error('Ping failed with error:', error) } ``` ## API Reference ### Ping Class #### Constructor ```typescript new Ping( hostname: string, timeoutInSeconds?: number, count?: number, intervalInSeconds?: number, packetSizeInBytes?: number, ttl?: number ) ``` **Parameters:** - `hostname` - The target hostname or IP address to ping - `timeoutInSeconds` - Timeout for each ping in seconds (default: 5) - `count` - Number of ping packets to send (default: 1) - `intervalInSeconds` - Interval between pings in seconds (default: 1.0) - `packetSizeInBytes` - Size of ping packets in bytes (default: 56) - `ttl` - Time To Live value (default: 64) #### Fluent Interface Methods All methods return `this` for method chaining: ```typescript ping.setTimeout(10) // Set timeout in seconds ping.setCount(5) // Set number of pings ping.setInterval(2.0) // Set interval between pings ping.setPacketSize(128) // Set packet size in bytes ping.setTtl(32) // Set Time To Live ping.setIPVersion(4) // Set IP version (4 or 6) ping.setIPv4() // Force IPv4 (convenience method) ping.setIPv6() // Force IPv6 (convenience method) ping.setAbortSignal(signal) // Set AbortSignal for cancellation ``` #### IPv4/IPv6 Support Control which IP version to use for ping operations. ts-ping automatically detects IPv6 addresses and uses the appropriate ping command on all platforms. ```typescript // Auto-detection: IPv6 addresses are automatically detected const ping6Auto = new Ping('2001:4860:4860::8888') // Auto-detects IPv6 const result6Auto = ping6Auto.run() // Uses ping6 on macOS, ping -6 on Linux/Windows // IPv4 addresses and hostnames use system default const ping4 = new Ping('8.8.8.8') // No auto-detection, uses system default const pingHost = new Ping('google.com') // Uses system default // Force IPv4 (overrides auto-detection) const ping4Force = new Ping('google.com').setIPv4() const result4 = ping4Force.run() // Force IPv6 (overrides auto-detection) const ping6Force = new Ping('google.com').setIPv6() const result6 = ping6Force.run() // Or use setIPVersion method const ping = new Ping('google.com').setIPVersion(6) // Works with all other options const result = new Ping('2001:4860:4860::8888') // Auto-detects IPv6 .setCount(3) .setTimeout(5) .run() if (result.isSuccess()) { console.log(`IPv${result.ipVersion} ping successful`) console.log(`Host: ${result.host}`) } ``` **Auto-Detection Behavior:** - **IPv6 addresses**: Automatically detected and use appropriate IPv6 ping command - **IPv4 addresses**: Use system default ping command (no version forcing) - **Hostnames**: Use system default ping command (no version forcing) - **Explicit methods**: `setIPv4()` and `setIPv6()` override auto-detection **Platform Support:** - **macOS**: Uses `ping` for IPv4 and `ping6` for IPv6 - **Linux/Windows**: Uses `ping -4` for IPv4 and `ping -6` for IPv6 - **Default**: When no IP version is specified, uses the system default (usually IPv4) **Streaming with IP Version:** ```typescript // Monitor IPv6 connectivity const ping = new Ping('ipv6.google.com').setIPv6().setInterval(1) for await (const result of ping.stream()) { if (result.isSuccess()) { console.log(`IPv6 ping: ${result.averageResponseTimeInMs()}ms`) } else { console.log(`IPv6 ping failed: ${result.error}`) } } ``` #### AbortSignal Support Control ping operations with external cancellation using the standard `AbortSignal` API: ```typescript // Manual cancellation with AbortController const abortController = new AbortController() const ping = new Ping('google.com') .setCount(0) // infinite pings .setInterval(1) .setAbortSignal(abortController.signal) // Cancel after 10 seconds setTimeout(() => abortController.abort(), 10000) try { for await (const result of ping.stream()) { console.log(`Ping: ${result.averageTimeInMs ?? 'failed'}ms`) } } catch (error) { console.log('Ping cancelled:', error.message) } ``` ```typescript // Timeout-based cancellation with AbortSignal.timeout() const ping = new Ping('google.com') .setCount(0) // infinite pings .setInterval(0.5) .setAbortSignal(AbortSignal.timeout(5000)) // Cancel after 5 seconds for await (const result of ping.stream()) { console.log(`Ping: ${result.averageTimeInMs ?? 'failed'}ms`) // Automatically stops after 5 seconds } ``` **AbortSignal Features:** - **External cancellation**: Stop ping operations from outside the ping logic - **Graceful shutdown**: Operations stop cleanly without throwing errors - **Standard API**: Uses the same pattern as `fetch()` and other modern APIs - **Multiple sources**: Works with `AbortController`, `AbortSignal.timeout()`, or any `AbortSignal` - **All methods supported**: Works with `runAsync()`, `stream()`, `streamWithFilter()`, and `streamBatched()` #### Method Chaining Example ```typescript const result = new Ping('example.com') .setTimeout(10) .setCount(5) .setInterval(1.5) .setPacketSize(128) .setIPv6() .run() ``` #### run() Executes the ping command synchronously and returns a `PingResult`. ```typescript const result = ping.run() ``` #### runAsync() Executes the ping command asynchronously and returns a `Promise<PingResult>`. ```typescript const result = await ping.runAsync() // With error handling try { const result = await ping.runAsync() if (result.isSuccess()) { console.log('Ping successful!') } } catch (error) { console.error('Ping failed:', error) } ``` **Benefits of async:** - Non-blocking execution - Better for multiple concurrent pings - Integrates well with async/await patterns - Proper timeout handling with Promise rejection #### stream() Creates an async generator that yields ping results continuously: ```typescript // Infinite stream (count = 0) const ping = new Ping('google.com').setCount(Infinity).setInterval(1) for await (const result of ping.stream()) { console.log(result.isSuccess() ? 'Success' : 'Failed') if (shouldStop) break } // Finite stream const ping = new Ping('google.com').setCount(5).setInterval(0.5) for await (const result of ping.stream()) { console.log(`Result ${result.host}: ${result.isSuccess()}`) } ``` #### streamWithFilter() Creates a filtered and optionally transformed stream: ```typescript // Filter successful pings and get latencies const latencies = ping.streamWithFilter( result => result.isSuccess(), result => result.averageResponseTimeInMs() ) for await (const latency of latencies) { console.log(`Latency: ${latency}ms`) } // Filter failures and get error messages const errors = ping.streamWithFilter( result => result.isFailure(), result => result.error ) for await (const error of errors) { console.error(`Error: ${error}`) } ``` #### streamBatched() Creates a stream that yields arrays of results in batches: ```typescript const ping = new Ping('google.com').setCount(10).setInterval(0.2) for await (const batch of ping.streamBatched(3)) { console.log(`Batch of ${batch.length} results:`) batch.forEach(result => { console.log(` ${result.host}: ${result.isSuccess()}`) }) } ``` ### PingStream Advanced streaming utilities for processing ping results: ```typescript import { PingStream } from 'ts-ping' const ping = new Ping('example.com').setInterval(0.5) const stream = new PingStream(ping) ``` #### take(n) Limits the stream to the first N results: ```typescript // Get exactly 10 results for await (const result of stream.take(10)) { console.log(result.isSuccess()) } ``` #### skipFailures() / skipSuccesses() Filter results by success status: ```typescript // Only successful pings for await (const success of stream.skipFailures()) { console.log(`Success: ${success.averageResponseTimeInMs()}ms`) } // Only failed pings for await (const failure of stream.skipSuccesses()) { console.error(`Failed: ${failure.error}`) } ``` #### window(size) Creates a sliding window of results: ```typescript // Process results in windows of 5 for await (const window of stream.window(5)) { const successRate = window.filter(r => r.isSuccess()).length / window.length console.log(`Success rate: ${(successRate * 100).toFixed(1)}%`) } ``` #### rollingStats(windowSize) Calculates rolling statistics over a window of results: ```typescript for await (const stats of stream.rollingStats(20)) { console.log(`Average: ${stats.average.toFixed(1)}ms`) console.log(`Jitter: ${stats.jitter.toFixed(1)}ms`) console.log(`Packet Loss: ${stats.packetLoss.toFixed(1)}%`) console.log(`Std Dev: ${stats.standardDeviation.toFixed(1)}ms`) console.log(`Min/Max: ${stats.minimum}ms/${stats.maximum}ms`) console.log(`Count: ${stats.count}`) console.log(`Timestamp: ${stats.timestamp}`) } ``` #### filter(predicate) / map(transform) Standard functional programming operations: ```typescript // Filter and transform const highLatencies = stream .filter(result => result.isSuccess() && result.averageResponseTimeInMs() > 100) .map(result => ({ host: result.host, latency: result.averageResponseTimeInMs(), timestamp: new Date() })) for await (const data of highLatencies) { console.log(`High latency detected: ${data.latency}ms`) } ``` #### batchWithTimeout(batchSize, timeoutMs) Batches results by size or timeout: ```typescript // Batch up to 5 results or every 2 seconds for await (const batch of stream.batchWithTimeout(5, 2000)) { console.log(`Processing batch of ${batch.length} results`) const avgLatency = batch .filter(r => r.isSuccess()) .map(r => r.averageResponseTimeInMs()) .reduce((sum, lat) => sum + lat, 0) / batch.length console.log(`Batch average: ${avgLatency}ms`) } ``` ### PingStats Interface Rolling statistics provided by `rollingStats()`: ```typescript interface PingStats { count: number // Number of successful pings average: number // Average response time in ms minimum: number // Minimum response time in ms maximum: number // Maximum response time in ms standardDeviation: number // Standard deviation of response times jitter: number // Network jitter (variance in response times) packetLoss: number // Packet loss percentage (0-100) timestamp: Date // When the stats were calculated } ``` ### combineAsyncIterators() Utility function to merge multiple async iterators: ```typescript import { combineAsyncIterators } from 'ts-ping' const stream1 = new Ping('google.com').stream() const stream2 = new Ping('github.com').stream() const combined = combineAsyncIterators(stream1, stream2) for await (const result of combined) { console.log(`${result.host}: ${result.isSuccess()}`) } ``` ### PingResult The result object uses discriminated unions for type safety. Use type guards to access specific properties: #### Successful Results ```typescript if (result.isSuccess()) { // TypeScript knows these properties are available and non-null console.log(result.host) // string console.log(result.numberOfPacketsTransmitted) // number console.log(result.numberOfPacketsReceived) // number console.log(result.packetLossPercentage) // number console.log(result.averageTimeInMs) // number | null console.log(result.minimumTimeInMs) // number | null console.log(result.maximumTimeInMs) // number | null } ``` #### Failed Results ```typescript if (result.isFailure()) { // TypeScript knows the error property is available console.log(result.error) // PingErrorType console.log(result.host) // string | null console.log(result.packetLossPercentage) // 100 } ``` #### Common Properties Available on both success and failure results: ```typescript result.rawOutput // string - full ping command output result.lines // PingResultLine[] - parsed ping response lines result.timeoutInSeconds // number | null result.intervalInSeconds // number result.packetSizeInBytes // number result.ttl // number result.ipVersion // 4 | 6 | undefined - IP version used for the ping ``` #### IP Version Information When IPv4 or IPv6 is explicitly set, the result includes the IP version: ```typescript const ping4 = new Ping('google.com').setIPv4() const result4 = ping4.run() if (result4.isSuccess()) { console.log(`Used IPv${result4.ipVersion}`) // "Used IPv4" } const ping6 = new Ping('google.com').setIPv6() const result6 = ping6.run() if (result6.isSuccess()) { console.log(`Used IPv${result6.ipVersion}`) // "Used IPv6" } // When no IP version is specified, ipVersion is undefined const pingDefault = new Ping('google.com') const resultDefault = pingDefault.run() console.log(resultDefault.ipVersion) // undefined ``` ### Error Types ```typescript type PingErrorType = | 'HostnameNotFound' | 'HostUnreachable' | 'PermissionDenied' | 'Timeout' | 'UnknownError' ``` ### PingResultLine Individual ping response lines with parsed timing information: ```typescript line.getRawLine() // string - original ping output line line.getTimeInMs() // number - parsed response time in milliseconds line.toArray() // { line: string, time_in_ms: number } ``` ## Examples ### Basic Ping ```typescript import { Ping } from 'ts-ping' const result = new Ping('google.com').run() if (result.isSuccess()) { console.log('Ping successful!') } else { console.log('Ping failed:', result.error) } ``` ### Async Ping ```typescript import { Ping } from 'ts-ping' async function pingExample() { try { const result = await new Ping('google.com').runAsync() if (result.isSuccess()) { console.log('Async ping successful!') console.log(`Average time: ${result.averageResponseTimeInMs()}ms`) } else { console.log('Async ping failed:', result.error) } } catch (error) { console.error('Ping threw an error:', error) } } pingExample() ``` ### Real-time Network Monitoring ```typescript import { Ping, PingStream } from 'ts-ping' async function networkMonitor() { const ping = new Ping('google.com').setInterval(0.5) // Ping every 500ms const stream = new PingStream(ping) // Monitor with rolling statistics for await (const stats of stream.rollingStats(10)) { console.clear() console.log('Network Monitor - Last 10 pings:') console.log(`Average Latency: ${stats.average.toFixed(1)}ms`) console.log(`Jitter: ${stats.jitter.toFixed(1)}ms`) console.log(`Packet Loss: ${stats.packetLoss.toFixed(1)}%`) console.log(`Min/Max: ${stats.minimum}ms/${stats.maximum}ms`) console.log(`Timestamp: ${stats.timestamp.toLocaleTimeString()}`) // Alert on high latency if (stats.average > 100) { console.log('WARNING: High latency detected!') } // Alert on packet loss if (stats.packetLoss > 5) { console.log('ALERT: Packet loss detected!') } } } networkMonitor() ``` ### Streaming with Filtering ```typescript import { Ping, PingStream } from 'ts-ping' async function monitorFailures() { const ping = new Ping('example.com').setInterval(1) const stream = new PingStream(ping) console.log('Monitoring for failures...') // Only process failures for alerting for await (const failure of stream.skipSuccesses().take(5)) { console.error(`Ping failed: ${failure.error}`) console.error(` Host: ${failure.host}`) console.error(` Time: ${new Date().toISOString()}`) // Send alert (example) await sendSlackAlert(`Ping to ${failure.host} failed: ${failure.error}`) } } async function sendSlackAlert(message: string) { // Implementation would send to Slack/Discord/etc console.log(`Alert: ${message}`) } monitorFailures() ``` ### Batched Processing ```typescript import { Ping, PingStream } from 'ts-ping' async function batchProcessor() { const ping = new Ping('github.com').setInterval(0.2) const stream = new PingStream(ping) // Process in batches of 5 or every 3 seconds for await (const batch of stream.batchWithTimeout(5, 3000)) { console.log(`\nProcessing batch of ${batch.length} results:`) const successful = batch.filter(r => r.isSuccess()) const failed = batch.filter(r => r.isFailure()) console.log(`Successful: ${successful.length}`) console.log(`Failed: ${failed.length}`) if (successful.length > 0) { const avgLatency = successful .map(r => r.averageResponseTimeInMs()) .reduce((sum, lat) => sum + lat, 0) / successful.length console.log(`Average latency: ${avgLatency.toFixed(1)}ms`) } // Save to database, send metrics, etc. await saveToDatabase(batch) } } async function saveToDatabase(batch: any[]) { console.log(`Saved ${batch.length} results to database`) } batchProcessor() ``` ### Multi-host Monitoring ```typescript import { Ping, PingStream, combineAsyncIterators } from 'ts-ping' async function multiHostMonitor() { const hosts = ['google.com', 'github.com', 'stackoverflow.com'] // Create streams for each host const streams = hosts.map(host => new Ping(host).setInterval(1).stream() ) // Combine all streams into one const combined = combineAsyncIterators(...streams) console.log('Monitoring multiple hosts...') for await (const result of combined) { const status = result.isSuccess() ? `${result.averageResponseTimeInMs()}ms` : `${result.error}` console.log(`${result.host}: ${status}`) // Take only first 20 results total if (Math.random() > 0.9) break // Example break condition } } multiHostMonitor() ``` ### Advanced Filtering and Transformation ```typescript import { Ping, PingStream } from 'ts-ping' async function advancedProcessing() { const ping = new Ping('example.com').setInterval(0.5) const stream = new PingStream(ping) // Chain multiple operations const processedStream = stream .filter(result => result.isSuccess()) // Only successful pings .map(result => ({ host: result.host, latency: result.averageResponseTimeInMs(), timestamp: new Date(), quality: result.averageResponseTimeInMs() < 50 ? 'excellent' : result.averageResponseTimeInMs() < 100 ? 'good' : 'poor' })) .take(10) // Only process first 10 successful pings for await (const data of processedStream) { console.log(`${data.host}: ${data.latency}ms (${data.quality})`) } } advancedProcessing() ``` ### Multiple Concurrent Pings ```typescript import { Ping } from 'ts-ping' async function pingMultipleHosts() { const hosts = ['google.com', 'github.com', 'stackoverflow.com'] const promises = hosts.map(host => new Ping(host).setTimeout(5).runAsync() ) try { const results = await Promise.all(promises) results.forEach((result, index) => { const host = hosts[index] if (result.isSuccess()) { console.log(`${host}: ${result.averageResponseTimeInMs()}ms`) } else { console.log(`${host}: ${result.error}`) } }) } catch (error) { console.error('One or more pings failed:', error) } } pingMultipleHosts() ``` ### Advanced Configuration ```typescript import { Ping } from 'ts-ping' const ping = new Ping('example.com') .setTimeout(10) // 10 second timeout .setCount(5) // Send 5 pings .setInterval(2.0) // 2 second interval .setPacketSize(128) // 128 byte packets .setTtl(32) // TTL of 32 const result = ping.run() if (result.isSuccess()) { console.log(`Host: ${result.host}`) console.log(`Packets sent: ${result.numberOfPacketsTransmitted}`) console.log(`Packets received: ${result.numberOfPacketsReceived}`) console.log(`Packet loss: ${result.packetLossPercentage}%`) if (result.averageTimeInMs) { console.log(`Average time: ${result.averageTimeInMs}ms`) } if (result.minimumTimeInMs && result.maximumTimeInMs) { console.log(`Time range: ${result.minimumTimeInMs}ms - ${result.maximumTimeInMs}ms`) } } else { console.error(`Ping failed with error: ${result.error}`) } ``` ### Processing Individual Lines ```typescript import { Ping } from 'ts-ping' const result = new Ping('google.com').setCount(3).run() if (result.isSuccess()) { console.log('Individual ping times:') result.lines.forEach((line, index) => { console.log(`${index + 1}: ${line.getTimeInMs()}ms`) }) } ``` ### Error Handling ```typescript import { Ping, PingError } from 'ts-ping' const result = new Ping('nonexistent.example.com').run() if (result.isFailure()) { switch (result.error) { case PingError.HostnameNotFound: console.error('Hostname could not be resolved') break case PingError.HostUnreachable: console.error('Host is unreachable') break case PingError.PermissionDenied: console.error('Permission denied - try running as administrator') break case PingError.Timeout: console.error('Ping timed out') break default: console.error('Unknown error occurred') } } ``` ## Platform Support ### Windows - Uses `-n` instead of `-c` for ping count - Uses `-w` (lowercase) for timeout in milliseconds - Uses `-l` for packet size instead of `-s` - Uses `-i` for TTL instead of `-t` - Does not support custom intervals (handled by streaming interval timing) ### macOS - Uses milliseconds for timeout values (`-W 5000`) ### Linux - Uses seconds for timeout values (`-W 5`) The library automatically detects the platform and adjusts command parameters accordingly. ## TypeScript Integration This library is built with TypeScript and provides excellent type safety: ```typescript import { FailedPingResult, Ping, PingResult, PingStream, SuccessfulPingResult } from 'ts-ping' function handlePingResult(result: PingResult) { if (result.isSuccess()) { // result is automatically narrowed to SuccessfulPingResult const host: string = result.host // string const transmitted: number = result.numberOfPacketsTransmitted // number } else { // result is automatically narrowed to FailedPingResult const error: PingErrorType = result.error // PingErrorType const loss: 100 = result.packetLossPercentage // exactly 100 } } // Streaming types are also fully typed async function typedStreaming() { const ping = new Ping('example.com') const stream = new PingStream(ping) // Async generators are properly typed const results: AsyncGenerator<PingResult> = stream.take(5) const stats: AsyncGenerator<PingStats> = stream.rollingStats(10) const latencies: AsyncGenerator<number> = stream .filter(r => r.isSuccess()) .map(r => r.averageResponseTimeInMs()) } ``` ## Development ### Building ```bash pnpm run build ``` ### Testing ```bash pnpm test # Run all tests pnpm run test:watch # Run tests in watch mode pnpm run test:coverage # Run tests with coverage ``` ### Linting ```bash pnpm run lint # Check for linting issues pnpm run lint:fix # Fix linting issues automatically ``` ## Requirements - Node.js 20+ - pnpm 8+ (recommended) or npm/yarn - TypeScript 5+ - macOS, Linux, or Windows (ping command must be available) ## License MIT License - see LICENSE file for details. ## Contributing Contributions are welcome! Please read the contributing guidelines and ensure all tests pass before submitting a pull request. ## Changelog ### v1.5.0 (2025-08-06) - **AbortSignal Support**: Added external cancellation control with standard `AbortSignal` API - **New Method**: Added `setAbortSignal(signal)` for fluent interface configuration - **Graceful Cancellation**: Operations stop cleanly without throwing errors when aborted - **Universal Support**: Works with `runAsync()`, `stream()`, `streamWithFilter()`, and `streamBatched()` - **Multiple Sources**: Compatible with `AbortController`, `AbortSignal.timeout()`, or any `AbortSignal` - **Modern API Pattern**: Follows the same pattern as `fetch()` and other modern JavaScript APIs - **Comprehensive Testing**: 13 new tests covering all abort scenarios and edge cases - **Enhanced Documentation**: Added AbortSignal section with usage examples and API reference - **Integration Examples**: Demonstrated timeout-based and manual cancellation patterns ### v1.4.0 (2025-07-28) - **IPv6 Auto-Detection**: Automatically detects IPv6 addresses and uses appropriate ping commands on all platforms - **macOS IPv6 Fix**: Resolves IPv6 ping failures on macOS by auto-selecting `ping6` command for IPv6 addresses - **Smart IP Detection**: Uses Node.js `isIPv6()` to intelligently detect IPv6 addresses without manual configuration - **Backward Compatible**: All existing code continues to work; auto-detection only applies to IPv6 addresses - **Override Support**: `setIPv4()` and `setIPv6()` methods can still override auto-detection when needed - **Enhanced Testing**: Added 8 new test cases covering IPv6 auto-detection scenarios and edge cases - **Updated Documentation**: Enhanced IPv4/IPv6 section with auto-detection examples and behavior explanation - **Example Updates**: Added IPv6 auto-detection demonstration to existing examples ### v1.3.0 (2025-07-28) - **IPv4/IPv6 Support**: Full dual-stack networking support with platform-specific command handling - **New IP Version Methods**: Added `setIPVersion(4|6)`, `setIPv4()`, and `setIPv6()` for explicit IP version control - **Enhanced Results**: Added `ipVersion` property to `PingResult` with IP version information - **Platform-Specific Commands**: - macOS: Uses `ping` for IPv4 and `ping6` for IPv6 - Linux/Windows: Uses `ping -4` for IPv4 and `ping -6` for IPv6 - **Streaming IP Support**: IP version information preserved in streaming results - **Comprehensive Testing**: 30+ new tests covering all IPv4/IPv6 platform scenarios - **Enhanced Documentation**: New IPv4/IPv6 section with usage examples and API reference ### v1.2.0 (2025-07-28) - **Enhanced Windows Support**: Improved Windows compatibility with correct ping command arguments - **Platform-Specific Commands**: Windows uses `-n` for count, `-w` for timeout, `-l` for packet size, `-i` for TTL - **Code Cleanup**: Removed non-existent `-O` flag functionality - **Comprehensive Testing**: Added dedicated Windows support tests with platform detection - **Updated Documentation**: Clarified platform-specific ping command differences ### v1.1.0 (2025-07-28) - **New Streaming Support**: Added async generator-based streaming for real-time ping monitoring - **PingStream Utilities**: Advanced stream processing with filtering, mapping, windowing, and statistics - **Rolling Statistics**: Live calculation of latency, jitter, packet loss, and performance metrics - **Memory Efficient**: Generator-based streaming that doesn't load all results into memory - **Windows Support**: Full Windows compatibility with platform-specific ping command handling - **Type-Safe Streams**: Full TypeScript support for async generators and streaming operations - **Stream Utilities**: `combineAsyncIterators`, batching, filtering, and transformation utilities - **Enhanced Documentation**: Comprehensive examples for streaming and real-time monitoring - **Improved Coverage**: Test coverage increased to 92%+ with extensive streaming tests ### v1.0.0 (2025-07-28) - Initial release with TypeScript support - Discriminated union types for type-safe results - Fluent interface for configuration - Cross-platform support (Windows/macOS/Linux) - Async support with `runAsync()` method - Comprehensive test suite with 94%+ coverage - Complete documentation with examples - Security policy and contribution guidelines