@chunkdb/client
Official Node.js and TypeScript client for chunkdb.
Package: @chunkdb/client on npm.
Targets the stable chunkdb 1.x protocol; see the engine's
compatibility policy.
This package is intentionally small:
ChunkClient= one long-lived socket- sequential request/response per client by default, with opt-in pipelining (
pipelineDepth) - opt-in pooling via
ChunkPool - no automatic retries or background reconnect loops
- no browser transport
Features
chunk://andchunks://URI support- Node core
net/tlstransport connect,connectUri,connectPool,ChunkClient, andChunkPoolauth,ping,info,get,readBlock,exists,set,unset,mset,mget,chunkExists,readChunk,setChunk,setChunkState,chunk,chunkbin,chunkbinState- batch
mset/mget(single round-trip for many blocks) and configurable request pipelining (pipelineDepth) for high-latency links - persistent socket reuse for low-concurrency callers and opt-in pooled concurrency for Node services
- typed error classes
- configurable connect and command timeouts
- dual ESM / CommonJS build output
Install
npm install @chunkdb/client
Quick Start
import { connectUri } from "@chunkdb/client";
const client = await connectUri("chunk://chunk-token@127.0.0.1:4242/");
console.log(await client.ping());
console.log(await client.readBlock(0, 0));
console.log(await client.readChunk(0, 0));
await client.close();
TLS
import { connectUri } from "@chunkdb/client";
const client = await connectUri("chunks://chunk-token@127.0.0.1:4242/", {
tlsInsecure: true,
});
console.log(await client.info());
await client.close();
Timeout Options
connectTimeoutMs: maximum time to establish the socket and complete TLS setupcommandTimeoutMs: maximum time to wait for one command response
const client = await connectUri("chunk://chunk-token@127.0.0.1:4242/", {
connectTimeoutMs: 2000,
commandTimeoutMs: 3000,
});
Connection Model
- Reuse one
ChunkClientfor low-concurrency code paths. It keeps one socket open and sends one request at a time. - Use one shared
ChunkPoolfor concurrent Node.js workloads. It keeps several warmChunkClientinstances and leases them per operation. - True single-socket multiplexing is intentionally out of scope for protocol v1. Parallelism comes from multiple sockets, not request IDs on one socket.
Pooling
import { connectPool } from "@chunkdb/client";
const pool = await connectPool({
uri: "chunk://chunk-token@127.0.0.1:4242/",
maxConnections: 4,
minConnections: 1,
acquireTimeoutMs: 2000,
});
await Promise.all([
pool.set(0, 0, "1011001110110011"),
pool.set(1, 0, "0000111100001111"),
]);
console.log(await pool.readBlock(0, 0));
await pool.withClient(async (client) => {
console.log(await client.ping());
console.log(await client.info());
});
await pool.close();
ChunkPoolOptions extends ChunkClientOptions and adds:
maxConnections: maximum number of leased/open clients in the poolminConnections: optional warm connections created byconnectPoolacquireTimeoutMs: maximum time to wait for a free pooled client
TLS Options
tls: trueorchunks://...to enable TLStlsInsecure: trueto skip certificate verification for local testing onlytlsServerNameto force SNI / hostname verification targetca,cert,keyfor custom trust and client certificate material
const client = await connectUri("chunks://chunk-token@127.0.0.1:4242/", {
ca: process.env.CHUNKDB_CA_PEM,
tlsServerName: "chunkdb.local",
});
API
connect(options)connectUri(uri, overrides?)connectPool(options)parseChunkUri(uri)formatChunkUri(parsed)ChunkClientChunkPool
ChunkClient methods:
connect()close()auth(token?)ping()info()get(x, y)readBlock(x, y)exists(x, y)set(x, y, bits)unset(x, y)mset(blocks: { x, y, bits }[])— batch write, one round-trip; items apply in order and are not atomic as a group (on error, earlier items may already be applied) — usechunkBatchfor an atomic single-chunk updatemget(blocks: { x, y }[]): Promise<string[]>— batch read, one round-tripchunkExists(cx, cy)readChunk(cx, cy)setChunk(cx, cy, bits)setChunkState(cx, cy, { bits, presence })chunk(cx, cy)chunkbin(cx, cy)chunkbinState(cx, cy)chunkbinCompressed(cx, cy)/chunkbinStateCompressed(cx, cy)— same payloads aschunkbin/chunkbinState, transferred compressed and decompressed client-sidechunkScan(limit, cursor?)— enumerate populated chunks in deterministic(cx, cy)order; returns{ coords, nextCursor }, passnextCursorback to continue (limit 1..1024 per page)chunkRange(cx0, cy0, cx1, cy1)— bounded rectangular multi-chunk read (max 256 chunks, 64 MiB response cap); returns{ cx, cy, bits, presence }for populated chunks onlychunkRadius(cx, cy, radiusChunks)— bounded radius/disc multi-chunk read with the same limits and result shape aschunkRangechunkVersion(cx, cy): Promise<bigint>— opaque chunk version tokenchunkCompareAndSet(cx, cy, expectedVersion, { bits, presence })— conditional full-chunk replace; resolves{ ok, version }(onok: falsethe returned version is the current one; state is unchanged)chunkBatch(cx, cy, operations, { ifVersion? })— atomic single-chunk batch of{ type: "set", x, y, bits }/{ type: "unset", x, y }operations; same{ ok, version }resultwalFlush()— explicit durability barrier: resolves once every previously acknowledged write is durable, even when the server runs inrelaxedmodemetrics()— Prometheus text-format runtime metrics
Chunk versions are opaque: they change on every content mutation and whenever
the server reloads the chunk (eviction or restart), so a stale version can
never silently match after recovery. On ok: false, re-read, reconcile, and
retry with the fresh version.
ChunkPool mirrors the same high-level data methods and adds:
close()withClient(fn)
readBlock(x, y) is the preferred high-level read API:
type ChunkBlockState =
| { exists: false; bits: null }
| { exists: true; bits: string };
- unset block ->
{ exists: false, bits: null } - explicit zero block ->
{ exists: true, bits: "000...0" }
get(x, y) is kept for backward-compatible low-level reads and still returns the configured zero-bit payload when a block is unset.
Use exists(x, y) only when you specifically want the lower-level protocol-style check.
Chunk-level presence uses the same pattern:
chunkExists(cx, cy)tells you whether the chunk is explicitly presentreadChunk(cx, cy)is the preferred high-level chunk read API and returns:
type ChunkChunkState = {
exists: boolean;
bits: string;
presence: string;
};
- absent chunk ->
{ exists: false, bits: "000...0", presence: "000...0" } - explicit zero chunk ->
{ exists: true, bits: "000...0", presence: "111...1" } chunk(cx, cy)is kept for backward-compatible low-level chunk reads and still returns the configured zero-bit payload for an absent chunksetChunk(cx, cy, bits)explicitly replaces the full chunk payload, including an all-zero chunksetChunkState(cx, cy, { bits, presence })writes mixed present/absent block state in one requestchunkbinState(cx, cy)returns[payload_bytes][presence_bytes]for exact chunk-state transfer
info() returns:
type ChunkInfo = {
raw: string;
values: Record<string, string>;
};
values contains the parsed INFO key/value pairs exactly as reported by the server.
Examples
import { connect } from "@chunkdb/client";
const client = await connect({
host: "127.0.0.1",
port: 4242,
token: "chunk-token",
});
await client.set(0, 0, "1011001110110011");
console.log(await client.readBlock(0, 0));
await client.unset(0, 0);
console.log(await client.readBlock(0, 0));
await client.close();
import { connectPool } from "@chunkdb/client";
const pool = await connectPool({
uri: "chunk://chunk-token@127.0.0.1:4242/",
maxConnections: 4,
minConnections: 1,
});
const writes = Array.from({ length: 8 }, (_, x) =>
pool.set(x, 0, x % 2 === 0 ? "1011001110110011" : "0000111100001111"),
);
await Promise.all(writes);
console.log(await Promise.all([pool.readBlock(0, 0), pool.readBlock(1, 0)]));
await pool.close();
Errors
ChunkConnectionErrorChunkTimeoutErrorChunkProtocolErrorChunkServerErrorChunkAuthErrorChunkTlsError
Server -ERR ... responses are surfaced as typed errors.
import { ChunkAuthError, ChunkServerError, connectUri } from "@chunkdb/client";
try {
const client = await connectUri("chunk://wrong-token@127.0.0.1:4242/");
await client.ping();
} catch (error) {
if (error instanceof ChunkAuthError) {
console.error("auth failed", error.serverCode, error.serverMessage);
} else if (error instanceof ChunkServerError) {
console.error("server error", error.serverCode, error.serverMessage);
} else {
throw error;
}
}
Local Development
npm install
npm run build
npm test
npm pack --dry-run
Integration tests build and use the TLS-enabled server at
../chunkdb/build-js-tests/chunkdb_server.
Override paths when needed with:
CHUNKDB_REPO_ROOTCHUNKDB_SERVER_BINCHUNKDB_SERVER_BIN_TLS