@aellopus/client
v1.0.0
Published
Official Node.js and Bun client for Aellopus, speaking the Aellopus Wire Protocol (AWP/1) over TCP.
Maintainers
Readme
@aellopus/client
The official Node.js and Bun client for Aellopus, a key–value server speaking the Aellopus Wire Protocol (AWP/1) over TCP.
- Zero runtime dependencies — only Node/Bun built-ins (
node:net). - ESM-only, TypeScript-first — ships type declarations; runs on Node 18+ and Bun.
- Binary-safe — values are bytes; keys and values may contain CR, LF, or NUL.
- Promise-based with per-call timeouts and
AbortSignalcancellation. - Pipelining-safe — one connection is shared across concurrent callers, replies correlated in FIFO order.
Validated against the AWP/1 conformance vectors and end-to-end against the reference Rust server.
Install
npm install @aellopus/client
# or: pnpm add @aellopus/client / yarn add @aellopus/client / bun add @aellopus/clientQuick start
import { connect, NotFoundError } from '@aellopus/client';
const client = await connect('127.0.0.1:6969');
try {
await client.set('greeting', 'hello world');
console.log(await client.getString('greeting')); // "hello world"
await client.set('session:42', 'token', { expiresSeconds: 60 });
console.log((await client.ttl('session:42')).seconds); // ~60
try {
await client.get('missing');
} catch (err) {
if (err instanceof NotFoundError) console.log('no such key');
}
} finally {
await client.close();
}A Client is safe to share across concurrent work — hold one rather than pooling.
Running a server
The client talks to an Aellopus server. The quickest way to get one for local development:
docker run --rm -p 6969:6969 adibhauzan/aellopus:latestThe server listens on 127.0.0.1:6969 by default. See the server repository for other options.
API
connect(addr, options?): Promise<Client>
Opens a client to addr, a host:port TCP address with no scheme (IPv6 hosts are bracketed, e.g. [::1]:6969). options ({ timeoutMs?, signal? }) bound establishing the connection only.
Values
| Method | Returns | Notes |
| --- | --- | --- |
| get(key) | Promise<Uint8Array> | Throws NotFoundError on a miss. An empty value is a present zero-length array. |
| getString(key) | Promise<string> | UTF-8 convenience over get. |
| set(key, value, options?) | Promise<void> | value is Uint8Array \| string; options.expiresSeconds attaches a TTL. |
| del(key) | Promise<boolean> | true if the key existed. |
| exists(key) | Promise<boolean> | |
| incr(key) / decr(key) | Promise<number> | Post-operation value. |
TTL
| Method | Returns | Notes |
| --- | --- | --- |
| ttl(key) | Promise<TTL> | Throws NotFoundError if the key is absent. |
| expire(key, seconds) | Promise<boolean> | true if the key existed. |
TTL exposes hasExpiry: boolean and seconds: number \| undefined (undefined when the key persists with no expiry).
Keys & scanning
| Method | Returns | Notes |
| --- | --- | --- |
| keys(pattern) | Promise<string[]> | Glob match; may block the server on a large keyspace. |
| scanPage(cursor, options?) | Promise<ScanResult> | One page. Pass '' to begin; options accepts match / count. |
| scan(options?) | AsyncIterableIterator<string> | Incremental walk; hides cursor bookkeeping. |
for await (const key of client.scan({ match: 'user:*' })) {
console.log(key);
}Health & metadata
| Method | Returns |
| --- | --- |
| ping() | Promise<void> |
| version() | Promise<string> — server semver |
| stats() | Promise<string> — server stats text |
Maintenance & lifecycle
| Method | Returns |
| --- | --- |
| flushAll() | Promise<void> — remove every key |
| close() | Promise<void> — idempotent |
Per-call options
Every method takes an optional final options argument:
const ac = new AbortController();
const value = await client.get('key', { timeoutMs: 250, signal: ac.signal });A timed-out or aborted call rejects but leaves the connection usable — its eventual reply is discarded and later calls stay correctly correlated.
Errors
All thrown errors extend AellopusError, so you can catch the family and discriminate the case:
| Error | Meaning | Recoverable? |
| --- | --- | --- |
| NotFoundError | Key absent (get, getString, ttl) | Yes |
| ServerError | Well-formed server error; has .code (e.g. NOTINT, TTLRANGE) | Yes |
| TimeoutError | Call exceeded timeoutMs | Yes |
| ProtocolError | Malformed or wrong-shaped reply | No — connection is closed |
| ClosedError | Client closed or connection ended | No — reconnect |
A cancelled call rejects with a Web-standard DOMException named AbortError.
Compatibility
- Runtimes: Node.js 18+ and Bun. ESM only.
- Protocol: AWP/1. Tested against Aellopus server
0.1.0.
