feat(core): Add heartbeat grace/timeout options, client retry/wait-for-ready, server readiness and socket cleanup, transport socket options, helper utilities, and tests

This commit is contained in:
2025-08-25 13:37:31 +00:00
parent e3c1d35895
commit dd25ffd3e4
9 changed files with 780 additions and 30 deletions

View File

@@ -22,6 +22,10 @@ export interface IIpcChannelOptions extends IIpcTransportOptions {
heartbeatInterval?: number;
/** Heartbeat timeout in ms */
heartbeatTimeout?: number;
/** Initial grace period before heartbeat timeout in ms */
heartbeatInitialGracePeriodMs?: number;
/** Throw on heartbeat timeout (default: true, set false to emit event instead) */
heartbeatThrowOnTimeout?: boolean;
}
/**
@@ -46,6 +50,7 @@ export class IpcChannel<TRequest = any, TResponse = any> extends plugins.EventEm
private heartbeatTimer?: NodeJS.Timeout;
private heartbeatCheckTimer?: NodeJS.Timeout;
private lastHeartbeat: number = Date.now();
private connectionStartTime: number = Date.now();
private isReconnecting = false;
private isClosing = false;
@@ -203,6 +208,7 @@ export class IpcChannel<TRequest = any, TResponse = any> extends plugins.EventEm
this.stopHeartbeat();
this.lastHeartbeat = Date.now();
this.connectionStartTime = Date.now();
// Send heartbeat messages
this.heartbeatTimer = setInterval(() => {
@@ -214,9 +220,25 @@ export class IpcChannel<TRequest = any, TResponse = any> extends plugins.EventEm
// Check for heartbeat timeout
this.heartbeatCheckTimer = setInterval(() => {
const timeSinceLastHeartbeat = Date.now() - this.lastHeartbeat;
const timeSinceConnection = Date.now() - this.connectionStartTime;
const gracePeriod = this.options.heartbeatInitialGracePeriodMs || 0;
// Skip timeout check during initial grace period
if (timeSinceConnection < gracePeriod) {
return;
}
if (timeSinceLastHeartbeat > this.options.heartbeatTimeout!) {
this.emit('error', new Error('Heartbeat timeout'));
this.transport.disconnect().catch(() => {});
const error = new Error('Heartbeat timeout');
if (this.options.heartbeatThrowOnTimeout !== false) {
// Default behavior: emit error which may cause disconnect
this.emit('error', error);
this.transport.disconnect().catch(() => {});
} else {
// Emit heartbeatTimeout event instead of error
this.emit('heartbeatTimeout', error);
}
}
}, this.options.heartbeatTimeout! / 2);
}