plasmite 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # Plasmite Node Bindings (v0)
2
+
3
+ These bindings wrap the `libplasmite` C ABI via a N-API addon.
4
+
5
+ ## Installation
6
+
7
+ Install from npm (canonical package name):
8
+
9
+ ```bash
10
+ npm install plasmite
11
+ ```
12
+
13
+ Published package artifacts bundle:
14
+ - `index.node` N-API addon
15
+ - `libplasmite.(dylib|so)` beside the addon
16
+ - `plasmite` CLI binary exposed as `npx plasmite`
17
+
18
+ For development/testing from this repo, see Build & Test below.
19
+
20
+ ## Build Requirements
21
+
22
+ - Node 20+
23
+ - Rust toolchain (for building the addon)
24
+ - `libplasmite` built from this repo (`cargo build -p plasmite`)
25
+
26
+ ## Build & Test
27
+
28
+ From the repo root:
29
+
30
+ ```bash
31
+ cargo build -p plasmite
32
+ ```
33
+
34
+ Canonical repo-root command:
35
+
36
+ ```bash
37
+ just bindings-node-test
38
+ ```
39
+
40
+ Equivalent manual command (from `bindings/node`):
41
+
42
+ ```bash
43
+ PLASMITE_LIB_DIR="$(pwd)/../../target/debug" npm test
44
+ ```
45
+
46
+ Pack/install smoke (ensures bundled assets work without env vars):
47
+
48
+ ```bash
49
+ ./scripts/node_pack_smoke.sh
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ```js
55
+ const { Client, Durability } = require("plasmite")
56
+
57
+ const client = new Client("./data")
58
+ const pool = client.createPool("docs", 64 * 1024 * 1024)
59
+ const payload = Buffer.from(JSON.stringify({ kind: "note", text: "hi" }))
60
+ const message = pool.appendJson(payload, ["note"], Durability.Fast)
61
+ console.log(message.toString("utf8"))
62
+
63
+ const frame = pool.getLite3(BigInt(1))
64
+ console.log(frame.payload.length)
65
+
66
+ pool.close()
67
+ client.close()
68
+ ```
69
+
70
+ Local binding failures throw `PlasmiteNativeError` with structured metadata fields (`kind`, `path`, `seq`, `offset`) when available.
71
+
72
+ ## Remote Client (HTTP/JSON)
73
+
74
+ ```js
75
+ const { RemoteClient } = require("plasmite")
76
+
77
+ const client = new RemoteClient("http://127.0.0.1:9700")
78
+ const pool = await client.openPool("docs")
79
+ const message = await pool.append({ kind: "note", text: "hi" }, ["note"])
80
+ console.log(message.seq, message.data)
81
+
82
+ const tail = await pool.tail({
83
+ sinceSeq: message.seq,
84
+ tags: ["note"],
85
+ maxMessages: 1,
86
+ timeoutMs: 500,
87
+ })
88
+ console.log(await tail.next())
89
+ tail.cancel()
90
+ ```
91
+
92
+ `tail({ tags: [...] })` performs exact tag matching and composes with other filters via AND semantics.
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ Purpose: Provide an npm bin entrypoint that runs the bundled plasmite CLI.
4
+ Key Exports: CLI entrypoint only.
5
+ Role: Ensure `npx plasmite` works from npm-installed package artifacts.
6
+ Invariants: Uses packaged binary first, then falls back to PATH.
7
+ Invariants: Forwards argv verbatim and propagates child exit status.
8
+ Notes: Prints a concise error when no CLI binary is available.
9
+ */
10
+
11
+ const { spawnSync } = require("node:child_process");
12
+ const fs = require("node:fs");
13
+ const path = require("node:path");
14
+
15
+ const packageRoot = path.resolve(__dirname, "..");
16
+ const bundled = path.join(packageRoot, "plasmite");
17
+ const target = fs.existsSync(bundled) ? bundled : "plasmite";
18
+
19
+ const result = spawnSync(target, process.argv.slice(2), { stdio: "inherit" });
20
+ if (result.error) {
21
+ console.error(`plasmite: failed to execute ${target}: ${result.error.message}`);
22
+ process.exit(1);
23
+ }
24
+ process.exit(result.status ?? 1);
package/index.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ export const enum Durability {
7
+ Fast = 0,
8
+ Flush = 1
9
+ }
10
+ export const enum ErrorKind {
11
+ Internal = 1,
12
+ Usage = 2,
13
+ NotFound = 3,
14
+ AlreadyExists = 4,
15
+ Busy = 5,
16
+ Permission = 6,
17
+ Corrupt = 7,
18
+ Io = 8
19
+ }
20
+ export interface Lite3Frame {
21
+ seq: bigint
22
+ timestampNs: bigint
23
+ flags: number
24
+ payload: Buffer
25
+ }
26
+ export declare class Client {
27
+ constructor(poolDir: string)
28
+ createPool(poolRef: string, sizeBytes: number | bigint): Pool
29
+ openPool(poolRef: string): Pool
30
+ close(): void
31
+ }
32
+ export declare class Pool {
33
+ appendJson(payload: Buffer, tags: Array<string>, durability: Durability): Buffer
34
+ appendLite3(payload: Buffer, durability: Durability): bigint
35
+ getJson(seq: number | bigint): Buffer
36
+ getLite3(seq: number | bigint): Lite3Frame
37
+ openStream(sinceSeq?: number | bigint | undefined | null, maxMessages?: number | bigint | undefined | null, timeoutMs?: number | bigint | undefined | null): Stream
38
+ openLite3Stream(sinceSeq?: number | bigint | undefined | null, maxMessages?: number | bigint | undefined | null, timeoutMs?: number | bigint | undefined | null): Lite3Stream
39
+ close(): void
40
+ }
41
+ export declare class Stream {
42
+ nextJson(): Buffer | null
43
+ close(): void
44
+ }
45
+ export declare class Lite3Stream {
46
+ next(): Lite3Frame | null
47
+ close(): void
48
+ }
package/index.js ADDED
@@ -0,0 +1,233 @@
1
+ /*
2
+ Purpose: JavaScript entry point for the Plasmite Node binding.
3
+ Key Exports: Client, Pool, Stream, Durability, ErrorKind, replay.
4
+ Role: Thin wrapper around the native N-API addon.
5
+ Invariants: Exports align with native symbols and v0 API semantics.
6
+ Notes: Requires libplasmite to be discoverable at runtime.
7
+ */
8
+
9
+ const native = require("./index.node");
10
+ const { RemoteClient, RemoteError, RemotePool, RemoteTail } = require("./remote");
11
+
12
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
13
+
14
+ class PlasmiteNativeError extends Error {
15
+ constructor(message, details = {}, cause = undefined) {
16
+ super(message);
17
+ this.name = "PlasmiteNativeError";
18
+ this.kind = details.kind;
19
+ this.path = details.path;
20
+ this.seq = details.seq;
21
+ this.offset = details.offset;
22
+ this.cause = cause;
23
+ }
24
+ }
25
+
26
+ function parseNativeError(err) {
27
+ if (!(err instanceof Error) || typeof err.message !== "string") {
28
+ return null;
29
+ }
30
+ const prefix = "plasmite error:";
31
+ if (!err.message.startsWith(prefix)) {
32
+ return null;
33
+ }
34
+ const parts = err.message
35
+ .slice(prefix.length)
36
+ .split(";")
37
+ .map((part) => part.trim())
38
+ .filter(Boolean);
39
+ if (parts.length === 0) {
40
+ return null;
41
+ }
42
+ const details = {};
43
+ for (const part of parts) {
44
+ const [key, ...valueParts] = part.split("=");
45
+ if (!key || valueParts.length === 0) {
46
+ continue;
47
+ }
48
+ const value = valueParts.join("=");
49
+ if (key === "seq" || key === "offset") {
50
+ const parsed = Number(value);
51
+ details[key] = Number.isFinite(parsed) ? parsed : undefined;
52
+ continue;
53
+ }
54
+ details[key] = value;
55
+ }
56
+ return new PlasmiteNativeError(err.message, details, err);
57
+ }
58
+
59
+ function wrapNativeError(err) {
60
+ return parseNativeError(err) ?? err;
61
+ }
62
+
63
+ class Client {
64
+ constructor(poolDir) {
65
+ this._inner = new native.Client(poolDir);
66
+ }
67
+
68
+ createPool(poolRef, sizeBytes) {
69
+ try {
70
+ return new Pool(this._inner.createPool(poolRef, sizeBytes));
71
+ } catch (err) {
72
+ throw wrapNativeError(err);
73
+ }
74
+ }
75
+
76
+ openPool(poolRef) {
77
+ try {
78
+ return new Pool(this._inner.openPool(poolRef));
79
+ } catch (err) {
80
+ throw wrapNativeError(err);
81
+ }
82
+ }
83
+
84
+ close() {
85
+ this._inner.close();
86
+ }
87
+ }
88
+
89
+ class Pool {
90
+ constructor(inner) {
91
+ this._inner = inner;
92
+ }
93
+
94
+ appendJson(payload, tags, durability) {
95
+ try {
96
+ return this._inner.appendJson(payload, tags, durability);
97
+ } catch (err) {
98
+ throw wrapNativeError(err);
99
+ }
100
+ }
101
+
102
+ appendLite3(payload, durability) {
103
+ try {
104
+ return this._inner.appendLite3(payload, durability);
105
+ } catch (err) {
106
+ throw wrapNativeError(err);
107
+ }
108
+ }
109
+
110
+ getJson(seq) {
111
+ try {
112
+ return this._inner.getJson(seq);
113
+ } catch (err) {
114
+ throw wrapNativeError(err);
115
+ }
116
+ }
117
+
118
+ getLite3(seq) {
119
+ try {
120
+ return this._inner.getLite3(seq);
121
+ } catch (err) {
122
+ throw wrapNativeError(err);
123
+ }
124
+ }
125
+
126
+ openStream(sinceSeq, maxMessages, timeoutMs) {
127
+ try {
128
+ return new Stream(this._inner.openStream(sinceSeq, maxMessages, timeoutMs));
129
+ } catch (err) {
130
+ throw wrapNativeError(err);
131
+ }
132
+ }
133
+
134
+ openLite3Stream(sinceSeq, maxMessages, timeoutMs) {
135
+ try {
136
+ return new Lite3Stream(
137
+ this._inner.openLite3Stream(sinceSeq, maxMessages, timeoutMs),
138
+ );
139
+ } catch (err) {
140
+ throw wrapNativeError(err);
141
+ }
142
+ }
143
+
144
+ close() {
145
+ this._inner.close();
146
+ }
147
+ }
148
+
149
+ class Stream {
150
+ constructor(inner) {
151
+ this._inner = inner;
152
+ }
153
+
154
+ nextJson() {
155
+ try {
156
+ return this._inner.nextJson();
157
+ } catch (err) {
158
+ throw wrapNativeError(err);
159
+ }
160
+ }
161
+
162
+ close() {
163
+ this._inner.close();
164
+ }
165
+ }
166
+
167
+ class Lite3Stream {
168
+ constructor(inner) {
169
+ this._inner = inner;
170
+ }
171
+
172
+ next() {
173
+ try {
174
+ return this._inner.next();
175
+ } catch (err) {
176
+ throw wrapNativeError(err);
177
+ }
178
+ }
179
+
180
+ close() {
181
+ this._inner.close();
182
+ }
183
+ }
184
+
185
+ async function* replay(pool, options = {}) {
186
+ const { speed = 1.0, sinceSeq, maxMessages, timeoutMs } = options;
187
+ const stream = pool.openStream(
188
+ sinceSeq ?? null,
189
+ maxMessages ?? null,
190
+ timeoutMs ?? null,
191
+ );
192
+
193
+ const messages = [];
194
+ try {
195
+ let msg;
196
+ while ((msg = stream.nextJson()) !== null) {
197
+ messages.push(msg);
198
+ }
199
+ } finally {
200
+ stream.close();
201
+ }
202
+
203
+ let prevMs = null;
204
+ for (const msg of messages) {
205
+ const parsed = JSON.parse(msg);
206
+ const curMs = new Date(parsed.time).getTime();
207
+
208
+ if (prevMs !== null && speed > 0) {
209
+ const delay = (curMs - prevMs) / speed;
210
+ if (delay > 0) {
211
+ await sleep(delay);
212
+ }
213
+ }
214
+
215
+ prevMs = curMs;
216
+ yield msg;
217
+ }
218
+ }
219
+
220
+ module.exports = {
221
+ Client,
222
+ Pool,
223
+ Stream,
224
+ Lite3Stream,
225
+ Durability: native.Durability,
226
+ ErrorKind: native.ErrorKind,
227
+ PlasmiteNativeError,
228
+ RemoteClient,
229
+ RemoteError,
230
+ RemotePool,
231
+ RemoteTail,
232
+ replay,
233
+ };
package/index.node ADDED
Binary file
package/libplasmite.so ADDED
Binary file
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "plasmite",
3
+ "version": "0.1.8",
4
+ "description": "Persistent JSON message queues for Node.js - native bindings for local and remote IPC",
5
+ "license": "MIT",
6
+ "main": "index.js",
7
+ "types": "types.d.ts",
8
+ "bin": {
9
+ "plasmite": "bin/plasmite.js"
10
+ },
11
+ "files": [
12
+ "index.js",
13
+ "index.node",
14
+ "index.d.ts",
15
+ "types.d.ts",
16
+ "remote.js",
17
+ "bin/plasmite.js",
18
+ "libplasmite.dylib",
19
+ "libplasmite.so",
20
+ "plasmite"
21
+ ],
22
+ "keywords": [
23
+ "ipc",
24
+ "queue",
25
+ "json",
26
+ "messaging",
27
+ "pubsub",
28
+ "napi",
29
+ "native"
30
+ ],
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/sandover/plasmite.git",
34
+ "directory": "bindings/node"
35
+ },
36
+ "scripts": {
37
+ "prepare-native": "node scripts/prepare_native_assets.js",
38
+ "build": "napi build --cargo-cwd native",
39
+ "test": "npm run build && node --test test/*.test.js",
40
+ "typecheck": "tsc --noEmit -p tsconfig.json",
41
+ "prepack": "npm run build && npm run prepare-native",
42
+ "prepublishOnly": "npm run typecheck"
43
+ },
44
+ "devDependencies": {
45
+ "@napi-rs/cli": "^2.18.0",
46
+ "@types/node": "^25.2.1",
47
+ "typescript": "^5.9.3"
48
+ },
49
+ "engines": {
50
+ "node": ">=20"
51
+ }
52
+ }
package/plasmite ADDED
Binary file
package/remote.js ADDED
@@ -0,0 +1,242 @@
1
+ /*
2
+ Purpose: Provide an HTTP/JSON RemoteClient for the Node binding.
3
+ Key Exports: RemoteClient, RemotePool, RemoteTail, RemoteError.
4
+ Role: JS-side remote access that mirrors the v0 server protocol.
5
+ Invariants: Uses JSON request/response envelopes from spec/remote/v0.
6
+ Invariants: Base URL must be http(s) without a path.
7
+ Invariants: Tail streams are JSONL and can be canceled.
8
+ */
9
+
10
+ const { Readable } = require("node:stream");
11
+ const readline = require("node:readline");
12
+
13
+ class RemoteError extends Error {
14
+ constructor(payload, status) {
15
+ const error = payload && payload.error ? payload.error : payload;
16
+ const message = error && error.message ? error.message : `Remote error ${status}`;
17
+ super(message);
18
+ this.name = "RemoteError";
19
+ this.status = status;
20
+ this.kind = error && error.kind ? error.kind : "Io";
21
+ this.hint = error && error.hint ? error.hint : undefined;
22
+ this.path = error && error.path ? error.path : undefined;
23
+ this.seq = error && error.seq ? error.seq : undefined;
24
+ this.offset = error && error.offset ? error.offset : undefined;
25
+ }
26
+ }
27
+
28
+ class RemoteClient {
29
+ constructor(baseUrl, options = {}) {
30
+ this.baseUrl = normalizeBaseUrl(baseUrl);
31
+ this.token = options.token || null;
32
+ }
33
+
34
+ withToken(token) {
35
+ this.token = token;
36
+ return this;
37
+ }
38
+
39
+ async createPool(pool, sizeBytes) {
40
+ const payload = { pool, size_bytes: Number(sizeBytes) };
41
+ const url = buildUrl(this.baseUrl, ["v0", "pools"]);
42
+ const data = await this._requestJson("POST", url, payload);
43
+ return data.pool;
44
+ }
45
+
46
+ async openPool(pool) {
47
+ const payload = { pool };
48
+ const url = buildUrl(this.baseUrl, ["v0", "pools", "open"]);
49
+ await this._requestJson("POST", url, payload);
50
+ return new RemotePool(this, pool);
51
+ }
52
+
53
+ async poolInfo(pool) {
54
+ const url = buildUrl(this.baseUrl, ["v0", "pools", pool, "info"]);
55
+ const data = await this._requestJson("GET", url, null);
56
+ return data.pool;
57
+ }
58
+
59
+ async listPools() {
60
+ const url = buildUrl(this.baseUrl, ["v0", "pools"]);
61
+ const data = await this._requestJson("GET", url, null);
62
+ return data.pools;
63
+ }
64
+
65
+ async deletePool(pool) {
66
+ const url = buildUrl(this.baseUrl, ["v0", "pools", pool]);
67
+ await this._requestJson("DELETE", url, null);
68
+ }
69
+
70
+ async _requestJson(method, url, body) {
71
+ const headers = { Accept: "application/json" };
72
+ if (this.token) {
73
+ headers.Authorization = `Bearer ${this.token}`;
74
+ }
75
+ let payload;
76
+ if (method !== "GET" && method !== "DELETE") {
77
+ headers["Content-Type"] = "application/json";
78
+ payload = JSON.stringify(body);
79
+ }
80
+
81
+ const response = await fetch(url.toString(), {
82
+ method,
83
+ headers,
84
+ body: payload,
85
+ });
86
+
87
+ if (!response.ok) {
88
+ throw await parseRemoteError(response);
89
+ }
90
+
91
+ if (response.status === 204) {
92
+ return null;
93
+ }
94
+ return response.json();
95
+ }
96
+
97
+ async _requestStream(url, controller) {
98
+ const headers = { Accept: "application/json" };
99
+ if (this.token) {
100
+ headers.Authorization = `Bearer ${this.token}`;
101
+ }
102
+
103
+ const response = await fetch(url.toString(), {
104
+ method: "GET",
105
+ headers,
106
+ signal: controller.signal,
107
+ });
108
+
109
+ if (!response.ok) {
110
+ throw await parseRemoteError(response);
111
+ }
112
+
113
+ return response;
114
+ }
115
+ }
116
+
117
+ class RemotePool {
118
+ constructor(client, pool) {
119
+ this.client = client;
120
+ this.pool = pool;
121
+ }
122
+
123
+ poolRef() {
124
+ return this.pool;
125
+ }
126
+
127
+ async append(data, tags = [], durability = "fast") {
128
+ const payload = { data, tags, durability };
129
+ const url = buildUrl(this.client.baseUrl, ["v0", "pools", this.pool, "append"]);
130
+ const response = await this.client._requestJson("POST", url, payload);
131
+ return response.message;
132
+ }
133
+
134
+ async get(seq) {
135
+ const url = buildUrl(this.client.baseUrl, [
136
+ "v0",
137
+ "pools",
138
+ this.pool,
139
+ "messages",
140
+ String(seq),
141
+ ]);
142
+ const response = await this.client._requestJson("GET", url, null);
143
+ return response.message;
144
+ }
145
+
146
+ async tail(options = {}) {
147
+ const url = buildUrl(this.client.baseUrl, ["v0", "pools", this.pool, "tail"]);
148
+ if (options.sinceSeq !== undefined) {
149
+ url.searchParams.set("since_seq", String(options.sinceSeq));
150
+ }
151
+ if (options.maxMessages !== undefined) {
152
+ url.searchParams.set("max", String(options.maxMessages));
153
+ }
154
+ if (options.timeoutMs !== undefined) {
155
+ url.searchParams.set("timeout_ms", String(options.timeoutMs));
156
+ }
157
+ if (options.tags !== undefined) {
158
+ const tags = Array.isArray(options.tags) ? options.tags : [options.tags];
159
+ for (const tag of tags) {
160
+ url.searchParams.append("tag", String(tag));
161
+ }
162
+ }
163
+
164
+ const controller = new AbortController();
165
+ const response = await this.client._requestStream(url, controller);
166
+ return new RemoteTail(response, controller);
167
+ }
168
+ }
169
+
170
+ class RemoteTail {
171
+ constructor(response, controller) {
172
+ if (!response.body) {
173
+ throw new Error("remote tail response has no body");
174
+ }
175
+ this.controller = controller;
176
+ const stream = Readable.fromWeb(response.body);
177
+ this.reader = readline.createInterface({ input: stream, crlfDelay: Infinity });
178
+ this.iterator = this.reader[Symbol.asyncIterator]();
179
+ this.done = false;
180
+ }
181
+
182
+ async next() {
183
+ if (this.done) {
184
+ return null;
185
+ }
186
+ const { value, done } = await this.iterator.next();
187
+ if (done) {
188
+ this.done = true;
189
+ return null;
190
+ }
191
+ if (!value || !value.trim()) {
192
+ return this.next();
193
+ }
194
+ return JSON.parse(value);
195
+ }
196
+
197
+ cancel() {
198
+ if (this.done) {
199
+ return;
200
+ }
201
+ this.done = true;
202
+ this.controller.abort();
203
+ this.reader.close();
204
+ }
205
+ }
206
+
207
+ function normalizeBaseUrl(raw) {
208
+ const url = new URL(raw);
209
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
210
+ throw new Error("remote base URL must use http or https");
211
+ }
212
+ if (url.pathname && url.pathname !== "/") {
213
+ throw new Error("remote base URL must not include a path");
214
+ }
215
+ url.pathname = "/";
216
+ url.search = "";
217
+ url.hash = "";
218
+ return url;
219
+ }
220
+
221
+ function buildUrl(baseUrl, segments) {
222
+ const url = new URL(baseUrl.toString());
223
+ url.pathname = `/${segments.map((segment) => encodeURIComponent(segment)).join("/")}`;
224
+ return url;
225
+ }
226
+
227
+ async function parseRemoteError(response) {
228
+ let payload = null;
229
+ try {
230
+ payload = await response.json();
231
+ } catch (err) {
232
+ payload = null;
233
+ }
234
+ return new RemoteError(payload, response.status);
235
+ }
236
+
237
+ module.exports = {
238
+ RemoteClient,
239
+ RemotePool,
240
+ RemoteTail,
241
+ RemoteError,
242
+ };
package/types.d.ts ADDED
@@ -0,0 +1,158 @@
1
+ /*
2
+ Purpose: Stable TypeScript declarations for the public Node bindings API.
3
+ Key Exports: Client, Pool, Stream, Lite3Stream, replay, and remote client types.
4
+ Role: Preserve complete JS + native type surface independently of generated files.
5
+ Invariants: Public runtime exports from index.js are represented here.
6
+ Invariants: Numeric sequence fields accept number or bigint input.
7
+ Notes: Kept separate from NAPI-generated index.d.ts to avoid regeneration loss.
8
+ */
9
+
10
+ export const enum Durability {
11
+ Fast = 0,
12
+ Flush = 1,
13
+ }
14
+
15
+ export const enum ErrorKind {
16
+ Internal = 1,
17
+ Usage = 2,
18
+ NotFound = 3,
19
+ AlreadyExists = 4,
20
+ Busy = 5,
21
+ Permission = 6,
22
+ Corrupt = 7,
23
+ Io = 8,
24
+ }
25
+
26
+ export interface Lite3Frame {
27
+ seq: bigint
28
+ timestampNs: bigint
29
+ flags: number
30
+ payload: Buffer
31
+ }
32
+
33
+ export class PlasmiteNativeError extends Error {
34
+ kind?: string
35
+ path?: string
36
+ seq?: number
37
+ offset?: number
38
+ cause?: unknown
39
+ }
40
+
41
+ export class Client {
42
+ constructor(poolDir: string)
43
+ createPool(poolRef: string, sizeBytes: number | bigint): Pool
44
+ openPool(poolRef: string): Pool
45
+ close(): void
46
+ }
47
+
48
+ export class Pool {
49
+ appendJson(payload: Buffer, tags: string[], durability: Durability): Buffer
50
+ appendLite3(payload: Buffer, durability: Durability): bigint
51
+ getJson(seq: number | bigint): Buffer
52
+ getLite3(seq: number | bigint): Lite3Frame
53
+ openStream(
54
+ sinceSeq?: number | bigint | null,
55
+ maxMessages?: number | bigint | null,
56
+ timeoutMs?: number | bigint | null,
57
+ ): Stream
58
+ openLite3Stream(
59
+ sinceSeq?: number | bigint | null,
60
+ maxMessages?: number | bigint | null,
61
+ timeoutMs?: number | bigint | null,
62
+ ): Lite3Stream
63
+ close(): void
64
+ }
65
+
66
+ export class Stream {
67
+ nextJson(): Buffer | null
68
+ close(): void
69
+ }
70
+
71
+ export class Lite3Stream {
72
+ next(): Lite3Frame | null
73
+ close(): void
74
+ }
75
+
76
+ export interface ReplayOptions {
77
+ speed?: number
78
+ sinceSeq?: number | bigint
79
+ maxMessages?: number | bigint
80
+ timeoutMs?: number | bigint
81
+ }
82
+
83
+ export function replay(
84
+ pool: Pool,
85
+ options?: ReplayOptions,
86
+ ): AsyncGenerator<Buffer, void, unknown>
87
+
88
+ export interface RemoteClientOptions {
89
+ token?: string
90
+ }
91
+
92
+ export interface RemotePoolInfo {
93
+ pool: string
94
+ path: string
95
+ file_size: number
96
+ ring_size: number
97
+ bounds: {
98
+ oldest_seq: number | null
99
+ newest_seq: number | null
100
+ }
101
+ write_cursor: number
102
+ index_inline: boolean
103
+ }
104
+
105
+ export interface RemoteMessage {
106
+ seq: number
107
+ time: string
108
+ data: unknown
109
+ meta?: {
110
+ tags?: string[]
111
+ }
112
+ }
113
+
114
+ export interface RemoteTailOptions {
115
+ sinceSeq?: number | bigint
116
+ maxMessages?: number | bigint
117
+ timeoutMs?: number
118
+ }
119
+
120
+ export class RemoteError extends Error {
121
+ status: number
122
+ kind: string
123
+ hint?: string
124
+ path?: string
125
+ seq?: number
126
+ offset?: number
127
+ }
128
+
129
+ export class RemoteClient {
130
+ constructor(baseUrl: string, options?: RemoteClientOptions)
131
+ baseUrl: URL
132
+ token: string | null
133
+ withToken(token: string): RemoteClient
134
+ createPool(pool: string, sizeBytes: number | bigint): Promise<string>
135
+ openPool(pool: string): Promise<RemotePool>
136
+ poolInfo(pool: string): Promise<RemotePoolInfo>
137
+ listPools(): Promise<string[]>
138
+ deletePool(pool: string): Promise<void>
139
+ }
140
+
141
+ export class RemotePool {
142
+ constructor(client: RemoteClient, pool: string)
143
+ client: RemoteClient
144
+ pool: string
145
+ poolRef(): string
146
+ append(
147
+ data: unknown,
148
+ tags?: string[],
149
+ durability?: "fast" | "flush",
150
+ ): Promise<RemoteMessage>
151
+ get(seq: number | bigint): Promise<RemoteMessage>
152
+ tail(options?: RemoteTailOptions): Promise<RemoteTail>
153
+ }
154
+
155
+ export class RemoteTail {
156
+ next(): Promise<RemoteMessage | null>
157
+ cancel(): void
158
+ }