plasmite 0.2.0 → 0.5.0
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 +94 -59
- package/index.js +405 -43
- package/mappings.js +55 -0
- package/message.js +97 -0
- 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/native/linux-arm64/index.node +0 -0
- package/native/linux-arm64/libplasmite.so +0 -0
- package/native/linux-arm64/plasmite +0 -0
- package/native/linux-x64/index.node +0 -0
- package/native/linux-x64/libplasmite.so +0 -0
- package/native/linux-x64/plasmite +0 -0
- package/native/win32-x64/index.node +0 -0
- package/native/win32-x64/plasmite.dll +0 -0
- package/native/win32-x64/plasmite.exe +0 -0
- package/package.json +5 -3
- package/remote.js +89 -41
- package/types.d.ts +61 -27
- package/index.d.ts +0 -48
package/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Purpose: JavaScript entry point for the Plasmite Node binding.
|
|
3
|
-
Key Exports: Client, Pool, Stream, Durability, ErrorKind, replay.
|
|
3
|
+
Key Exports: Client, Pool, Message, Stream, Durability, ErrorKind, replay.
|
|
4
4
|
Role: Thin wrapper around the native N-API addon.
|
|
5
5
|
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 { RemoteClient, RemoteError, RemotePool
|
|
9
|
+
const { RemoteClient, RemoteError, RemotePool } = require("./remote");
|
|
10
|
+
const { Message, parseMessage } = require("./message");
|
|
11
|
+
const { ERROR_KIND_VALUES, mapErrorKind } = require("./mappings");
|
|
10
12
|
|
|
11
13
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
14
|
const path = require("node:path");
|
|
15
|
+
const os = require("node:os");
|
|
13
16
|
|
|
14
17
|
const PLATFORM_DIRS = Object.freeze({
|
|
15
18
|
linux: Object.freeze({ x64: "linux-x64", arm64: "linux-arm64" }),
|
|
@@ -33,6 +36,9 @@ function resolveNativeAddonPath() {
|
|
|
33
36
|
return path.join(__dirname, "native", platformDir, "index.node");
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
const DEFAULT_POOL_DIR = path.join(os.homedir(), ".plasmite", "pools");
|
|
40
|
+
const DEFAULT_POOL_SIZE = 1024 * 1024;
|
|
41
|
+
const DEFAULT_POOL_SIZE_BYTES = DEFAULT_POOL_SIZE;
|
|
36
42
|
let native = null;
|
|
37
43
|
let nativeLoadError = null;
|
|
38
44
|
let nativeAddonPath = null;
|
|
@@ -48,6 +54,9 @@ try {
|
|
|
48
54
|
nativeLoadError = err;
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
const Durability = native ? native.Durability : Object.freeze({ Fast: 0, Flush: 1 });
|
|
58
|
+
const ErrorKind = native ? native.ErrorKind : ERROR_KIND_VALUES;
|
|
59
|
+
|
|
51
60
|
function makeNativeUnavailableError() {
|
|
52
61
|
const reason = nativeLoadError instanceof Error ? nativeLoadError.message : "unknown load error";
|
|
53
62
|
return new Error(
|
|
@@ -97,8 +106,15 @@ function parseNativeError(err) {
|
|
|
97
106
|
details[key] = Number.isFinite(parsed) ? parsed : undefined;
|
|
98
107
|
continue;
|
|
99
108
|
}
|
|
109
|
+
if (key === "kind") {
|
|
110
|
+
details.kind = mapErrorKind(value);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
100
113
|
details[key] = value;
|
|
101
114
|
}
|
|
115
|
+
if (details.kind === undefined) {
|
|
116
|
+
details.kind = ErrorKind.Io;
|
|
117
|
+
}
|
|
102
118
|
return new PlasmiteNativeError(err.message, details, err);
|
|
103
119
|
}
|
|
104
120
|
|
|
@@ -106,15 +122,33 @@ function wrapNativeError(err) {
|
|
|
106
122
|
return parseNativeError(err) ?? err;
|
|
107
123
|
}
|
|
108
124
|
|
|
125
|
+
function isNativeNotFoundError(err) {
|
|
126
|
+
if (!(err instanceof PlasmiteNativeError)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return err.kind === ErrorKind.NotFound;
|
|
130
|
+
}
|
|
131
|
+
|
|
109
132
|
class Client {
|
|
110
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Create a local client bound to a pool directory.
|
|
135
|
+
* @param {string} [poolDir]
|
|
136
|
+
* @returns {Client}
|
|
137
|
+
*/
|
|
138
|
+
constructor(poolDir = DEFAULT_POOL_DIR) {
|
|
111
139
|
if (!native) {
|
|
112
140
|
throw makeNativeUnavailableError();
|
|
113
141
|
}
|
|
114
142
|
this._inner = new native.Client(poolDir);
|
|
115
143
|
}
|
|
116
144
|
|
|
117
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Create a new pool.
|
|
147
|
+
* @param {string} poolRef
|
|
148
|
+
* @param {number|bigint} [sizeBytes]
|
|
149
|
+
* @returns {Pool}
|
|
150
|
+
*/
|
|
151
|
+
createPool(poolRef, sizeBytes = DEFAULT_POOL_SIZE_BYTES) {
|
|
118
152
|
try {
|
|
119
153
|
return new Pool(this._inner.createPool(poolRef, sizeBytes));
|
|
120
154
|
} catch (err) {
|
|
@@ -122,6 +156,11 @@ class Client {
|
|
|
122
156
|
}
|
|
123
157
|
}
|
|
124
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Open an existing pool.
|
|
161
|
+
* @param {string} poolRef
|
|
162
|
+
* @returns {Pool}
|
|
163
|
+
*/
|
|
125
164
|
openPool(poolRef) {
|
|
126
165
|
try {
|
|
127
166
|
return new Pool(this._inner.openPool(poolRef));
|
|
@@ -130,12 +169,41 @@ class Client {
|
|
|
130
169
|
}
|
|
131
170
|
}
|
|
132
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Open a pool if it exists, otherwise create it.
|
|
174
|
+
* @param {string} poolRef
|
|
175
|
+
* @param {number|bigint} [sizeBytes]
|
|
176
|
+
* @returns {Pool}
|
|
177
|
+
*/
|
|
178
|
+
pool(poolRef, sizeBytes = DEFAULT_POOL_SIZE_BYTES) {
|
|
179
|
+
try {
|
|
180
|
+
return this.openPool(poolRef);
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (isNativeNotFoundError(err)) {
|
|
183
|
+
return this.createPool(poolRef, sizeBytes);
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Close the client handle.
|
|
191
|
+
* @returns {void}
|
|
192
|
+
*/
|
|
133
193
|
close() {
|
|
134
194
|
this._inner.close();
|
|
135
195
|
}
|
|
196
|
+
|
|
197
|
+
[Symbol.dispose]() {
|
|
198
|
+
this.close();
|
|
199
|
+
}
|
|
136
200
|
}
|
|
137
201
|
|
|
138
202
|
class Pool {
|
|
203
|
+
/**
|
|
204
|
+
* @param {object} inner
|
|
205
|
+
* @returns {Pool}
|
|
206
|
+
*/
|
|
139
207
|
constructor(inner) {
|
|
140
208
|
if (!native) {
|
|
141
209
|
throw makeNativeUnavailableError();
|
|
@@ -143,22 +211,54 @@ class Pool {
|
|
|
143
211
|
this._inner = inner;
|
|
144
212
|
}
|
|
145
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Append JSON bytes and return raw message bytes.
|
|
216
|
+
* @param {unknown} payload
|
|
217
|
+
* @param {string[]} [tags]
|
|
218
|
+
* @param {number} [durability]
|
|
219
|
+
* @returns {Buffer}
|
|
220
|
+
*/
|
|
146
221
|
appendJson(payload, tags, durability) {
|
|
222
|
+
const input = Buffer.isBuffer(payload)
|
|
223
|
+
? payload
|
|
224
|
+
: Buffer.from(JSON.stringify(payload));
|
|
147
225
|
try {
|
|
148
|
-
return this._inner.appendJson(
|
|
226
|
+
return this._inner.appendJson(input, tags ?? [], durability ?? Durability.Fast);
|
|
149
227
|
} catch (err) {
|
|
150
228
|
throw wrapNativeError(err);
|
|
151
229
|
}
|
|
152
230
|
}
|
|
153
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Append payload and return parsed Message.
|
|
234
|
+
* @param {unknown} payload
|
|
235
|
+
* @param {string[]} [tags]
|
|
236
|
+
* @param {number} [durability]
|
|
237
|
+
* @returns {Message}
|
|
238
|
+
*/
|
|
239
|
+
append(payload, tags, durability) {
|
|
240
|
+
return parseMessage(this.appendJson(payload, tags, durability));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Append a Lite3 frame payload.
|
|
245
|
+
* @param {Buffer} payload
|
|
246
|
+
* @param {number} [durability]
|
|
247
|
+
* @returns {bigint}
|
|
248
|
+
*/
|
|
154
249
|
appendLite3(payload, durability) {
|
|
155
250
|
try {
|
|
156
|
-
return this._inner.appendLite3(payload, durability);
|
|
251
|
+
return this._inner.appendLite3(payload, durability ?? Durability.Fast);
|
|
157
252
|
} catch (err) {
|
|
158
253
|
throw wrapNativeError(err);
|
|
159
254
|
}
|
|
160
255
|
}
|
|
161
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Get raw message bytes by sequence.
|
|
259
|
+
* @param {number|bigint} seq
|
|
260
|
+
* @returns {Buffer}
|
|
261
|
+
*/
|
|
162
262
|
getJson(seq) {
|
|
163
263
|
try {
|
|
164
264
|
return this._inner.getJson(seq);
|
|
@@ -167,14 +267,35 @@ class Pool {
|
|
|
167
267
|
}
|
|
168
268
|
}
|
|
169
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Get parsed Message by sequence.
|
|
272
|
+
* @param {number|bigint} seq
|
|
273
|
+
* @returns {Message}
|
|
274
|
+
*/
|
|
275
|
+
get(seq) {
|
|
276
|
+
return parseMessage(this.getJson(seq));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get Lite3 frame by sequence.
|
|
281
|
+
* @param {number|bigint} seq
|
|
282
|
+
* @returns {import("./types").Lite3Frame}
|
|
283
|
+
*/
|
|
170
284
|
getLite3(seq) {
|
|
171
285
|
try {
|
|
172
|
-
return this._inner.getLite3(seq);
|
|
286
|
+
return decorateLite3Frame(this._inner.getLite3(seq));
|
|
173
287
|
} catch (err) {
|
|
174
288
|
throw wrapNativeError(err);
|
|
175
289
|
}
|
|
176
290
|
}
|
|
177
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Open a raw JSON stream.
|
|
294
|
+
* @param {number|bigint|null} sinceSeq
|
|
295
|
+
* @param {number|bigint|null} maxMessages
|
|
296
|
+
* @param {number|bigint|null} timeoutMs
|
|
297
|
+
* @returns {Stream}
|
|
298
|
+
*/
|
|
178
299
|
openStream(sinceSeq, maxMessages, timeoutMs) {
|
|
179
300
|
try {
|
|
180
301
|
return new Stream(this._inner.openStream(sinceSeq, maxMessages, timeoutMs));
|
|
@@ -183,6 +304,13 @@ class Pool {
|
|
|
183
304
|
}
|
|
184
305
|
}
|
|
185
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Open a raw Lite3 stream.
|
|
309
|
+
* @param {number|bigint|null} sinceSeq
|
|
310
|
+
* @param {number|bigint|null} maxMessages
|
|
311
|
+
* @param {number|bigint|null} timeoutMs
|
|
312
|
+
* @returns {Lite3Stream}
|
|
313
|
+
*/
|
|
186
314
|
openLite3Stream(sinceSeq, maxMessages, timeoutMs) {
|
|
187
315
|
try {
|
|
188
316
|
return new Lite3Stream(
|
|
@@ -193,12 +321,217 @@ class Pool {
|
|
|
193
321
|
}
|
|
194
322
|
}
|
|
195
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Tail parsed messages with optional filtering.
|
|
326
|
+
* @param {{sinceSeq?: number|bigint, maxMessages?: number|bigint, timeoutMs?: number|bigint, tags?: string[]}} [options]
|
|
327
|
+
* @returns {AsyncGenerator<Message, void, unknown>}
|
|
328
|
+
*/
|
|
329
|
+
async *tail(options = {}) {
|
|
330
|
+
const { sinceSeq, maxMessages, timeoutMs, tags } = options;
|
|
331
|
+
const requiredTags = normalizeTagFilter(tags);
|
|
332
|
+
const limit = normalizeOptionalCount(maxMessages, "maxMessages");
|
|
333
|
+
const pollTimeoutMs = normalizePollingTimeout(timeoutMs);
|
|
334
|
+
|
|
335
|
+
let delivered = 0;
|
|
336
|
+
let cursor = sinceSeq ?? null;
|
|
337
|
+
while (true) {
|
|
338
|
+
if (limit !== null && delivered >= limit) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const remaining = limit === null ? null : limit - delivered;
|
|
343
|
+
const streamLimit = requiredTags.length && remaining !== null ? null : remaining;
|
|
344
|
+
const stream = this.openStream(cursor, streamLimit, pollTimeoutMs);
|
|
345
|
+
let sawRawMessage = false;
|
|
346
|
+
try {
|
|
347
|
+
for (const message of stream) {
|
|
348
|
+
sawRawMessage = true;
|
|
349
|
+
const parsed = parseMessage(message);
|
|
350
|
+
cursor = nextSinceSeq(parsed, cursor);
|
|
351
|
+
if (!messageHasTags(parsed, requiredTags)) {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
delivered += 1;
|
|
355
|
+
yield parsed;
|
|
356
|
+
if (limit !== null && delivered >= limit) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
} finally {
|
|
361
|
+
stream.close();
|
|
362
|
+
}
|
|
363
|
+
if (limit !== null && !sawRawMessage) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
await sleep(0);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Replay parsed messages with original timing.
|
|
372
|
+
* @param {{speed?: number, sinceSeq?: number|bigint, maxMessages?: number|bigint, timeoutMs?: number|bigint, tags?: string[]}} [options]
|
|
373
|
+
* @returns {AsyncGenerator<Message, void, unknown>}
|
|
374
|
+
*/
|
|
375
|
+
async *replay(options = {}) {
|
|
376
|
+
const { speed = 1.0, sinceSeq, maxMessages, timeoutMs, tags } = options;
|
|
377
|
+
if (speed <= 0) {
|
|
378
|
+
throw new Error("speed must be positive");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const requiredTags = normalizeTagFilter(tags);
|
|
382
|
+
const limit = normalizeOptionalCount(maxMessages, "maxMessages");
|
|
383
|
+
const pollTimeoutMs = normalizePollingTimeout(timeoutMs);
|
|
384
|
+
const streamLimit = requiredTags.length && limit !== null ? null : limit;
|
|
385
|
+
const stream = this.openStream(
|
|
386
|
+
sinceSeq ?? null,
|
|
387
|
+
streamLimit,
|
|
388
|
+
pollTimeoutMs,
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
const messages = [];
|
|
392
|
+
try {
|
|
393
|
+
for (const message of stream) {
|
|
394
|
+
const parsed = parseMessage(message);
|
|
395
|
+
if (!messageHasTags(parsed, requiredTags)) {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
messages.push({
|
|
399
|
+
message: parsed,
|
|
400
|
+
timeMs: messageTimeMs(parsed),
|
|
401
|
+
});
|
|
402
|
+
if (limit !== null && messages.length >= limit) {
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} finally {
|
|
407
|
+
stream.close();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
let prevMs = null;
|
|
411
|
+
for (const entry of messages) {
|
|
412
|
+
if (prevMs !== null && entry.timeMs !== null && speed > 0) {
|
|
413
|
+
const delay = (entry.timeMs - prevMs) / speed;
|
|
414
|
+
if (delay > 0) {
|
|
415
|
+
await sleep(delay);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (entry.timeMs !== null) {
|
|
419
|
+
prevMs = entry.timeMs;
|
|
420
|
+
}
|
|
421
|
+
yield entry.message;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Close the pool handle.
|
|
427
|
+
* @returns {void}
|
|
428
|
+
*/
|
|
196
429
|
close() {
|
|
197
430
|
this._inner.close();
|
|
198
431
|
}
|
|
432
|
+
|
|
433
|
+
[Symbol.dispose]() {
|
|
434
|
+
this.close();
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function normalizeTagFilter(tags) {
|
|
439
|
+
if (tags === undefined || tags === null) {
|
|
440
|
+
return [];
|
|
441
|
+
}
|
|
442
|
+
return Array.isArray(tags) ? tags : [tags];
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function messageHasTags(message, requiredTags) {
|
|
446
|
+
if (!requiredTags.length) {
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
const messageTags = message && message.meta && Array.isArray(message.meta.tags)
|
|
450
|
+
? message.meta.tags
|
|
451
|
+
: null;
|
|
452
|
+
if (!messageTags) {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
return requiredTags.every((tag) => messageTags.includes(tag));
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function messageTimeMs(message) {
|
|
459
|
+
if (!message || !(message.time instanceof Date)) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
const value = message.time.getTime();
|
|
463
|
+
return Number.isFinite(value) ? value : null;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function nextSinceSeq(message, fallback) {
|
|
467
|
+
if (!message) {
|
|
468
|
+
return fallback;
|
|
469
|
+
}
|
|
470
|
+
if (typeof message.seq === "bigint") {
|
|
471
|
+
return message.seq + 1n;
|
|
472
|
+
}
|
|
473
|
+
if (typeof message.seq === "number" && Number.isFinite(message.seq)) {
|
|
474
|
+
return message.seq + 1;
|
|
475
|
+
}
|
|
476
|
+
return fallback;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function decorateLite3Frame(frame) {
|
|
480
|
+
if (!frame || typeof frame !== "object") {
|
|
481
|
+
return frame;
|
|
482
|
+
}
|
|
483
|
+
if (!Object.prototype.hasOwnProperty.call(frame, "time")) {
|
|
484
|
+
Object.defineProperty(frame, "time", {
|
|
485
|
+
configurable: false,
|
|
486
|
+
enumerable: true,
|
|
487
|
+
get() {
|
|
488
|
+
return new Date(Number(this.timestampNs) / 1_000_000);
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
return frame;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function normalizeOptionalCount(value, fieldName) {
|
|
496
|
+
if (value === undefined || value === null) {
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
if (typeof value === "bigint") {
|
|
500
|
+
if (value < 0n) {
|
|
501
|
+
throw new TypeError(`${fieldName} must be non-negative`);
|
|
502
|
+
}
|
|
503
|
+
return Number(value);
|
|
504
|
+
}
|
|
505
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
506
|
+
throw new TypeError(`${fieldName} must be non-negative`);
|
|
507
|
+
}
|
|
508
|
+
return Math.floor(value);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function normalizePollingTimeout(timeoutMs) {
|
|
512
|
+
if (timeoutMs === undefined || timeoutMs === null) {
|
|
513
|
+
return 1000;
|
|
514
|
+
}
|
|
515
|
+
if (typeof timeoutMs === "bigint") {
|
|
516
|
+
if (timeoutMs <= 0n) {
|
|
517
|
+
return 1000;
|
|
518
|
+
}
|
|
519
|
+
return Number(timeoutMs);
|
|
520
|
+
}
|
|
521
|
+
if (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs)) {
|
|
522
|
+
throw new TypeError("timeoutMs must be numeric");
|
|
523
|
+
}
|
|
524
|
+
if (timeoutMs <= 0) {
|
|
525
|
+
return 1000;
|
|
526
|
+
}
|
|
527
|
+
return timeoutMs;
|
|
199
528
|
}
|
|
200
529
|
|
|
201
530
|
class Stream {
|
|
531
|
+
/**
|
|
532
|
+
* @param {object} inner
|
|
533
|
+
* @returns {Stream}
|
|
534
|
+
*/
|
|
202
535
|
constructor(inner) {
|
|
203
536
|
if (!native) {
|
|
204
537
|
throw makeNativeUnavailableError();
|
|
@@ -206,6 +539,10 @@ class Stream {
|
|
|
206
539
|
this._inner = inner;
|
|
207
540
|
}
|
|
208
541
|
|
|
542
|
+
/**
|
|
543
|
+
* Read the next raw JSON message.
|
|
544
|
+
* @returns {Buffer|null}
|
|
545
|
+
*/
|
|
209
546
|
nextJson() {
|
|
210
547
|
try {
|
|
211
548
|
return this._inner.nextJson();
|
|
@@ -214,12 +551,35 @@ class Stream {
|
|
|
214
551
|
}
|
|
215
552
|
}
|
|
216
553
|
|
|
554
|
+
/**
|
|
555
|
+
* Iterate raw JSON messages.
|
|
556
|
+
* @returns {Iterator<Buffer>}
|
|
557
|
+
*/
|
|
558
|
+
*[Symbol.iterator]() {
|
|
559
|
+
let message;
|
|
560
|
+
while ((message = this.nextJson()) !== null) {
|
|
561
|
+
yield message;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Close the stream handle.
|
|
567
|
+
* @returns {void}
|
|
568
|
+
*/
|
|
217
569
|
close() {
|
|
218
570
|
this._inner.close();
|
|
219
571
|
}
|
|
572
|
+
|
|
573
|
+
[Symbol.dispose]() {
|
|
574
|
+
this.close();
|
|
575
|
+
}
|
|
220
576
|
}
|
|
221
577
|
|
|
222
578
|
class Lite3Stream {
|
|
579
|
+
/**
|
|
580
|
+
* @param {object} inner
|
|
581
|
+
* @returns {Lite3Stream}
|
|
582
|
+
*/
|
|
223
583
|
constructor(inner) {
|
|
224
584
|
if (!native) {
|
|
225
585
|
throw makeNativeUnavailableError();
|
|
@@ -227,52 +587,50 @@ class Lite3Stream {
|
|
|
227
587
|
this._inner = inner;
|
|
228
588
|
}
|
|
229
589
|
|
|
590
|
+
/**
|
|
591
|
+
* Read the next Lite3 frame.
|
|
592
|
+
* @returns {import("./types").Lite3Frame|null}
|
|
593
|
+
*/
|
|
230
594
|
next() {
|
|
231
595
|
try {
|
|
232
|
-
return this._inner.next();
|
|
596
|
+
return decorateLite3Frame(this._inner.next());
|
|
233
597
|
} catch (err) {
|
|
234
598
|
throw wrapNativeError(err);
|
|
235
599
|
}
|
|
236
600
|
}
|
|
237
601
|
|
|
602
|
+
/**
|
|
603
|
+
* Iterate Lite3 frames.
|
|
604
|
+
* @returns {Iterator<import("./types").Lite3Frame>}
|
|
605
|
+
*/
|
|
606
|
+
*[Symbol.iterator]() {
|
|
607
|
+
let frame;
|
|
608
|
+
while ((frame = this.next()) !== null) {
|
|
609
|
+
yield frame;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Close the Lite3 stream handle.
|
|
615
|
+
* @returns {void}
|
|
616
|
+
*/
|
|
238
617
|
close() {
|
|
239
618
|
this._inner.close();
|
|
240
619
|
}
|
|
241
|
-
}
|
|
242
620
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const stream = pool.openStream(
|
|
246
|
-
sinceSeq ?? null,
|
|
247
|
-
maxMessages ?? null,
|
|
248
|
-
timeoutMs ?? null,
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
const messages = [];
|
|
252
|
-
try {
|
|
253
|
-
let msg;
|
|
254
|
-
while ((msg = stream.nextJson()) !== null) {
|
|
255
|
-
messages.push(msg);
|
|
256
|
-
}
|
|
257
|
-
} finally {
|
|
258
|
-
stream.close();
|
|
621
|
+
[Symbol.dispose]() {
|
|
622
|
+
this.close();
|
|
259
623
|
}
|
|
624
|
+
}
|
|
260
625
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
await sleep(delay);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
prevMs = curMs;
|
|
274
|
-
yield msg;
|
|
275
|
-
}
|
|
626
|
+
/**
|
|
627
|
+
* Backward-compatible replay helper.
|
|
628
|
+
* @param {Pool} pool
|
|
629
|
+
* @param {object} [options]
|
|
630
|
+
* @returns {AsyncGenerator<Message, void, unknown>}
|
|
631
|
+
*/
|
|
632
|
+
async function* replay(pool, options = {}) {
|
|
633
|
+
yield* pool.replay(options);
|
|
276
634
|
}
|
|
277
635
|
|
|
278
636
|
module.exports = {
|
|
@@ -280,12 +638,16 @@ module.exports = {
|
|
|
280
638
|
Pool,
|
|
281
639
|
Stream,
|
|
282
640
|
Lite3Stream,
|
|
283
|
-
|
|
284
|
-
|
|
641
|
+
Message,
|
|
642
|
+
DEFAULT_POOL_DIR,
|
|
643
|
+
DEFAULT_POOL_SIZE,
|
|
644
|
+
DEFAULT_POOL_SIZE_BYTES,
|
|
645
|
+
Durability,
|
|
646
|
+
ErrorKind,
|
|
285
647
|
PlasmiteNativeError,
|
|
286
648
|
RemoteClient,
|
|
287
649
|
RemoteError,
|
|
288
650
|
RemotePool,
|
|
289
|
-
|
|
651
|
+
parseMessage,
|
|
290
652
|
replay,
|
|
291
653
|
};
|
package/mappings.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Purpose: Centralize Node binding value mappings shared across local and remote paths.
|
|
3
|
+
Key Exports: ERROR_KIND_VALUES, mapErrorKind, mapDurability.
|
|
4
|
+
Role: Keep error-kind and durability normalization behavior consistent.
|
|
5
|
+
Invariants: Error kind names map to stable numeric values for v0 semantics.
|
|
6
|
+
Invariants: Durability accepts fast/flush and numeric enum aliases 0/1.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const ERROR_KIND_VALUES = Object.freeze({
|
|
10
|
+
Internal: 1,
|
|
11
|
+
Usage: 2,
|
|
12
|
+
NotFound: 3,
|
|
13
|
+
AlreadyExists: 4,
|
|
14
|
+
Busy: 5,
|
|
15
|
+
Permission: 6,
|
|
16
|
+
Corrupt: 7,
|
|
17
|
+
Io: 8,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const DURABILITY_VALUES = Object.freeze({
|
|
21
|
+
fast: "fast",
|
|
22
|
+
flush: "flush",
|
|
23
|
+
0: "fast",
|
|
24
|
+
1: "flush",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
function mapErrorKind(value, fallback = undefined) {
|
|
28
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === "string") {
|
|
32
|
+
const mapped = ERROR_KIND_VALUES[value];
|
|
33
|
+
if (mapped !== undefined) {
|
|
34
|
+
return mapped;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function mapDurability(value) {
|
|
41
|
+
if (value === undefined || value === null) {
|
|
42
|
+
return "fast";
|
|
43
|
+
}
|
|
44
|
+
const mapped = DURABILITY_VALUES[String(value).toLowerCase()];
|
|
45
|
+
if (mapped) {
|
|
46
|
+
return mapped;
|
|
47
|
+
}
|
|
48
|
+
throw new TypeError("durability must be Durability.Fast or Durability.Flush");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
ERROR_KIND_VALUES,
|
|
53
|
+
mapErrorKind,
|
|
54
|
+
mapDurability,
|
|
55
|
+
};
|