@mark1russell7/harness 0.11.26 → 0.12.1

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.
@@ -1,96 +0,0 @@
1
- // server-ws --port N --tcp-keepalive off|N --heartbeat off|on [--heartbeat-interval-ms N --heartbeat-misses N]
2
- //
3
- // A cell's target: the procedures server of @procedures/client 0.7.0 (Server and
4
- // WebSocketServerTransport) on the http.Server of Node 24.21.0, at the path /ws. It has two
5
- // procedures: `harness.echo` answers at once, and `harness.hold` answers after `ms` milliseconds
6
- // (a held reply, as BLPOP is for Redis).
7
- //
8
- // --tcp-keepalive is the keepalive of each accepted socket: off, or its idle time in ms. Node's
9
- // default is off (keepAlive of http.Server), and the cell says it.
10
- //
11
- // --heartbeat is the heartbeat of the server (roadmap R3): a WebSocket ping to each connection at
12
- // each interval, and terminate() for a connection that missed `misses` pongs. Each setting is a
13
- // flag, and the server has no default. 0.7.0 takes each time as a duration, not a number. One
14
- // setInterval of the transport, from its start, beats for all connections: the first ping of a
15
- // connection comes at the next beat after its accept (transport.js:94-112, :147-151).
16
- //
17
- // Each event is a JSON line on the clock of the clients: a connection (`accepted`), a request,
18
- // a reply, and the close of a connection. The `ws` package does the WebSocket of the server. Its
19
- // `ping` and `terminate` write a line first (`ping`, `drop`): the heartbeat of the server pings its
20
- // clients and drops a silent one. Each line names the connection by its peer.
21
-
22
- import http from "node:http";
23
- import { WebSocket } from "ws";
24
- import { Server, WebSocketServerTransport } from "@procedures/client";
25
- import { bootId, commandLine, emit } from "./lines.mjs";
26
-
27
- const line = commandLine("server-ws --port N --tcp-keepalive off|N --heartbeat off|on [--heartbeat-interval-ms N --heartbeat-misses N]");
28
- const port = line.number("--port");
29
- const keepalive = line.required("--tcp-keepalive");
30
- const keepAliveMs = keepalive === "off" ? null : Number(keepalive);
31
- if (keepAliveMs !== null && !Number.isFinite(keepAliveMs)) line.fail("--tcp-keepalive is off or a number of ms");
32
- const heartbeat = line.toggle("--heartbeat")
33
- ? { interval: line.number("--heartbeat-interval-ms"), misses: line.number("--heartbeat-misses") }
34
- : null;
35
-
36
- const peerOf = (socket) => `${socket?.remoteAddress}:${socket?.remotePort}`;
37
-
38
- // the acts of a heartbeat of the server, in the package that the transport imports
39
- const ping = WebSocket.prototype.ping;
40
- WebSocket.prototype.ping = function (...args) {
41
- emit("ping", { peer: peerOf(this._socket) });
42
- return ping.apply(this, args);
43
- };
44
- const terminate = WebSocket.prototype.terminate;
45
- WebSocket.prototype.terminate = function (...args) {
46
- emit("drop", { peer: peerOf(this._socket) });
47
- return terminate.apply(this, args);
48
- };
49
-
50
- const ok = (request, payload) => ({ id: request.id, status: { type: "success", code: 200 }, payload, metadata: {} });
51
- const server = new Server();
52
- server.register({ service: "harness", operation: "echo" }, async (request) => {
53
- emit("request", { method: "harness.echo" });
54
- return ok(request, { ok: true });
55
- });
56
- server.register({ service: "harness", operation: "hold" }, async (request) => {
57
- const ms = Number(request.payload?.ms ?? 0);
58
- emit("request", { method: "harness.hold", ms });
59
- await new Promise((resolve) => {
60
- const timer = setTimeout(resolve, ms);
61
- request.signal?.addEventListener("abort", () => {
62
- clearTimeout(timer);
63
- resolve();
64
- });
65
- });
66
- emit("replied", { method: "harness.hold", aborted: request.signal?.aborted === true });
67
- return ok(request, { ok: true, held: ms });
68
- });
69
-
70
- const httpServer = http.createServer({
71
- keepAlive: keepAliveMs !== null,
72
- ...(keepAliveMs === null ? {} : { keepAliveInitialDelay: keepAliveMs }),
73
- });
74
- const transport = new WebSocketServerTransport(server, {
75
- server: httpServer,
76
- path: "/ws",
77
- heartbeat: heartbeat === null ? false : { interval: { value: heartbeat.interval, unit: "ms" }, misses: heartbeat.misses },
78
- onConnection: (ws, request) => {
79
- const peer = peerOf(request.socket);
80
- emit("accepted", { peer });
81
- ws.on("close", (code) => emit("closed", { peer, code }));
82
- },
83
- });
84
- server.addTransport(transport);
85
- await server.start();
86
-
87
- httpServer.listen(port, "0.0.0.0", () =>
88
- emit("listening", {
89
- port,
90
- boot_id: bootId(),
91
- node: process.version,
92
- package: "@procedures/client 0.7.0",
93
- tcpKeepalive: keepAliveMs,
94
- heartbeat,
95
- }),
96
- );