plasmite 0.1.8 → 0.1.13
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 +163 -57
- package/bin/plasmite.js +27 -4
- package/index.js +60 -3
- package/native/darwin-arm64/index.node +0 -0
- package/native/darwin-arm64/libplasmite.dylib +0 -0
- package/native/darwin-arm64/plasmite +0 -0
- package/native/darwin-x64/index.node +0 -0
- package/native/darwin-x64/libplasmite.dylib +0 -0
- package/native/darwin-x64/plasmite +0 -0
- package/{index.node → native/linux-x64/index.node} +0 -0
- package/{libplasmite.so → native/linux-x64/libplasmite.so} +0 -0
- package/{plasmite → native/linux-x64/plasmite} +0 -0
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -1,92 +1,198 @@
|
|
|
1
|
-
#
|
|
1
|
+
# plasmite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Persistent JSON message queues for Node.js. No broker, no daemon — just files on disk.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Plasmite gives you fast, crash-safe, disk-backed ring buffers ("pools") that
|
|
6
|
+
multiple processes can read and write concurrently. Use it for IPC, event
|
|
7
|
+
sourcing, job queues, or anywhere you'd reach for Redis but don't want to run
|
|
8
|
+
a server.
|
|
6
9
|
|
|
7
|
-
Install
|
|
10
|
+
## Install
|
|
8
11
|
|
|
9
12
|
```bash
|
|
10
13
|
npm install plasmite
|
|
11
14
|
```
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- `libplasmite.(dylib|so)` beside the addon
|
|
16
|
-
- `plasmite` CLI binary exposed as `npx plasmite`
|
|
16
|
+
Requires Node 20+. The package ships pre-built native binaries — no Rust
|
|
17
|
+
toolchain or compile step needed.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
## Quick start
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
### Local pools (native, in-process)
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
```js
|
|
24
|
+
const { Client, Durability } = require("plasmite");
|
|
25
|
+
|
|
26
|
+
// Open a client pointed at a directory (created if it doesn't exist)
|
|
27
|
+
const client = new Client("./data");
|
|
28
|
+
|
|
29
|
+
// Create a 64 MB pool called "events"
|
|
30
|
+
const pool = client.createPool("events", 64 * 1024 * 1024);
|
|
31
|
+
|
|
32
|
+
// Append a JSON message with tags
|
|
33
|
+
pool.appendJson(
|
|
34
|
+
Buffer.from(JSON.stringify({ kind: "signup", user: "alice" })),
|
|
35
|
+
["user-event"],
|
|
36
|
+
Durability.Flush,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Read it back by sequence number
|
|
40
|
+
const msg = pool.getJson(1n);
|
|
41
|
+
console.log(JSON.parse(msg.toString()));
|
|
42
|
+
// => { kind: "signup", user: "alice" }
|
|
43
|
+
|
|
44
|
+
// Stream messages as they arrive
|
|
45
|
+
const stream = pool.openStream();
|
|
46
|
+
let frame;
|
|
47
|
+
while ((frame = stream.nextJson()) !== null) {
|
|
48
|
+
console.log(JSON.parse(frame.toString()));
|
|
49
|
+
}
|
|
50
|
+
stream.close();
|
|
51
|
+
|
|
52
|
+
pool.close();
|
|
53
|
+
client.close();
|
|
54
|
+
```
|
|
25
55
|
|
|
26
|
-
|
|
56
|
+
### Remote pools (HTTP/JSON)
|
|
27
57
|
|
|
28
|
-
|
|
58
|
+
Connect to a plasmite server (`npx plasmite serve` or `pls serve`) to read
|
|
59
|
+
and write pools over the network.
|
|
29
60
|
|
|
30
|
-
```
|
|
31
|
-
|
|
61
|
+
```js
|
|
62
|
+
const { RemoteClient } = require("plasmite");
|
|
63
|
+
|
|
64
|
+
const client = new RemoteClient("http://127.0.0.1:9700");
|
|
65
|
+
// With auth: new RemoteClient("http://...", { token: "secret" })
|
|
66
|
+
|
|
67
|
+
const pool = await client.openPool("events");
|
|
68
|
+
|
|
69
|
+
// Append — accepts plain objects, serialized as JSON for you
|
|
70
|
+
const message = await pool.append(
|
|
71
|
+
{ kind: "deploy", sha: "abc123" },
|
|
72
|
+
["ops"],
|
|
73
|
+
);
|
|
74
|
+
console.log(message.seq); // => 1
|
|
75
|
+
|
|
76
|
+
// Read by sequence number
|
|
77
|
+
const got = await pool.get(1);
|
|
78
|
+
console.log(got.data); // => { kind: "deploy", sha: "abc123" }
|
|
79
|
+
|
|
80
|
+
// Tail — live-stream new messages (JSONL under the hood)
|
|
81
|
+
const tail = await pool.tail({ sinceSeq: 0, tags: ["ops"] });
|
|
82
|
+
const next = await tail.next(); // resolves on next matching message
|
|
83
|
+
console.log(next);
|
|
84
|
+
tail.cancel();
|
|
32
85
|
```
|
|
33
86
|
|
|
34
|
-
|
|
87
|
+
## API
|
|
88
|
+
|
|
89
|
+
### Local client
|
|
90
|
+
|
|
91
|
+
| Class | Method | Description |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `Client(dir)` | | Open a pool directory |
|
|
94
|
+
| | `.createPool(name, sizeBytes)` | Create a new pool (returns `Pool`) |
|
|
95
|
+
| | `.openPool(name)` | Open an existing pool (returns `Pool`) |
|
|
96
|
+
| | `.close()` | Release resources |
|
|
97
|
+
| `Pool` | `.appendJson(buf, tags, durability)` | Append a JSON message; returns the stored envelope as `Buffer` |
|
|
98
|
+
| | `.appendLite3(buf, durability)` | Append raw bytes (lite3 framing); returns sequence `bigint` |
|
|
99
|
+
| | `.getJson(seq)` | Get message by sequence number; returns `Buffer` |
|
|
100
|
+
| | `.getLite3(seq)` | Get lite3 frame by sequence number |
|
|
101
|
+
| | `.openStream(sinceSeq?, max?, timeoutMs?)` | Open a message stream |
|
|
102
|
+
| | `.openLite3Stream(sinceSeq?, max?, timeoutMs?)` | Open a lite3 frame stream |
|
|
103
|
+
| | `.close()` | Close the pool |
|
|
104
|
+
| `Stream` | `.nextJson()` | Next message as `Buffer`, or `null` at end |
|
|
105
|
+
| | `.close()` | Close the stream |
|
|
106
|
+
|
|
107
|
+
**Durability** controls fsync behavior:
|
|
108
|
+
- `Durability.Fast` — buffered writes (higher throughput)
|
|
109
|
+
- `Durability.Flush` — fsync after write (crash-safe)
|
|
110
|
+
|
|
111
|
+
Sequence numbers accept `number` or `bigint`.
|
|
112
|
+
|
|
113
|
+
### Remote client
|
|
114
|
+
|
|
115
|
+
| Class | Method | Description |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `RemoteClient(url, opts?)` | | Connect to a plasmite server |
|
|
118
|
+
| | `.withToken(token)` | Set bearer token (returns `this`) |
|
|
119
|
+
| | `.createPool(name, sizeBytes)` | Create a pool on the server |
|
|
120
|
+
| | `.openPool(name)` | Open a remote pool (returns `RemotePool`) |
|
|
121
|
+
| | `.poolInfo(name)` | Get pool metadata |
|
|
122
|
+
| | `.listPools()` | List all pools |
|
|
123
|
+
| | `.deletePool(name)` | Delete a pool |
|
|
124
|
+
| `RemotePool` | `.append(data, tags?, durability?)` | Append a message (data is any JSON-serializable value) |
|
|
125
|
+
| | `.get(seq)` | Get a message by sequence number |
|
|
126
|
+
| | `.tail(opts?)` | Live-tail messages (returns `RemoteTail`) |
|
|
127
|
+
| `RemoteTail` | `.next()` | Await next message (resolves to message or `null`) |
|
|
128
|
+
| | `.cancel()` | Stop the tail stream |
|
|
129
|
+
|
|
130
|
+
### Tail options
|
|
35
131
|
|
|
36
|
-
```
|
|
37
|
-
|
|
132
|
+
```js
|
|
133
|
+
await pool.tail({
|
|
134
|
+
sinceSeq: 0, // start after this sequence number
|
|
135
|
+
maxMessages: 100, // stop after N messages
|
|
136
|
+
timeoutMs: 5000, // stop after N ms of inactivity
|
|
137
|
+
tags: ["signup"], // filter by exact tag match (AND across tags)
|
|
138
|
+
});
|
|
38
139
|
```
|
|
39
140
|
|
|
40
|
-
|
|
141
|
+
### Error handling
|
|
41
142
|
|
|
42
|
-
|
|
43
|
-
|
|
143
|
+
Local operations throw `PlasmiteNativeError` with structured fields:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
try {
|
|
147
|
+
pool.getJson(999n);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
if (err instanceof PlasmiteNativeError) {
|
|
150
|
+
console.log(err.kind); // "NotFound"
|
|
151
|
+
console.log(err.seq); // 999
|
|
152
|
+
}
|
|
153
|
+
}
|
|
44
154
|
```
|
|
45
155
|
|
|
46
|
-
|
|
156
|
+
Remote operations throw `RemoteError` with `status`, `kind`, and optional
|
|
157
|
+
`hint`.
|
|
158
|
+
|
|
159
|
+
### CLI
|
|
160
|
+
|
|
161
|
+
The package includes the `plasmite` CLI:
|
|
47
162
|
|
|
48
163
|
```bash
|
|
49
|
-
|
|
164
|
+
npx plasmite --version
|
|
165
|
+
npx plasmite serve ./data --port 9700
|
|
50
166
|
```
|
|
51
167
|
|
|
52
|
-
##
|
|
168
|
+
## TypeScript
|
|
53
169
|
|
|
54
|
-
|
|
55
|
-
|
|
170
|
+
Type declarations ship with the package (`types.d.ts`). No `@types/` install
|
|
171
|
+
needed.
|
|
56
172
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const message = pool.appendJson(payload, ["note"], Durability.Fast)
|
|
61
|
-
console.log(message.toString("utf8"))
|
|
173
|
+
```ts
|
|
174
|
+
import { Client, Durability, Pool, RemoteClient } from "plasmite";
|
|
175
|
+
```
|
|
62
176
|
|
|
63
|
-
|
|
64
|
-
console.log(frame.payload.length)
|
|
177
|
+
## Platform support
|
|
65
178
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
179
|
+
Pre-built binaries are included for Linux x86_64. macOS and Windows users
|
|
180
|
+
should install via Homebrew (`brew install sandover/tap/plasmite`) or build
|
|
181
|
+
from source.
|
|
69
182
|
|
|
70
|
-
|
|
183
|
+
## Contributing
|
|
71
184
|
|
|
72
|
-
|
|
185
|
+
Development requires a Rust toolchain to build the native addon:
|
|
73
186
|
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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()
|
|
187
|
+
```bash
|
|
188
|
+
# From the repo root
|
|
189
|
+
cargo build -p plasmite
|
|
190
|
+
cd bindings/node && PLASMITE_LIB_DIR=../../target/debug npm test
|
|
90
191
|
```
|
|
91
192
|
|
|
92
|
-
|
|
193
|
+
See the [main repo](https://github.com/sandover/plasmite) for full build
|
|
194
|
+
instructions.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT
|
package/bin/plasmite.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*
|
|
3
|
-
Purpose: Provide an npm bin entrypoint that runs the
|
|
3
|
+
Purpose: Provide an npm bin entrypoint that runs the packaged platform CLI binary.
|
|
4
4
|
Key Exports: CLI entrypoint only.
|
|
5
5
|
Role: Ensure `npx plasmite` works from npm-installed package artifacts.
|
|
6
|
-
Invariants:
|
|
6
|
+
Invariants: Resolves CLI from native/{platform}/ for supported platforms.
|
|
7
7
|
Invariants: Forwards argv verbatim and propagates child exit status.
|
|
8
8
|
Notes: Prints a concise error when no CLI binary is available.
|
|
9
9
|
*/
|
|
@@ -12,9 +12,32 @@ const { spawnSync } = require("node:child_process");
|
|
|
12
12
|
const fs = require("node:fs");
|
|
13
13
|
const path = require("node:path");
|
|
14
14
|
|
|
15
|
+
const PLATFORM_DIRS = Object.freeze({
|
|
16
|
+
linux: Object.freeze({ x64: "linux-x64" }),
|
|
17
|
+
darwin: Object.freeze({ x64: "darwin-x64", arm64: "darwin-arm64" }),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function resolvePlatformDir() {
|
|
21
|
+
const byArch = PLATFORM_DIRS[process.platform];
|
|
22
|
+
if (!byArch) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return byArch[process.arch] ?? null;
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
const packageRoot = path.resolve(__dirname, "..");
|
|
16
|
-
const
|
|
17
|
-
const target =
|
|
29
|
+
const platformDir = resolvePlatformDir();
|
|
30
|
+
const target = platformDir
|
|
31
|
+
? path.join(packageRoot, "native", platformDir, "plasmite")
|
|
32
|
+
: null;
|
|
33
|
+
|
|
34
|
+
if (!target || !fs.existsSync(target)) {
|
|
35
|
+
console.error(
|
|
36
|
+
`plasmite: native CLI is unavailable for ${process.platform}-${process.arch}. ` +
|
|
37
|
+
"This package supports remote-only mode without native binaries on unsupported platforms.",
|
|
38
|
+
);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
18
41
|
|
|
19
42
|
const result = spawnSync(target, process.argv.slice(2), { stdio: "inherit" });
|
|
20
43
|
if (result.error) {
|
package/index.js
CHANGED
|
@@ -6,10 +6,55 @@ Invariants: Exports align with native symbols and v0 API semantics.
|
|
|
6
6
|
Notes: Requires libplasmite to be discoverable at runtime.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const native = require("./index.node");
|
|
10
9
|
const { RemoteClient, RemoteError, RemotePool, RemoteTail } = require("./remote");
|
|
11
10
|
|
|
12
11
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
const path = require("node:path");
|
|
13
|
+
|
|
14
|
+
const PLATFORM_DIRS = Object.freeze({
|
|
15
|
+
linux: Object.freeze({ x64: "linux-x64" }),
|
|
16
|
+
darwin: Object.freeze({ x64: "darwin-x64", arm64: "darwin-arm64" }),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function resolvePlatformDir() {
|
|
20
|
+
const byArch = PLATFORM_DIRS[process.platform];
|
|
21
|
+
if (!byArch) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return byArch[process.arch] ?? null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveNativeAddonPath() {
|
|
28
|
+
const platformDir = resolvePlatformDir();
|
|
29
|
+
if (!platformDir) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return path.join(__dirname, "native", platformDir, "index.node");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let native = null;
|
|
36
|
+
let nativeLoadError = null;
|
|
37
|
+
let nativeAddonPath = null;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
nativeAddonPath = resolveNativeAddonPath();
|
|
41
|
+
if (nativeAddonPath) {
|
|
42
|
+
native = require(nativeAddonPath);
|
|
43
|
+
} else {
|
|
44
|
+
nativeLoadError = new Error(`unsupported platform: ${process.platform}-${process.arch}`);
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
nativeLoadError = err;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function makeNativeUnavailableError() {
|
|
51
|
+
const reason = nativeLoadError instanceof Error ? nativeLoadError.message : "unknown load error";
|
|
52
|
+
return new Error(
|
|
53
|
+
`plasmite native addon is unavailable for ${process.platform}-${process.arch} (${reason}). ` +
|
|
54
|
+
`expected addon path: ${nativeAddonPath ?? "unsupported platform"}. ` +
|
|
55
|
+
"RemoteClient remains supported without native artifacts.",
|
|
56
|
+
);
|
|
57
|
+
}
|
|
13
58
|
|
|
14
59
|
class PlasmiteNativeError extends Error {
|
|
15
60
|
constructor(message, details = {}, cause = undefined) {
|
|
@@ -62,6 +107,9 @@ function wrapNativeError(err) {
|
|
|
62
107
|
|
|
63
108
|
class Client {
|
|
64
109
|
constructor(poolDir) {
|
|
110
|
+
if (!native) {
|
|
111
|
+
throw makeNativeUnavailableError();
|
|
112
|
+
}
|
|
65
113
|
this._inner = new native.Client(poolDir);
|
|
66
114
|
}
|
|
67
115
|
|
|
@@ -88,6 +136,9 @@ class Client {
|
|
|
88
136
|
|
|
89
137
|
class Pool {
|
|
90
138
|
constructor(inner) {
|
|
139
|
+
if (!native) {
|
|
140
|
+
throw makeNativeUnavailableError();
|
|
141
|
+
}
|
|
91
142
|
this._inner = inner;
|
|
92
143
|
}
|
|
93
144
|
|
|
@@ -148,6 +199,9 @@ class Pool {
|
|
|
148
199
|
|
|
149
200
|
class Stream {
|
|
150
201
|
constructor(inner) {
|
|
202
|
+
if (!native) {
|
|
203
|
+
throw makeNativeUnavailableError();
|
|
204
|
+
}
|
|
151
205
|
this._inner = inner;
|
|
152
206
|
}
|
|
153
207
|
|
|
@@ -166,6 +220,9 @@ class Stream {
|
|
|
166
220
|
|
|
167
221
|
class Lite3Stream {
|
|
168
222
|
constructor(inner) {
|
|
223
|
+
if (!native) {
|
|
224
|
+
throw makeNativeUnavailableError();
|
|
225
|
+
}
|
|
169
226
|
this._inner = inner;
|
|
170
227
|
}
|
|
171
228
|
|
|
@@ -222,8 +279,8 @@ module.exports = {
|
|
|
222
279
|
Pool,
|
|
223
280
|
Stream,
|
|
224
281
|
Lite3Stream,
|
|
225
|
-
Durability: native.Durability,
|
|
226
|
-
ErrorKind: native.ErrorKind,
|
|
282
|
+
Durability: native ? native.Durability : Object.freeze({}),
|
|
283
|
+
ErrorKind: native ? native.ErrorKind : Object.freeze({}),
|
|
227
284
|
PlasmiteNativeError,
|
|
228
285
|
RemoteClient,
|
|
229
286
|
RemoteError,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plasmite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Persistent JSON message queues for Node.js - native bindings for local and remote IPC",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
|
@@ -10,14 +10,13 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"index.js",
|
|
13
|
-
"index.node",
|
|
14
13
|
"index.d.ts",
|
|
15
14
|
"types.d.ts",
|
|
16
15
|
"remote.js",
|
|
17
16
|
"bin/plasmite.js",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
17
|
+
"native/linux-x64/**",
|
|
18
|
+
"native/darwin-x64/**",
|
|
19
|
+
"native/darwin-arm64/**"
|
|
21
20
|
],
|
|
22
21
|
"keywords": [
|
|
23
22
|
"ipc",
|