npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cldmv/stubborn-tcp

v1.0.4

Published

A stubbornly persistent TCP client that automatically reconnects when connections fail

Readme

@cldmv/stubborn-tcp

@cldmv/stubborn-tcp is a TCP and TLS client for Node.js that refuses to stay disconnected. When the connection drops, it reconnects on its own with exponential backoff, and it can keep a quiet link alive with TCP keep-alive and application-level heartbeats.

It is built for long-lived connections to devices and services — control protocols, telemetry feeds, line-based servers — where the remote end restarts, the network blips, and the client is expected to simply carry on. Everything is exposed through an EventEmitter, with legacy callback options kept for code ported from older clients.

A stubbornly persistent TCP/TLS client that automatically reconnects when connections fail.

npm version npm downloads GitHub downloads Last commit npm last update coverage

Contributors Sponsor shinrai


✨ What's New

Latest: v1.0.4 (October 2026)

  • TLS connections work — the tls option shadowed Node.js's tls module inside the constructor, so every TLS connection attempt threw tls.connect is not a function. tls: true now connects over TLS, and secureConnect and the TLS helper methods work as documented (#1).
  • Built package with TypeScript declarations — the code now ships as a minified dist/ bundle with .d.mts declarations; test/ and examples/ are no longer published. The package root exports are unchanged, and require() (Node.js ^20.19.0 or >=22.12.0) now works in esbuild/webpack bundles and fails with a clear ERR_REQUIRE_ESM message on older Node.js (#1).
  • Connection fixes — an unobserved socket error no longer crashes the process, failToConnect now fires for each failed attempt, open() on a connected client keeps it usable, open() without arguments uses the constructor's host and port, and connectionTimeout no longer drops idle connections (a new opt-in idleTimeout covers that) (#9, #10, #11, #13).
  • View full v1.0.4 Changelog

Recent Releases

  • v1.0.3 (October 2025) — debug: true emits debug events instead of printing to the console; zero or negative timeouts disable the socket timeout (Changelog)
  • v1.0.2 (October 2025) — data event arguments become (data, handle, instance) (Changelog)
  • v1.0.1 (October 2025) — require() returns the constructor instead of a Promise (Changelog)
  • v1.0.0 (October 2025) — Initial release: auto-reconnecting TCP client with backoff, socket tuning and heartbeats (Changelog)

📚 For complete version history and detailed release notes, see the docs/changelog/ folder.


🚀 Key Features

  • 🔄 Auto-reconnection: Intelligent exponential backoff reconnection strategy
  • 🔒 TLS/SSL Support: Full TLS encryption with certificate validation
  • 🚀 Socket Optimization: TCP keep-alive, no-delay, and timeout configuration
  • 💓 Application Heartbeat: Cycling heartbeat functions with activity-based reset
  • 🎯 Modern API: Options-based constructor with EventEmitter support
  • 📦 Dual Module Support: Works with both ESM (import) and CommonJS (require)
  • 🛡️ Production Ready: Comprehensive error handling and connection monitoring
  • 🔧 Highly Configurable: Fine-grained control over all connection parameters
  • 📝 Well Documented: Comprehensive JSDoc documentation and TypeScript declarations

📦 Installation

Requirements

  • Node.js 14.0 or higher for import (ESM).
  • require() needs Node.js ^20.19.0 or >=22.12.0 — the CommonJS entry loads the ES module build through require(esm), and throws ERR_REQUIRE_ESM with a pointer to import() on older versions.
  • Platforms: Windows, macOS, Linux.
  • Protocol Support: TCP and TLS/SSL.

Install

npm install @cldmv/stubborn-tcp

🚀 Quick Start

Passing host and port to the constructor connects immediately. Omit them and call open(host, port) when you want to connect later.

Basic TCP Connection

import StubbornTCP from "@cldmv/stubborn-tcp";
// CommonJS: const StubbornTCP = require("@cldmv/stubborn-tcp");

// Modern options-based API
const client = new StubbornTCP({
	host: "localhost",
	port: 8080,
	debug: true
});

// Event-driven approach
client.on("connect", () => console.log("Connected!"));
client.on("data", (data) => console.log("Received:", data.toString()));
client.on("disconnect", () => console.log("Disconnected, will retry..."));
client.on("debug", (message) => console.log(message));

// Send data
client.write("Hello, server!");

TLS/SSL Secure Connection

const secureClient = new StubbornTCP({
	host: "secure.example.com",
	port: 443,
	tls: true,
	tlsOptions: {
		rejectUnauthorized: true,
		servername: "secure.example.com"
	}
});

secureClient.on("secureConnect", () => {
	console.log("Secure connection established");
	console.log("Certificate:", secureClient.getPeerCertificate());
});

Advanced Configuration

const client = new StubbornTCP({
	host: "localhost",
	port: 8080,

	// Connection options
	keepAlive: true,
	keepAliveInitialDelay: 30000,
	noDelay: true,
	connectionTimeout: 5000,

	// Reconnection strategy
	autoReconnect: true,
	reconnectDelay: 1000,
	maxReconnectDelay: 30000,
	reconnectBackoffFactor: 2.0,
	maxReconnectAttempts: 10,

	// Application heartbeat
	heartbeatEnabled: true,
	heartbeatInterval: 15000,
	heartbeatFunc: [(client) => client.write("PING"), (client) => client.write("KEEPALIVE")]
});

📖 API Reference

Constructor

new StubbornTCP(options);

Configuration Options:

Connection Options

  • host (string): Host to connect to (also the default for open() called without arguments)
  • port (number): Port to connect to (also the default for open() called without arguments). With both host and port set, the client connects immediately
  • debug (boolean, default: false): Emit debug events with diagnostic messages

Socket Options

  • keepAlive (boolean, default: true): Enable TCP keep-alive
  • keepAliveInitialDelay (number, default: 60000): Keep-alive initial delay (ms)
  • noDelay (boolean, default: true): Disable Nagle's algorithm
  • connectionTimeout (number, default: 10000): Connect-phase timeout (ms). Bounds how long each connection attempt (including the TLS handshake) may take; a timed-out attempt is closed and goes through the normal reconnect path. It is cleared once connected, so it never closes an established connection. 0 or negative disables it
  • idleTimeout (number, default: 0): Idle timeout (ms) for an established connection — with no socket activity for this long, the connection is timed out and closed (and reconnected if autoReconnect is on). 0 or negative disables it (the default)

TLS/SSL Options

  • tls (boolean, default: false): Enable TLS encryption
  • tlsOptions (object, default: {}): TLS configuration (certificates, etc.)

Reconnection Options

  • autoReconnect (boolean, default: true): Enable auto-reconnection
  • reconnectDelay (number, default: 1000): Initial reconnect delay (ms)
  • maxReconnectDelay (number, default: 30000): Maximum reconnect delay (ms)
  • reconnectBackoffFactor (number, default: 1.5): Exponential backoff multiplier
  • maxReconnectAttempts (number, default: -1): Max attempts (-1 = infinite)

Heartbeat Options

  • heartbeatEnabled (boolean, default: false): Enable application heartbeat
  • heartbeatInterval (number, default: 30000): Heartbeat interval (ms)
  • heartbeatFunc (function|array): Function(s) to call for heartbeat; each receives the client instance
  • heartbeatResetOnActivity (boolean, default: true): Reset timer on activity

Legacy Callback Options

  • rxFunc (function): Data received callback
  • onConnect (function): Connection established callback
  • onDisconnect (function): Connection lost callback
  • onError (function): Error occurred callback
  • onTimeout (function): Connection timeout callback

Methods

Connection Methods

  • open(host, port) - Open connection to host:port (closes an existing connection first)
  • close() - Close connection and disable auto-reconnect
  • write(data) - Send data to remote host

Socket Configuration Methods

  • setKeepAlive(enable, initialDelay) - Configure TCP keep-alive
  • setNoDelay(noDelay) - Configure Nagle's algorithm
  • setTimeout(timeout) - Set the connect-phase timeout (connectionTimeout); re-arms a pending attempt, never affects an established connection
  • setIdleTimeout(timeout) - Set the idle timeout (idleTimeout); applies to the live connection and later ones

Heartbeat Methods

  • enableHeartbeat(options) - Enable heartbeat with options object
  • disableHeartbeat() - Disable heartbeat functionality
  • addHeartbeatFunction(functions) - Add heartbeat function(s)
  • clearHeartbeatFunctions() - Clear all heartbeat functions

TLS Methods (TLS connections only)

  • getPeerCertificate() - Get peer certificate information
  • getCipher() - Get cipher information
  • isAuthorized() - Check if connection is authorized
  • getAuthorizationError() - Get authorization error details

Utility Methods

  • resetReconnectState() - Reset reconnection attempt counter

Properties

Connection State

  • connectState (read-only): 0 = disconnected, 1 = connected
  • openState (read-only): Connection open state
  • handle (read-only): Unique connection identifier

Connection Information

  • isSecure (read-only): Whether connection uses TLS
  • localAddress (read-only): Local IP address
  • localPort (read-only): Local port number
  • remoteAddress (read-only): Remote IP address
  • remotePort (read-only): Remote port number

Events

Most events pass the connection handle and the client instance as their last two arguments.

Connection Events

  • connect (handle, instance) - Connection established
  • disconnect (handle, instance) - Connection lost
  • data (data, handle, instance) - Data received
  • error (error, handle, instance) - Error occurred (only emitted while an error listener is attached; without one, errors go to the debug event and the client keeps reconnecting instead of crashing the process)
  • failToConnect (error, handle, instance) - A connection attempt (the first one or a reconnect) failed before connect; fires once per failed attempt, before error. A timeout before connecting reports an ETIMEDOUT error
  • timeout (handle, instance) - A connection attempt timed out (connectionTimeout), or an established connection was idle for idleTimeout

Reconnection Events

  • maxReconnectAttemptsReached (attempts) - Max reconnect attempts reached

Heartbeat Events

  • heartbeat (handle, instance) - Heartbeat sent
  • heartbeatFailed (error, functionIndex, handle, instance) - Heartbeat function failed

TLS Events (TLS connections only)

  • secureConnect (handle, instance) - TLS secure connection established
  • keylog (line, handle, instance) - TLS key material generated/received
  • OCSPResponse (response, handle, instance) - OCSP response received

Debug Events

  • debug (message, handle, instance) - Diagnostic message, emitted only when debug: true

🔄 Auto-Reconnection with Exponential Backoff

StubbornTCP features intelligent reconnection with exponential backoff:

  • Initial Delay: 1000ms (configurable)
  • Maximum Delay: 30000ms (configurable)
  • Backoff Factor: 1.5x (configurable)
  • Unlimited Attempts: By default (configurable)
  • Reset on Success: Delay resets after successful connection

🧪 Advanced Examples

Complete Configuration Example

import fs from "node:fs";
import StubbornTCP from "@cldmv/stubborn-tcp";

// host and port are given, so the client connects immediately
const client = new StubbornTCP({
	// Connection settings
	host: "localhost",
	port: 8080,
	debug: true,

	// Socket optimization
	keepAlive: true,
	keepAliveInitialDelay: 60000,
	noDelay: true,
	connectionTimeout: 15000,

	// TLS encryption
	tls: true,
	tlsOptions: {
		rejectUnauthorized: false,
		cert: fs.readFileSync("client-cert.pem"),
		key: fs.readFileSync("client-key.pem"),
		ca: fs.readFileSync("ca-cert.pem")
	},

	// Reconnection strategy
	autoReconnect: true,
	reconnectDelay: 2000,
	maxReconnectDelay: 60000,
	reconnectBackoffFactor: 2.0,
	maxReconnectAttempts: 10,

	// Application heartbeat
	heartbeatEnabled: true,
	heartbeatInterval: 30000,
	heartbeatFunc: [(client) => client.write("PING\n"), (client) => console.log("💓 Heartbeat sent")]
});

// Event-driven approach
client.on("connect", (handle, instance) => {
	console.log("🔗 Connected securely");
	instance.write("Hello secure server!");
});

client.on("data", (data, handle, instance) => {
	console.log("📨 Received:", data.toString());
});

client.on("secureConnect", () => {
	const cert = client.getPeerCertificate();
	console.log("🔒 TLS Certificate:", cert.subject);
});

Heartbeat with Multiple Functions

const client = new StubbornTCP();

// Enable heartbeat with multiple functions
client.enableHeartbeat({
	interval: 15000,
	resetOnActivity: true,
	functions: [(client) => client.write("KEEPALIVE\n"), (client) => console.log("💓 Keep-alive sent"), (client) => updateLastHeartbeat()]
});

client.on("heartbeatFailed", (error) => {
	console.error("💔 Heartbeat failed:", error.message);
});

client.open("localhost", 8080);

Socket Configuration at Runtime

const client = new StubbornTCP();

client.on("connect", () => {
	// Optimize socket after connection
	client.setKeepAlive(true, 30000);
	client.setNoDelay(true);
	client.setIdleTimeout(60000);

	console.log("Socket optimized for performance");
});

client.open("api.example.com", 443);

🔁 Migration from Legacy API

The constructor only accepts an options object. Code written against the older positional constructor needs its arguments moved into options; the legacy Open() and Write() methods and the OnConnectFunc / OnDisconnectFunc properties are still available as aliases.

Old Style (Deprecated)

const client = new StubbornTCP(rxFunc, "localhost", 8080, instance, bufferSize, debug);
client.OnConnectFunc = onConnect;
client.OnDisconnectFunc = onDisconnect;
client.Open("localhost", 8080);

New Style (Recommended)

// Connects immediately because host and port are given
const client = new StubbornTCP({
	host: "localhost",
	port: 8080,
	debug,
	rxFunc,
	onConnect,
	onDisconnect
});

🛡 Error Handling

The client handles various error conditions gracefully:

  • Connection refused: Automatically retries connection
  • Network timeouts: Continues reconnection attempts
  • Server disconnection: Attempts to reconnect after delay
  • Write errors: Returns false, connection state updated

📚 Documentation

CodeFactor OpenSSF Scorecard npms.io score npm unpacked size Repo size


🤝 Contributing

  1. Fork the repository
  2. Create your feature branch off next (git checkout -b feat/amazing-feature origin/next)
  3. Commit your changes (git commit -m 'feat: add some amazing feature')
  4. Push to the branch (git push origin feat/amazing-feature)
  5. Open a Pull Request into next

Contributors Sponsor shinrai


🔗 Links


📄 License

GitHub license npm license

This project is licensed under the Apache License Version 2.0 - see the LICENSE file for details.