@rljson/server 0.0.7 → 0.0.9
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.architecture.md +2 -0
- package/README.public.md +11 -8
- package/dist/README.architecture.md +2 -0
- package/dist/README.public.md +11 -8
- package/dist/file-logger.d.ts +63 -0
- package/dist/index.d.ts +2 -0
- package/dist/server.js +113 -0
- package/package.json +9 -9
package/README.architecture.md
CHANGED
|
@@ -134,6 +134,8 @@ The `Server` class acts as a central coordination point that:
|
|
|
134
134
|
- Broadcasts notifications between clients
|
|
135
135
|
- Provides read access to its own local storage
|
|
136
136
|
|
|
137
|
+
**Relay mode (`disableLocalCache: true`):** When the `disableLocalCache` option is set, the server omits the local IoMem/BsMem from its IoMulti/BsMulti stacks. In this mode the server reads all data exclusively from connected client peers, acting as a pure relay without caching any data locally. This is useful for memory-constrained deployments where the server should not retain copies of client data.
|
|
138
|
+
|
|
137
139
|
**Data Flow Architecture:**
|
|
138
140
|
|
|
139
141
|
```text
|
package/README.public.md
CHANGED
|
@@ -176,6 +176,7 @@ This is implemented with `IoMulti` and `BsMulti` internally, but the public API
|
|
|
176
176
|
- `bs` – Bs interface used by server
|
|
177
177
|
- `clients` – `Map` of connected clients (keyed by internal clientId)
|
|
178
178
|
- `isTornDown` – whether the server has been shut down
|
|
179
|
+
- `isLocalCacheDisabled` – whether local caching is disabled (pure relay mode)
|
|
179
180
|
- `logger` – the `ServerLogger` instance (defaults to `noopLogger`)
|
|
180
181
|
|
|
181
182
|
## Example
|
|
@@ -306,17 +307,19 @@ const server = new Server(route, io, bs, {
|
|
|
306
307
|
logger: new ConsoleLogger(), // Structured logging (default: NoopLogger)
|
|
307
308
|
refEvictionIntervalMs: 60_000, // Ref dedup sweep interval (default: 60 s, 0 = disable)
|
|
308
309
|
peerInitTimeoutMs: 30_000, // Peer handshake timeout (default: 30 s, 0 = disable)
|
|
310
|
+
disableLocalCache: true, // Pure relay mode — no local Io/Bs cache (default: false)
|
|
309
311
|
});
|
|
310
312
|
```
|
|
311
313
|
|
|
312
|
-
| Option | Default | Description
|
|
313
|
-
| ----------------------- | ---------- |
|
|
314
|
-
| `logger` | NoopLogger | Structured logger for lifecycle, traffic, and error events.
|
|
315
|
-
| `refEvictionIntervalMs` | 60 000 | Two-generation sweep interval for multicast ref dedup. Refs older than two intervals are forgotten, preventing unbounded memory growth.
|
|
316
|
-
| `peerInitTimeoutMs` | 30 000 | Maximum time `addSocket()` waits for a peer to initialize. Prevents hanging on unresponsive clients.
|
|
317
|
-
| `syncConfig` | undefined | Sync protocol configuration (see below). Enables ACK aggregation, gap-fill, and enriched payloads.
|
|
318
|
-
| `refLogSize` | 1 000 | Maximum number of recent payloads retained in the ref log for gap-fill responses.
|
|
319
|
-
| `ackTimeoutMs` | 10 000 | Timeout for collecting individual client ACKs before emitting the aggregated ACK. Falls back to `syncConfig.ackTimeoutMs`.
|
|
314
|
+
| Option | Default | Description |
|
|
315
|
+
| ----------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
316
|
+
| `logger` | NoopLogger | Structured logger for lifecycle, traffic, and error events. |
|
|
317
|
+
| `refEvictionIntervalMs` | 60 000 | Two-generation sweep interval for multicast ref dedup. Refs older than two intervals are forgotten, preventing unbounded memory growth. |
|
|
318
|
+
| `peerInitTimeoutMs` | 30 000 | Maximum time `addSocket()` waits for a peer to initialize. Prevents hanging on unresponsive clients. |
|
|
319
|
+
| `syncConfig` | undefined | Sync protocol configuration (see below). Enables ACK aggregation, gap-fill, and enriched payloads. |
|
|
320
|
+
| `refLogSize` | 1 000 | Maximum number of recent payloads retained in the ref log for gap-fill responses. |
|
|
321
|
+
| `ackTimeoutMs` | 10 000 | Timeout for collecting individual client ACKs before emitting the aggregated ACK. Falls back to `syncConfig.ackTimeoutMs`. |
|
|
322
|
+
| `disableLocalCache` | false | When true, the server skips creating local IoMem/BsMem caches. The server acts as a pure relay, reading data only from connected client peers. Useful for memory-constrained deployments. |
|
|
320
323
|
|
|
321
324
|
## Client options
|
|
322
325
|
|
|
@@ -134,6 +134,8 @@ The `Server` class acts as a central coordination point that:
|
|
|
134
134
|
- Broadcasts notifications between clients
|
|
135
135
|
- Provides read access to its own local storage
|
|
136
136
|
|
|
137
|
+
**Relay mode (`disableLocalCache: true`):** When the `disableLocalCache` option is set, the server omits the local IoMem/BsMem from its IoMulti/BsMulti stacks. In this mode the server reads all data exclusively from connected client peers, acting as a pure relay without caching any data locally. This is useful for memory-constrained deployments where the server should not retain copies of client data.
|
|
138
|
+
|
|
137
139
|
**Data Flow Architecture:**
|
|
138
140
|
|
|
139
141
|
```text
|
package/dist/README.public.md
CHANGED
|
@@ -176,6 +176,7 @@ This is implemented with `IoMulti` and `BsMulti` internally, but the public API
|
|
|
176
176
|
- `bs` – Bs interface used by server
|
|
177
177
|
- `clients` – `Map` of connected clients (keyed by internal clientId)
|
|
178
178
|
- `isTornDown` – whether the server has been shut down
|
|
179
|
+
- `isLocalCacheDisabled` – whether local caching is disabled (pure relay mode)
|
|
179
180
|
- `logger` – the `ServerLogger` instance (defaults to `noopLogger`)
|
|
180
181
|
|
|
181
182
|
## Example
|
|
@@ -306,17 +307,19 @@ const server = new Server(route, io, bs, {
|
|
|
306
307
|
logger: new ConsoleLogger(), // Structured logging (default: NoopLogger)
|
|
307
308
|
refEvictionIntervalMs: 60_000, // Ref dedup sweep interval (default: 60 s, 0 = disable)
|
|
308
309
|
peerInitTimeoutMs: 30_000, // Peer handshake timeout (default: 30 s, 0 = disable)
|
|
310
|
+
disableLocalCache: true, // Pure relay mode — no local Io/Bs cache (default: false)
|
|
309
311
|
});
|
|
310
312
|
```
|
|
311
313
|
|
|
312
|
-
| Option | Default | Description
|
|
313
|
-
| ----------------------- | ---------- |
|
|
314
|
-
| `logger` | NoopLogger | Structured logger for lifecycle, traffic, and error events.
|
|
315
|
-
| `refEvictionIntervalMs` | 60 000 | Two-generation sweep interval for multicast ref dedup. Refs older than two intervals are forgotten, preventing unbounded memory growth.
|
|
316
|
-
| `peerInitTimeoutMs` | 30 000 | Maximum time `addSocket()` waits for a peer to initialize. Prevents hanging on unresponsive clients.
|
|
317
|
-
| `syncConfig` | undefined | Sync protocol configuration (see below). Enables ACK aggregation, gap-fill, and enriched payloads.
|
|
318
|
-
| `refLogSize` | 1 000 | Maximum number of recent payloads retained in the ref log for gap-fill responses.
|
|
319
|
-
| `ackTimeoutMs` | 10 000 | Timeout for collecting individual client ACKs before emitting the aggregated ACK. Falls back to `syncConfig.ackTimeoutMs`.
|
|
314
|
+
| Option | Default | Description |
|
|
315
|
+
| ----------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
316
|
+
| `logger` | NoopLogger | Structured logger for lifecycle, traffic, and error events. |
|
|
317
|
+
| `refEvictionIntervalMs` | 60 000 | Two-generation sweep interval for multicast ref dedup. Refs older than two intervals are forgotten, preventing unbounded memory growth. |
|
|
318
|
+
| `peerInitTimeoutMs` | 30 000 | Maximum time `addSocket()` waits for a peer to initialize. Prevents hanging on unresponsive clients. |
|
|
319
|
+
| `syncConfig` | undefined | Sync protocol configuration (see below). Enables ACK aggregation, gap-fill, and enriched payloads. |
|
|
320
|
+
| `refLogSize` | 1 000 | Maximum number of recent payloads retained in the ref log for gap-fill responses. |
|
|
321
|
+
| `ackTimeoutMs` | 10 000 | Timeout for collecting individual client ACKs before emitting the aggregated ACK. Falls back to `syncConfig.ackTimeoutMs`. |
|
|
322
|
+
| `disableLocalCache` | false | When true, the server skips creating local IoMem/BsMem caches. The server acts as a pure relay, reading data only from connected client peers. Useful for memory-constrained deployments. |
|
|
320
323
|
|
|
321
324
|
## Client options
|
|
322
325
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ServerLogger } from './logger.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Options for creating a FileLogger.
|
|
4
|
+
*/
|
|
5
|
+
export interface FileLoggerOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Absolute path to the log file.
|
|
8
|
+
* Parent directories are created automatically if they don't exist.
|
|
9
|
+
*/
|
|
10
|
+
filePath: string;
|
|
11
|
+
/**
|
|
12
|
+
* When `true`, log entries are also written to console
|
|
13
|
+
* (info/traffic → console.log, warn → console.warn, error → console.error).
|
|
14
|
+
* Defaults to `false`.
|
|
15
|
+
*/
|
|
16
|
+
echo?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Appends log entries to a file. Optionally echoes to console.
|
|
20
|
+
*
|
|
21
|
+
* Each line is a self-contained JSON object for easy parsing:
|
|
22
|
+
* ```
|
|
23
|
+
* {"ts":"2026-02-25T12:00:00.000Z","level":"info","source":"Server","message":"initialized","data":{"port":3000}}
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Parent directories are created on construction if they don't exist.
|
|
27
|
+
* Uses synchronous writes (`appendFileSync`) to guarantee ordering
|
|
28
|
+
* and avoid lost entries on crash.
|
|
29
|
+
*/
|
|
30
|
+
export declare class FileLogger implements ServerLogger {
|
|
31
|
+
private readonly _filePath;
|
|
32
|
+
private readonly _echo;
|
|
33
|
+
constructor(options: FileLoggerOptions);
|
|
34
|
+
/** The file path this logger writes to. */
|
|
35
|
+
get filePath(): string;
|
|
36
|
+
/** Whether console echo is enabled. */
|
|
37
|
+
get echo(): boolean;
|
|
38
|
+
info(source: string, message: string, data?: Record<string, unknown>): void;
|
|
39
|
+
warn(source: string, message: string, data?: Record<string, unknown>): void;
|
|
40
|
+
error(source: string, message: string, error?: unknown, data?: Record<string, unknown>): void;
|
|
41
|
+
traffic(direction: 'in' | 'out', source: string, event: string, data?: Record<string, unknown>): void;
|
|
42
|
+
/**
|
|
43
|
+
* Build a standard log entry object.
|
|
44
|
+
* @param level - Log level string
|
|
45
|
+
* @param source - Component identifier
|
|
46
|
+
* @param message - Human-readable message
|
|
47
|
+
* @param data - Optional structured context
|
|
48
|
+
*/
|
|
49
|
+
private _entry;
|
|
50
|
+
/**
|
|
51
|
+
* Append a JSON line to the log file.
|
|
52
|
+
* @param entry - The log entry object to serialize
|
|
53
|
+
*/
|
|
54
|
+
private _write;
|
|
55
|
+
/**
|
|
56
|
+
* Format a human-readable console line.
|
|
57
|
+
* @param level - Display level label (e.g. 'INFO')
|
|
58
|
+
* @param source - Component identifier
|
|
59
|
+
* @param message - Human-readable message
|
|
60
|
+
* @param data - Optional structured context
|
|
61
|
+
*/
|
|
62
|
+
private _format;
|
|
63
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { Client } from './client.ts';
|
|
2
2
|
export type { ClientOptions } from './client.ts';
|
|
3
|
+
export { FileLogger } from './file-logger.ts';
|
|
4
|
+
export type { FileLoggerOptions } from './file-logger.ts';
|
|
3
5
|
export { BufferedLogger, ConsoleLogger, FilteredLogger, NoopLogger, noopLogger, } from './logger.ts';
|
|
4
6
|
export type { LogEntry, ServerLogger } from './logger.ts';
|
|
5
7
|
export { Server } from './server.ts';
|
package/dist/server.js
CHANGED
|
@@ -2,6 +2,8 @@ import { hshBuffer } from "@rljson/hash";
|
|
|
2
2
|
import { Readable } from "node:stream";
|
|
3
3
|
import { Db, Connector } from "@rljson/db";
|
|
4
4
|
import { IoPeerBridge, IoMulti, IoPeer, IoServer, IoMem, SocketMock } from "@rljson/io";
|
|
5
|
+
import { existsSync, mkdirSync, appendFileSync } from "node:fs";
|
|
6
|
+
import { dirname } from "node:path";
|
|
5
7
|
import { syncEvents, Route } from "@rljson/rljson";
|
|
6
8
|
import { syncEvents as syncEvents2 } from "@rljson/rljson";
|
|
7
9
|
class BsMem {
|
|
@@ -1399,6 +1401,116 @@ class Client extends BaseNode {
|
|
|
1399
1401
|
);
|
|
1400
1402
|
}
|
|
1401
1403
|
}
|
|
1404
|
+
class FileLogger {
|
|
1405
|
+
_filePath;
|
|
1406
|
+
_echo;
|
|
1407
|
+
constructor(options) {
|
|
1408
|
+
this._filePath = options.filePath;
|
|
1409
|
+
this._echo = options.echo ?? false;
|
|
1410
|
+
const dir = dirname(this._filePath);
|
|
1411
|
+
if (!existsSync(dir)) {
|
|
1412
|
+
mkdirSync(dir, { recursive: true });
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
/** The file path this logger writes to. */
|
|
1416
|
+
get filePath() {
|
|
1417
|
+
return this._filePath;
|
|
1418
|
+
}
|
|
1419
|
+
/** Whether console echo is enabled. */
|
|
1420
|
+
get echo() {
|
|
1421
|
+
return this._echo;
|
|
1422
|
+
}
|
|
1423
|
+
info(source, message, data) {
|
|
1424
|
+
const entry = this._entry("info", source, message, data);
|
|
1425
|
+
this._write(entry);
|
|
1426
|
+
if (this._echo) {
|
|
1427
|
+
console.log(this._format("INFO", source, message, data));
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
warn(source, message, data) {
|
|
1431
|
+
const entry = this._entry("warn", source, message, data);
|
|
1432
|
+
this._write(entry);
|
|
1433
|
+
if (this._echo) {
|
|
1434
|
+
console.warn(this._format("WARN", source, message, data));
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
error(source, message, error, data) {
|
|
1438
|
+
const entry = {
|
|
1439
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1440
|
+
level: "error",
|
|
1441
|
+
source,
|
|
1442
|
+
message
|
|
1443
|
+
};
|
|
1444
|
+
if (error !== void 0) {
|
|
1445
|
+
entry["error"] = error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : String(error);
|
|
1446
|
+
}
|
|
1447
|
+
if (data !== void 0) {
|
|
1448
|
+
entry["data"] = data;
|
|
1449
|
+
}
|
|
1450
|
+
this._write(entry);
|
|
1451
|
+
if (this._echo) {
|
|
1452
|
+
const errStr = error ? ` ${error}` : "";
|
|
1453
|
+
const dataStr = data ? " " + JSON.stringify(data) : "";
|
|
1454
|
+
console.error(`[ERROR] [${source}] ${message}${errStr}${dataStr}`);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
traffic(direction, source, event, data) {
|
|
1458
|
+
const entry = {
|
|
1459
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1460
|
+
level: "traffic",
|
|
1461
|
+
direction,
|
|
1462
|
+
source,
|
|
1463
|
+
event
|
|
1464
|
+
};
|
|
1465
|
+
if (data !== void 0) {
|
|
1466
|
+
entry["data"] = data;
|
|
1467
|
+
}
|
|
1468
|
+
this._write(entry);
|
|
1469
|
+
if (this._echo) {
|
|
1470
|
+
const arrow = direction === "in" ? "⬅" : "➡";
|
|
1471
|
+
const dataStr = data ? " " + JSON.stringify(data) : "";
|
|
1472
|
+
console.log(`[TRAFFIC] ${arrow} [${source}] ${event}${dataStr}`);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
// ...........................................................................
|
|
1476
|
+
/**
|
|
1477
|
+
* Build a standard log entry object.
|
|
1478
|
+
* @param level - Log level string
|
|
1479
|
+
* @param source - Component identifier
|
|
1480
|
+
* @param message - Human-readable message
|
|
1481
|
+
* @param data - Optional structured context
|
|
1482
|
+
*/
|
|
1483
|
+
_entry(level, source, message, data) {
|
|
1484
|
+
const entry = {
|
|
1485
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1486
|
+
level,
|
|
1487
|
+
source,
|
|
1488
|
+
message
|
|
1489
|
+
};
|
|
1490
|
+
if (data !== void 0) {
|
|
1491
|
+
entry["data"] = data;
|
|
1492
|
+
}
|
|
1493
|
+
return entry;
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Append a JSON line to the log file.
|
|
1497
|
+
* @param entry - The log entry object to serialize
|
|
1498
|
+
*/
|
|
1499
|
+
_write(entry) {
|
|
1500
|
+
appendFileSync(this._filePath, JSON.stringify(entry) + "\n", "utf-8");
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Format a human-readable console line.
|
|
1504
|
+
* @param level - Display level label (e.g. 'INFO')
|
|
1505
|
+
* @param source - Component identifier
|
|
1506
|
+
* @param message - Human-readable message
|
|
1507
|
+
* @param data - Optional structured context
|
|
1508
|
+
*/
|
|
1509
|
+
_format(level, source, message, data) {
|
|
1510
|
+
const dataStr = data ? " " + JSON.stringify(data) : "";
|
|
1511
|
+
return `[${level}] [${source}] ${message}${dataStr}`;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1402
1514
|
class Server extends BaseNode {
|
|
1403
1515
|
constructor(_route, _localIo, _localBs, options) {
|
|
1404
1516
|
super(_localIo);
|
|
@@ -2157,6 +2269,7 @@ export {
|
|
|
2157
2269
|
BufferedLogger,
|
|
2158
2270
|
Client,
|
|
2159
2271
|
ConsoleLogger,
|
|
2272
|
+
FileLogger,
|
|
2160
2273
|
FilteredLogger,
|
|
2161
2274
|
NoopLogger,
|
|
2162
2275
|
Server,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rljson/server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Rljson server description",
|
|
5
5
|
"homepage": "https://github.com/rljson/server",
|
|
6
6
|
"bugs": "https://github.com/rljson/server/issues",
|
|
@@ -20,21 +20,21 @@
|
|
|
20
20
|
],
|
|
21
21
|
"type": "module",
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^25.
|
|
24
|
-
"@typescript-eslint/eslint-plugin": "^8.56.
|
|
25
|
-
"@typescript-eslint/parser": "^8.56.
|
|
23
|
+
"@types/node": "^25.3.0",
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
25
|
+
"@typescript-eslint/parser": "^8.56.1",
|
|
26
26
|
"@vitest/coverage-v8": "^4.0.18",
|
|
27
27
|
"cross-env": "^10.1.0",
|
|
28
28
|
"eslint": "~9.39.2",
|
|
29
|
-
"eslint-plugin-jsdoc": "^62.
|
|
30
|
-
"eslint-plugin-tsdoc": "^0.5.
|
|
29
|
+
"eslint-plugin-jsdoc": "^62.7.1",
|
|
30
|
+
"eslint-plugin-tsdoc": "^0.5.1",
|
|
31
31
|
"globals": "^17.3.0",
|
|
32
32
|
"jsdoc": "^4.0.5",
|
|
33
33
|
"read-pkg": "^10.1.0",
|
|
34
34
|
"socket.io": "^4.8.3",
|
|
35
35
|
"socket.io-client": "^4.8.3",
|
|
36
36
|
"typescript": "~5.9.3",
|
|
37
|
-
"typescript-eslint": "^8.56.
|
|
37
|
+
"typescript-eslint": "^8.56.1",
|
|
38
38
|
"vite": "^7.3.1",
|
|
39
39
|
"vite-node": "^5.3.0",
|
|
40
40
|
"vite-plugin-dts": "^4.5.4",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@rljson/bs": "^0.0.21",
|
|
47
|
-
"@rljson/db": "^0.0.
|
|
47
|
+
"@rljson/db": "^0.0.15",
|
|
48
48
|
"@rljson/hash": "^0.0.18",
|
|
49
49
|
"@rljson/io": "^0.0.66",
|
|
50
50
|
"@rljson/json": "^0.0.23",
|
|
51
|
-
"@rljson/rljson": "^0.0.
|
|
51
|
+
"@rljson/rljson": "^0.0.78"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "pnpm exec vite build && tsc && node scripts/copy-readme-to-dist.js",
|