first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
# @suaveplan/network-transport-websockets - WebSocket client utilities: typed message framing, connection lifecycle, heartbeat, backpressure queue, and injectable transport adapters
## Summary
WebSocket client utilities with typed message framing, connection lifecycle management, heartbeat, backpressure queue, and injectable transport adapters.
<!-- begin:auto-docs -->
## Documentation tree
- [adapters](./src/adapters/native-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [native-adapter](./src/adapters/native-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [error-codes](./src/error-codes.md) — Frozen error-code registry for the `@suaveplan/network-transport-websockets` package. Centralises every failure classif…
<!-- end:auto-docs --> - [native-adapter](./src/adapters/native-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Frozen error-code registry for the `@suaveplan/network-transport-websockets` package. Centralises every failure classif…
- [native-adapter](./src/adapters/native-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Frozen error-code registry for the `@suaveplan/network-transport-websockets` package. Centralises every failure classif…
- [native-adapter](./src/adapters/native-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Frozen error-code registry for the `@suaveplan/network-transport-websockets` package. Centralises every failure classif…
- [native-adapter](./src/adapters/native-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_ - [client](./src/core/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_ - [heartbeat](./src/core/heartbeat/heartbeat.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_ - [queue](./src/core/queue/queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_ - [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_ - [plugin](./src/plugin/plugin.md) — <!-- begin:auto-docs --> _(no submodules)_- [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_ - [transport](./src/transport/transport.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ - [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Frozen error-code registry for the `@suaveplan/network-transport-websockets` package. Centralises every failure classif…
## Purpose
`@suaveplan/network-transport-websockets` provides a robust, enterprise-grade WebSocket client for browser and Node.js environments. It handles connection establishment, automatic reconnection with exponential backoff, heartbeat/ping-pong keepalive, a bounded message queue for backpressure, and channel-based message multiplexing over a single WebSocket connection. All timing is injectable via a `Clock` interface for deterministic testing.
## Features
- **Connection lifecycle** — connect, disconnect, and state tracking (`disconnected` | `connecting` | `connected` | `reconnecting` | `closed`)
- **Automatic reconnect** — configurable exponential backoff with max attempts and capped delay
- **Heartbeat** — configurable ping interval and pong timeout; disconnects on missed pong
- **Message queue** — bounded queue that drops oldest messages when full, with rejections for dropped items
- **Channel multiplexing** — subscribe to named channels and receive typed messages
- **Injectable adapter** — swap the native WebSocket transport for custom implementations (tests, mocks, alternate runtimes)
- **Injectable clock** — drive timers deterministically in tests using `FakeClock`
- **Event bus integration** — emits `websocket:connected` and `websocket:disconnected` events
- **AbortSignal support** — cancel `connect()` and `send()` via `AbortSignal`
- **Structured errors** — all errors use `@suaveplan/error` with stable `WS.*` error codes
## Usage
### Basic Example
```ts
import { createWebSocketClient } from "@suaveplan/network-transport-websockets";
const client = createWebSocketClient({
url: "wss://example.com/socket",
heartbeat: { enabled: true, intervalMs: 30000, timeoutMs: 10000 },
reconnect: { enabled: true, maxAttempts: 5, initialDelayMs: 1000, maxDelayMs: 30000 },
});
await client.connect();
client.subscribe("chat", (msg, meta) => {
console.log(`[${meta.channel}] ${msg}`);
});
await client.send({ type: "chat", channel: "chat", payload: { text: "hello" } });
client.disconnect();
```
### Advanced Example
```ts
import { createWebSocketClient } from "@suaveplan/network-transport-websockets";
const client = createWebSocketClient({
url: "wss://example.com/socket",
clock: fakeClock, // injectable for tests
adapter: customAdapter, // custom WebSocket transport
eventBus: appEventBus, // emit connection events
maxQueueSize: 100,
serialization: "json",
});
client.on("connect", () => console.log("connected"));
client.on("disconnect", ({ code, reason }) => console.log("disconnected", code, reason));
client.on("reconnect", ({ attempt }) => console.log("reconnecting, attempt", attempt));
const ctrl = new AbortController();
await client.connect(ctrl.signal);
// Send with abort support
await client.send({ type: "ping", payload: null }, ctrl.signal);
```
## API Reference
See the per-module documentation in `src/`:
- `src/core/client/` — `createWebSocketClient`, `WebSocketClient`, connection lifecycle
- `src/core/heartbeat/` — `createHeartbeat`, `HeartbeatController`
- `src/core/queue/` — `MessageQueue`, bounded message queue
- `src/adapters/` — `nativeWebSocketAdapter`, `WebSocketAdapter` contract
- `src/types/` — `WebSocketClientOptions`, `WebSocketMessage`, `MessageMeta`, `Clock`
- `src/error-codes.ts` — `WS_CODES` and `WsError`
## Implementation Notes
The client uses private fields for all internal state and exposes a minimal public API. The native adapter wraps the global `WebSocket` with safe `close()` handling for reserved close codes. The heartbeat controller is fully decoupled from the client and can be tested in isolation. All timer-based behavior uses an injectable `Clock` so tests can advance time deterministically without real waits.