velocious 1.0.356 → 1.0.358
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 +9 -0
- package/build/src/application.d.ts.map +1 -1
- package/build/src/application.js +3 -1
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +3 -1
- package/build/src/background-jobs/worker.d.ts.map +1 -1
- package/build/src/background-jobs/worker.js +4 -1
- package/build/src/beacon/client.d.ts +113 -0
- package/build/src/beacon/client.d.ts.map +1 -0
- package/build/src/beacon/client.js +224 -0
- package/build/src/beacon/in-process-broker.d.ts +22 -0
- package/build/src/beacon/in-process-broker.d.ts.map +1 -0
- package/build/src/beacon/in-process-broker.js +61 -0
- package/build/src/beacon/in-process-client.d.ts +74 -0
- package/build/src/beacon/in-process-client.d.ts.map +1 -0
- package/build/src/beacon/in-process-client.js +99 -0
- package/build/src/beacon/server.d.ts +61 -0
- package/build/src/beacon/server.d.ts.map +1 -0
- package/build/src/beacon/server.js +115 -0
- package/build/src/beacon/types.d.ts +76 -0
- package/build/src/beacon/types.d.ts.map +1 -0
- package/build/src/beacon/types.js +42 -0
- package/build/src/cli/commands/beacon.d.ts +5 -0
- package/build/src/cli/commands/beacon.d.ts.map +1 -0
- package/build/src/cli/commands/beacon.js +7 -0
- package/build/src/configuration-types.d.ts +36 -1
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +10 -1
- package/build/src/configuration.d.ts +114 -1
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +247 -2
- package/build/src/controller.d.ts +8 -0
- package/build/src/controller.d.ts.map +1 -1
- package/build/src/controller.js +39 -17
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +8 -1
- package/build/src/environment-handlers/base.d.ts +32 -0
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +48 -1
- package/build/src/environment-handlers/node/cli/commands/beacon.d.ts +5 -0
- package/build/src/environment-handlers/node/cli/commands/beacon.d.ts.map +1 -0
- package/build/src/environment-handlers/node/cli/commands/beacon.js +18 -0
- package/build/src/environment-handlers/node.d.ts +1 -0
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +52 -2
- package/build/src/http-server/client/index.d.ts.map +1 -1
- package/build/src/http-server/client/index.js +2 -1
- package/build/src/http-server/client/request-runner.d.ts +7 -1
- package/build/src/http-server/client/request-runner.d.ts.map +1 -1
- package/build/src/http-server/client/request-runner.js +46 -1
- package/build/src/http-server/client/request-timing.d.ts +75 -0
- package/build/src/http-server/client/request-timing.d.ts.map +1 -0
- package/build/src/http-server/client/request-timing.js +128 -0
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +4 -1
- package/build/src/logger/console-logger.d.ts +2 -2
- package/build/src/logger/file-logger.d.ts +2 -2
- package/build/src/routes/resolver.d.ts +21 -1
- package/build/src/routes/resolver.d.ts.map +1 -1
- package/build/src/routes/resolver.js +38 -6
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import JsonSocket from "../background-jobs/json-socket.js";
|
|
5
|
+
import EventEmitter from "../utils/event-emitter.js";
|
|
6
|
+
const DEFAULT_RECONNECT_DELAY_MS = 1000;
|
|
7
|
+
const MAX_RECONNECT_DELAY_MS = 30_000;
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {function(import("./types.js").BeaconBroadcastMessage): void} BeaconBroadcastHandler
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* BeaconClient connects to a `velocious beacon` daemon and exchanges
|
|
13
|
+
* broadcasts with all peer processes connected to the same daemon.
|
|
14
|
+
*
|
|
15
|
+
* Lifecycle:
|
|
16
|
+
* const client = new BeaconClient({host, port, peerType: "server"})
|
|
17
|
+
* await client.connect() // resolves on first successful connect
|
|
18
|
+
* client.onBroadcast((message) => { ... }) // every fan-out
|
|
19
|
+
* client.publish({channel, broadcastParams, body})
|
|
20
|
+
* await client.close()
|
|
21
|
+
*
|
|
22
|
+
* Reconnect: on socket close (without an explicit `close()` call) the
|
|
23
|
+
* client schedules a reconnect with exponential backoff. While the
|
|
24
|
+
* underlying socket is down, `publish(...)` returns false and the
|
|
25
|
+
* caller can fall back to local-only delivery; subsequent reconnects do
|
|
26
|
+
* not replay missed publishes (Beacon is pubsub, not a queue).
|
|
27
|
+
*/
|
|
28
|
+
export default class BeaconClient extends EventEmitter {
|
|
29
|
+
/**
|
|
30
|
+
* @param {object} args - Options.
|
|
31
|
+
* @param {string} args.host - Beacon host.
|
|
32
|
+
* @param {number} args.port - Beacon port.
|
|
33
|
+
* @param {string} [args.peerType] - Optional human-readable peer label.
|
|
34
|
+
* @param {string} [args.peerId] - Optional explicit peer id (defaults to a random UUID).
|
|
35
|
+
* @param {number} [args.reconnectDelayMs] - Starting reconnect delay in ms.
|
|
36
|
+
* @param {number} [args.maxReconnectDelayMs] - Maximum reconnect delay in ms.
|
|
37
|
+
*/
|
|
38
|
+
constructor({ host, port, peerType, peerId, reconnectDelayMs, maxReconnectDelayMs }) {
|
|
39
|
+
super();
|
|
40
|
+
this.host = host;
|
|
41
|
+
this.port = port;
|
|
42
|
+
this.peerType = peerType;
|
|
43
|
+
this.peerId = peerId || randomUUID();
|
|
44
|
+
this._initialReconnectDelayMs = reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;
|
|
45
|
+
this._maxReconnectDelayMs = maxReconnectDelayMs ?? MAX_RECONNECT_DELAY_MS;
|
|
46
|
+
this._reconnectDelayMs = this._initialReconnectDelayMs;
|
|
47
|
+
/** @type {JsonSocket | undefined} */
|
|
48
|
+
this._jsonSocket = undefined;
|
|
49
|
+
/** @type {net.Socket | undefined} */
|
|
50
|
+
this._socket = undefined;
|
|
51
|
+
this._connected = false;
|
|
52
|
+
this._closed = false;
|
|
53
|
+
/** @type {NodeJS.Timeout | undefined} */
|
|
54
|
+
this._reconnectTimer = undefined;
|
|
55
|
+
/** @type {Promise<void> | undefined} */
|
|
56
|
+
this._connectPromise = undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Last socket error observed while connected, surfaced as the disconnect reason.
|
|
59
|
+
* @type {Error | undefined}
|
|
60
|
+
*/
|
|
61
|
+
this._lastSocketError = undefined;
|
|
62
|
+
}
|
|
63
|
+
/** @returns {string} - The peer id sent on the hello handshake. */
|
|
64
|
+
getPeerId() { return this.peerId; }
|
|
65
|
+
/** @returns {boolean} - Whether the underlying socket is currently connected. */
|
|
66
|
+
isConnected() { return this._connected; }
|
|
67
|
+
/**
|
|
68
|
+
* Resolves on the first successful connect. Subsequent calls return
|
|
69
|
+
* the same promise. Subsequent reconnects after a drop are silent —
|
|
70
|
+
* use `on("connect")` / `on("disconnect", reason)` to observe them.
|
|
71
|
+
* The `disconnect` listener receives an `Error` reason: either the
|
|
72
|
+
* underlying socket error, or `Error("Beacon broker disconnected")`
|
|
73
|
+
* when the close had no preceding error.
|
|
74
|
+
* @returns {Promise<void>}
|
|
75
|
+
*/
|
|
76
|
+
async connect() {
|
|
77
|
+
if (this._closed)
|
|
78
|
+
throw new Error("BeaconClient has been closed");
|
|
79
|
+
if (this._connectPromise)
|
|
80
|
+
return await this._connectPromise;
|
|
81
|
+
this._connectPromise = new Promise((resolve, reject) => {
|
|
82
|
+
const onConnect = () => {
|
|
83
|
+
this.off("connect", onConnect);
|
|
84
|
+
this.off("connect-error", onError);
|
|
85
|
+
resolve();
|
|
86
|
+
};
|
|
87
|
+
const onError = (/** @type {Error} */ error) => {
|
|
88
|
+
this.off("connect", onConnect);
|
|
89
|
+
this.off("connect-error", onError);
|
|
90
|
+
reject(error);
|
|
91
|
+
};
|
|
92
|
+
this.on("connect", onConnect);
|
|
93
|
+
this.on("connect-error", onError);
|
|
94
|
+
});
|
|
95
|
+
this._openSocket();
|
|
96
|
+
return await this._connectPromise;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Publishes a broadcast message to all peers (including this one,
|
|
100
|
+
* unless the daemon is restarted mid-publish).
|
|
101
|
+
* @param {object} args - Broadcast args.
|
|
102
|
+
* @param {string} args.channel - Channel name.
|
|
103
|
+
* @param {Record<string, any>} args.broadcastParams - Routing params.
|
|
104
|
+
* @param {any} args.body - Message body.
|
|
105
|
+
* @returns {boolean} - True if the publish was written to the socket. False if the client is currently disconnected.
|
|
106
|
+
*/
|
|
107
|
+
publish({ channel, broadcastParams, body }) {
|
|
108
|
+
if (!this._connected || !this._jsonSocket)
|
|
109
|
+
return false;
|
|
110
|
+
/** @type {import("./types.js").BeaconBroadcastMessage} */
|
|
111
|
+
const message = {
|
|
112
|
+
type: "broadcast",
|
|
113
|
+
channel,
|
|
114
|
+
broadcastParams,
|
|
115
|
+
body,
|
|
116
|
+
originPeerId: this.peerId
|
|
117
|
+
};
|
|
118
|
+
try {
|
|
119
|
+
this._jsonSocket.send(message);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Registers a handler called once for every `broadcast` message
|
|
128
|
+
* received from the daemon (including echoes of this client's own
|
|
129
|
+
* publishes — synapse-style fan-out).
|
|
130
|
+
* @param {BeaconBroadcastHandler} handler - Handler.
|
|
131
|
+
* @returns {() => void} - Unregister function.
|
|
132
|
+
*/
|
|
133
|
+
onBroadcast(handler) {
|
|
134
|
+
this.on("broadcast", handler);
|
|
135
|
+
return () => this.off("broadcast", handler);
|
|
136
|
+
}
|
|
137
|
+
/** @returns {Promise<void>} - Resolves once the socket is closed. */
|
|
138
|
+
async close() {
|
|
139
|
+
this._closed = true;
|
|
140
|
+
if (this._reconnectTimer) {
|
|
141
|
+
clearTimeout(this._reconnectTimer);
|
|
142
|
+
this._reconnectTimer = undefined;
|
|
143
|
+
}
|
|
144
|
+
if (this._socket) {
|
|
145
|
+
const socket = this._socket;
|
|
146
|
+
await new Promise((resolve) => {
|
|
147
|
+
socket.once("close", () => resolve(undefined));
|
|
148
|
+
socket.end();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* @returns {void}
|
|
154
|
+
*/
|
|
155
|
+
_openSocket() {
|
|
156
|
+
if (this._closed)
|
|
157
|
+
return;
|
|
158
|
+
const socket = net.createConnection({ host: this.host, port: this.port });
|
|
159
|
+
this._socket = socket;
|
|
160
|
+
const jsonSocket = new JsonSocket(socket);
|
|
161
|
+
this._jsonSocket = jsonSocket;
|
|
162
|
+
socket.on("connect", () => {
|
|
163
|
+
this._connected = true;
|
|
164
|
+
this._reconnectDelayMs = this._initialReconnectDelayMs;
|
|
165
|
+
jsonSocket.send({
|
|
166
|
+
type: "hello",
|
|
167
|
+
role: "client",
|
|
168
|
+
peerId: this.peerId,
|
|
169
|
+
peerType: this.peerType
|
|
170
|
+
});
|
|
171
|
+
this.emit("connect");
|
|
172
|
+
});
|
|
173
|
+
jsonSocket.on("message", (/** @type {import("./types.js").BeaconSocketMessage} */ message) => {
|
|
174
|
+
if (message?.type === "broadcast") {
|
|
175
|
+
this.emit("broadcast", message);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
jsonSocket.on("error", (error) => {
|
|
179
|
+
if (!this._connected) {
|
|
180
|
+
// Initial connect failed — surface to `connect()`'s promise via `connect-error`.
|
|
181
|
+
// No `error` emit: that channel is for established-session errors; the
|
|
182
|
+
// initial-connect path is owned by `connect-error`.
|
|
183
|
+
this.emit("connect-error", error);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
// Established session error. Cache so the upcoming `close` event can
|
|
187
|
+
// surface it as the disconnect reason.
|
|
188
|
+
this._lastSocketError = error;
|
|
189
|
+
this.emit("error", error);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
jsonSocket.on("close", () => {
|
|
193
|
+
const wasConnected = this._connected;
|
|
194
|
+
this._connected = false;
|
|
195
|
+
this._jsonSocket = undefined;
|
|
196
|
+
this._socket = undefined;
|
|
197
|
+
// Explicit close() sets `_closed = true` before ending the socket.
|
|
198
|
+
// Don't surface user-initiated teardown as a disconnect "error" —
|
|
199
|
+
// only network-driven drops should be reported.
|
|
200
|
+
if (wasConnected && !this._closed) {
|
|
201
|
+
const reason = this._lastSocketError || new Error("Beacon broker disconnected");
|
|
202
|
+
this.emit("disconnect", reason);
|
|
203
|
+
}
|
|
204
|
+
this._lastSocketError = undefined;
|
|
205
|
+
if (this._closed)
|
|
206
|
+
return;
|
|
207
|
+
this._scheduleReconnect();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* @returns {void}
|
|
212
|
+
*/
|
|
213
|
+
_scheduleReconnect() {
|
|
214
|
+
if (this._reconnectTimer)
|
|
215
|
+
return;
|
|
216
|
+
const delay = this._reconnectDelayMs;
|
|
217
|
+
this._reconnectTimer = setTimeout(() => {
|
|
218
|
+
this._reconnectTimer = undefined;
|
|
219
|
+
this._reconnectDelayMs = Math.min(delay * 2, this._maxReconnectDelayMs);
|
|
220
|
+
this._openSocket();
|
|
221
|
+
}, delay);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2JlYWNvbi9jbGllbnQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sRUFBQyxVQUFVLEVBQUMsTUFBTSxhQUFhLENBQUE7QUFDdEMsT0FBTyxHQUFHLE1BQU0sVUFBVSxDQUFBO0FBRTFCLE9BQU8sVUFBVSxNQUFNLG1DQUFtQyxDQUFBO0FBQzFELE9BQU8sWUFBWSxNQUFNLDJCQUEyQixDQUFBO0FBRXBELE1BQU0sMEJBQTBCLEdBQUcsSUFBSSxDQUFBO0FBQ3ZDLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFBO0FBRXJDOztHQUVHO0FBRUg7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLENBQUMsT0FBTyxPQUFPLFlBQWEsU0FBUSxZQUFZO0lBQ3BEOzs7Ozs7OztPQVFHO0lBQ0gsWUFBWSxFQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxnQkFBZ0IsRUFBRSxtQkFBbUIsRUFBQztRQUMvRSxLQUFLLEVBQUUsQ0FBQTtRQUNQLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFBO1FBQ2hCLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFBO1FBQ2hCLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFBO1FBQ3hCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxJQUFJLFVBQVUsRUFBRSxDQUFBO1FBQ3BDLElBQUksQ0FBQyx3QkFBd0IsR0FBRyxnQkFBZ0IsSUFBSSwwQkFBMEIsQ0FBQTtRQUM5RSxJQUFJLENBQUMsb0JBQW9CLEdBQUcsbUJBQW1CLElBQUksc0JBQXNCLENBQUE7UUFDekUsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQyx3QkFBd0IsQ0FBQTtRQUN0RCxxQ0FBcUM7UUFDckMsSUFBSSxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUE7UUFDNUIscUNBQXFDO1FB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LFNBQVMsQ0FBQTtZQUM1QixJQUFJLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQTtZQUV4QixtRUFBbUU7WUFDbkUsa0VBQWtFO1lBQ2xFLGdEQUFnRDtZQUNoRCxJQUFJLFlBQVksSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDbEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixJQUFJLElBQUksS0FBSyxDQUFDLDRCQUE0QixDQUFDLENBQUE7Z0JBRS9FLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLE1BQU0sQ0FBQyxDQUFBO1lBQ2pDLENBQUM7WUFFRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsU0FBUyxDQUFBO1lBRWpDLElBQUksSUFBSSxDQUFDLE9BQU87Z0JBQUUsT0FBTTtZQUV4QixJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQTtRQUMzQixDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILGtCQUFrQjtRQUNoQixJQUFJLElBQUksQ0FBQyxlQUFlO1lBQUUsT0FBTTtRQUVoQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUE7UUFFcEMsSUFBSSxDQUFDLGVBQWUsR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFO1lBQ3JDLElBQUksQ0FBQyxlQUFlLEdBQUcsU0FBUyxDQUFBO1lBQ2hDLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUE7WUFDdkUsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFBO1FBQ3BCLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQTtJQUNYLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQge3JhbmRvbVVVSUR9IGZyb20gXCJub2RlOmNyeXB0b1wiXG5pbXBvcnQgbmV0IGZyb20gXCJub2RlOm5ldFwiXG5cbmltcG9ydCBKc29uU29ja2V0IGZyb20gXCIuLi9iYWNrZ3JvdW5kLWpvYnMvanNvbi1zb2NrZXQuanNcIlxuaW1wb3J0IEV2ZW50RW1pdHRlciBmcm9tIFwiLi4vdXRpbHMvZXZlbnQtZW1pdHRlci5qc1wiXG5cbmNvbnN0IERFRkFVTFRfUkVDT05ORUNUX0RFTEFZX01TID0gMTAwMFxuY29uc3QgTUFYX1JFQ09OTkVDVF9ERUxBWV9NUyA9IDMwXzAwMFxuXG4vKipcbiAqIEB0eXBlZGVmIHtmdW5jdGlvbihpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJlYWNvbkJyb2FkY2FzdE1lc3NhZ2UpOiB2b2lkfSBCZWFjb25Ccm9hZGNhc3RIYW5kbGVyXG4gKi9cblxuLyoqXG4gKiBCZWFjb25DbGllbnQgY29ubmVjdHMgdG8gYSBgdmVsb2Npb3VzIGJlYWNvbmAgZGFlbW9uIGFuZCBleGNoYW5nZXNcbiAqIGJyb2FkY2FzdHMgd2l0aCBhbGwgcGVlciBwcm9jZXNzZXMgY29ubmVjdGVkIHRvIHRoZSBzYW1lIGRhZW1vbi5cbiAqXG4gKiBMaWZlY3ljbGU6XG4gKiAgIGNvbnN0IGNsaWVudCA9IG5ldyBCZWFjb25DbGllbnQoe2hvc3QsIHBvcnQsIHBlZXJUeXBlOiBcInNlcnZlclwifSlcbiAqICAgYXdhaXQgY2xpZW50LmNvbm5lY3QoKSAgICAgICAgICAgICAgICAgICAgICAgICAvLyByZXNvbHZlcyBvbiBmaXJzdCBzdWNjZXNzZnVsIGNvbm5lY3RcbiAqICAgY2xpZW50Lm9uQnJvYWRjYXN0KChtZXNzYWdlKSA9PiB7IC4uLiB9KSAgICAgICAvLyBldmVyeSBmYW4tb3V0XG4gKiAgIGNsaWVudC5wdWJsaXNoKHtjaGFubmVsLCBicm9hZGNhc3RQYXJhbXMsIGJvZHl9KVxuICogICBhd2FpdCBjbGllbnQuY2xvc2UoKVxuICpcbiAqIFJlY29ubmVjdDogb24gc29ja2V0IGNsb3NlICh3aXRob3V0IGFuIGV4cGxpY2l0IGBjbG9zZSgpYCBjYWxsKSB0aGVcbiAqIGNsaWVudCBzY2hlZHVsZXMgYSByZWNvbm5lY3Qgd2l0aCBleHBvbmVudGlhbCBiYWNrb2ZmLiBXaGlsZSB0aGVcbiAqIHVuZGVybHlpbmcgc29ja2V0IGlzIGRvd24sIGBwdWJsaXNoKC4uLilgIHJldHVybnMgZmFsc2UgYW5kIHRoZVxuICogY2FsbGVyIGNhbiBmYWxsIGJhY2sgdG8gbG9jYWwtb25seSBkZWxpdmVyeTsgc3Vic2VxdWVudCByZWNvbm5lY3RzIGRvXG4gKiBub3QgcmVwbGF5IG1pc3NlZCBwdWJsaXNoZXMgKEJlYWNvbiBpcyBwdWJzdWIsIG5vdCBhIHF1ZXVlKS5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQmVhY29uQ2xpZW50IGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuaG9zdCAtIEJlYWNvbiBob3N0LlxuICAgKiBAcGFyYW0ge251bWJlcn0gYXJncy5wb3J0IC0gQmVhY29uIHBvcnQuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbYXJncy5wZWVyVHlwZV0gLSBPcHRpb25hbCBodW1hbi1yZWFkYWJsZSBwZWVyIGxhYmVsLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3MucGVlcklkXSAtIE9wdGlvbmFsIGV4cGxpY2l0IHBlZXIgaWQgKGRlZmF1bHRzIHRvIGEgcmFuZG9tIFVVSUQpLlxuICAgKiBAcGFyYW0ge251bWJlcn0gW2FyZ3MucmVjb25uZWN0RGVsYXlNc10gLSBTdGFydGluZyByZWNvbm5lY3QgZGVsYXkgaW4gbXMuXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbYXJncy5tYXhSZWNvbm5lY3REZWxheU1zXSAtIE1heGltdW0gcmVjb25uZWN0IGRlbGF5IGluIG1zLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioe2hvc3QsIHBvcnQsIHBlZXJUeXBlLCBwZWVySWQsIHJlY29ubmVjdERlbGF5TXMsIG1heFJlY29ubmVjdERlbGF5TXN9KSB7XG4gICAgc3VwZXIoKVxuICAgIHRoaXMuaG9zdCA9IGhvc3RcbiAgICB0aGlzLnBvcnQgPSBwb3J0XG4gICAgdGhpcy5wZWVyVHlwZSA9IHBlZXJUeXBlXG4gICAgdGhpcy5wZWVySWQgPSBwZWVySWQgfHwgcmFuZG9tVVVJRCgpXG4gICAgdGhpcy5faW5pdGlhbFJlY29ubmVjdERlbGF5TXMgPSByZWNvbm5lY3REZWxheU1zID8/IERFRkFVTFRfUkVDT05ORUNUX0RFTEFZX01TXG4gICAgdGhpcy5fbWF4UmVjb25uZWN0RGVsYXlNcyA9IG1heFJlY29ubmVjdERlbGF5TXMgPz8gTUFYX1JFQ09OTkVDVF9ERUxBWV9NU1xuICAgIHRoaXMuX3JlY29ubmVjdERlbGF5TXMgPSB0aGlzLl9pbml0aWFsUmVjb25uZWN0RGVsYXlNc1xuICAgIC8qKiBAdHlwZSB7SnNvblNvY2tldCB8IHVuZGVmaW5lZH0gKi9cbiAgICB0aGlzLl9qc29uU29ja2V0ID0gdW5kZWZpbmVkXG4gICAgLyoqIEB0eXBlIHtuZXQuU29ja2V0IHwgdW5kZWZpbmVkfSAqL1xuICAgIHRoaXMuX3NvY2tldCA9IHVuZGVmaW5lZFxuICAgIHRoaXMuX2Nvbm5lY3RlZCA9IGZhbHNlXG4gICAgdGhpcy5fY2xvc2VkID0gZmFsc2VcbiAgICAvKiogQHR5cGUge05vZGVKUy5UaW1lb3V0IHwgdW5kZWZpbmVkfSAqL1xuICAgIHRoaXMuX3JlY29ubmVjdFRpbWVyID0gdW5kZWZpbmVkXG4gICAgLyoqIEB0eXBlIHtQcm9taXNlPHZvaWQ+IHwgdW5kZWZpbmVkfSAqL1xuICAgIHRoaXMuX2Nvbm5lY3RQcm9taXNlID0gdW5kZWZpbmVkXG4gICAgLyoqXG4gICAgICogTGFzdCBzb2NrZXQgZXJyb3Igb2JzZXJ2ZWQgd2hpbGUgY29ubmVjdGVkLCBzdXJmYWNlZCBhcyB0aGUgZGlzY29ubmVjdCByZWFzb24uXG4gICAgICogQHR5cGUge0Vycm9yIHwgdW5kZWZpbmVkfVxuICAgICAqL1xuICAgIHRoaXMuX2xhc3RTb2NrZXRFcnJvciA9IHVuZGVmaW5lZFxuICB9XG5cbiAgLyoqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIHBlZXIgaWQgc2VudCBvbiB0aGUgaGVsbG8gaGFuZHNoYWtlLiAqL1xuICBnZXRQZWVySWQoKSB7IHJldHVybiB0aGlzLnBlZXJJZCB9XG5cbiAgLyoqIEByZXR1cm5zIHtib29sZWFufSAtIFdoZXRoZXIgdGhlIHVuZGVybHlpbmcgc29ja2V0IGlzIGN1cnJlbnRseSBjb25uZWN0ZWQuICovXG4gIGlzQ29ubmVjdGVkKCkgeyByZXR1cm4gdGhpcy5fY29ubmVjdGVkIH1cblxuICAvKipcbiAgICogUmVzb2x2ZXMgb24gdGhlIGZpcnN0IHN1Y2Nlc3NmdWwgY29ubmVjdC4gU3Vic2VxdWVudCBjYWxscyByZXR1cm5cbiAgICogdGhlIHNhbWUgcHJvbWlzZS4gU3Vic2VxdWVudCByZWNvbm5lY3RzIGFmdGVyIGEgZHJvcCBhcmUgc2lsZW50IOKAlFxuICAgKiB1c2UgYG9uKFwiY29ubmVjdFwiKWAgLyBgb24oXCJkaXNjb25uZWN0XCIsIHJlYXNvbilgIHRvIG9ic2VydmUgdGhlbS5cbiAgICogVGhlIGBkaXNjb25uZWN0YCBsaXN0ZW5lciByZWNlaXZlcyBhbiBgRXJyb3JgIHJlYXNvbjogZWl0aGVyIHRoZVxuICAgKiB1bmRlcmx5aW5nIHNvY2tldCBlcnJvciwgb3IgYEVycm9yKFwiQmVhY29uIGJyb2tlciBkaXNjb25uZWN0ZWRcIilgXG4gICAqIHdoZW4gdGhlIGNsb3NlIGhhZCBubyBwcmVjZWRpbmcgZXJyb3IuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgYXN5bmMgY29ubmVjdCgpIHtcbiAgICBpZiAodGhpcy5fY2xvc2VkKSB0aHJvdyBuZXcgRXJyb3IoXCJCZWFjb25DbGllbnQgaGFzIGJlZW4gY2xvc2VkXCIpXG4gICAgaWYgKHRoaXMuX2Nvbm5lY3RQcm9taXNlKSByZXR1cm4gYXdhaXQgdGhpcy5fY29ubmVjdFByb21pc2VcblxuICAgIHRoaXMuX2Nvbm5lY3RQcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3Qgb25Db25uZWN0ID0gKCkgPT4ge1xuICAgICAgICB0aGlzLm9mZihcImNvbm5lY3RcIiwgb25Db25uZWN0KVxuICAgICAgICB0aGlzLm9mZihcImNvbm5lY3QtZXJyb3JcIiwgb25FcnJvcilcbiAgICAgICAgcmVzb2x2ZSgpXG4gICAgICB9XG4gICAgICBjb25zdCBvbkVycm9yID0gKC8qKiBAdHlwZSB7RXJyb3J9ICovIGVycm9yKSA9PiB7XG4gICAgICAgIHRoaXMub2ZmKFwiY29ubmVjdFwiLCBvbkNvbm5lY3QpXG4gICAgICAgIHRoaXMub2ZmKFwiY29ubmVjdC1lcnJvclwiLCBvbkVycm9yKVxuICAgICAgICByZWplY3QoZXJyb3IpXG4gICAgICB9XG5cbiAgICAgIHRoaXMub24oXCJjb25uZWN0XCIsIG9uQ29ubmVjdClcbiAgICAgIHRoaXMub24oXCJjb25uZWN0LWVycm9yXCIsIG9uRXJyb3IpXG4gICAgfSlcblxuICAgIHRoaXMuX29wZW5Tb2NrZXQoKVxuXG4gICAgcmV0dXJuIGF3YWl0IHRoaXMuX2Nvbm5lY3RQcm9taXNlXG4gIH1cblxuICAvKipcbiAgICogUHVibGlzaGVzIGEgYnJvYWRjYXN0IG1lc3NhZ2UgdG8gYWxsIHBlZXJzIChpbmNsdWRpbmcgdGhpcyBvbmUsXG4gICAqIHVubGVzcyB0aGUgZGFlbW9uIGlzIHJlc3RhcnRlZCBtaWQtcHVibGlzaCkuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQnJvYWRjYXN0IGFyZ3MuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmNoYW5uZWwgLSBDaGFubmVsIG5hbWUuXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgYW55Pn0gYXJncy5icm9hZGNhc3RQYXJhbXMgLSBSb3V0aW5nIHBhcmFtcy5cbiAgICogQHBhcmFtIHthbnl9IGFyZ3MuYm9keSAtIE1lc3NhZ2UgYm9keS5cbiAgICogQHJldHVybnMge2Jvb2xlYW59IC0gVHJ1ZSBpZiB0aGUgcHVibGlzaCB3YXMgd3JpdHRlbiB0byB0aGUgc29ja2V0LiBGYWxzZSBpZiB0aGUgY2xpZW50IGlzIGN1cnJlbnRseSBkaXNjb25uZWN0ZWQuXG4gICAqL1xuICBwdWJsaXNoKHtjaGFubmVsLCBicm9hZGNhc3RQYXJhbXMsIGJvZHl9KSB7XG4gICAgaWYgKCF0aGlzLl9jb25uZWN0ZWQgfHwgIXRoaXMuX2pzb25Tb2NrZXQpIHJldHVybiBmYWxzZVxuXG4gICAgLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJlYWNvbkJyb2FkY2FzdE1lc3NhZ2V9ICovXG4gICAgY29uc3QgbWVzc2FnZSA9IHtcbiAgICAgIHR5cGU6IFwiYnJvYWRjYXN0XCIsXG4gICAgICBjaGFubmVsLFxuICAgICAgYnJvYWRjYXN0UGFyYW1zLFxuICAgICAgYm9keSxcbiAgICAgIG9yaWdpblBlZXJJZDogdGhpcy5wZWVySWRcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgdGhpcy5fanNvblNvY2tldC5zZW5kKG1lc3NhZ2UpXG4gICAgICByZXR1cm4gdHJ1ZVxuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlZ2lzdGVycyBhIGhhbmRsZXIgY2FsbGVkIG9uY2UgZm9yIGV2ZXJ5IGBicm9hZGNhc3RgIG1lc3NhZ2VcbiAgICogcmVjZWl2ZWQgZnJvbSB0aGUgZGFlbW9uIChpbmNsdWRpbmcgZWNob2VzIG9mIHRoaXMgY2xpZW50J3Mgb3duXG4gICAqIHB1Ymxpc2hlcyDigJQgc3luYXBzZS1zdHlsZSBmYW4tb3V0KS5cbiAgICogQHBhcmFtIHtCZWFjb25Ccm9hZGNhc3RIYW5kbGVyfSBoYW5kbGVyIC0gSGFuZGxlci5cbiAgICogQHJldHVybnMgeygpID0+IHZvaWR9IC0gVW5yZWdpc3RlciBmdW5jdGlvbi5cbiAgICovXG4gIG9uQnJvYWRjYXN0KGhhbmRsZXIpIHtcbiAgICB0aGlzLm9uKFwiYnJvYWRjYXN0XCIsIGhhbmRsZXIpXG4gICAgcmV0dXJuICgpID0+IHRoaXMub2ZmKFwiYnJvYWRjYXN0XCIsIGhhbmRsZXIpXG4gIH1cblxuICAvKiogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgb25jZSB0aGUgc29ja2V0IGlzIGNsb3NlZC4gKi9cbiAgYXN5bmMgY2xvc2UoKSB7XG4gICAgdGhpcy5fY2xvc2VkID0gdHJ1ZVxuXG4gICAgaWYgKHRoaXMuX3JlY29ubmVjdFRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5fcmVjb25uZWN0VGltZXIpXG4gICAgICB0aGlzLl9yZWNvbm5lY3RUaW1lciA9IHVuZGVmaW5lZFxuICAgIH1cblxuICAgIGlmICh0aGlzLl9zb2NrZXQpIHtcbiAgICAgIGNvbnN0IHNvY2tldCA9IHRoaXMuX3NvY2tldFxuXG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICBzb2NrZXQub25jZShcImNsb3NlXCIsICgpID0+IHJlc29sdmUodW5kZWZpbmVkKSlcbiAgICAgICAgc29ja2V0LmVuZCgpXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9vcGVuU29ja2V0KCkge1xuICAgIGlmICh0aGlzLl9jbG9zZWQpIHJldHVyblxuXG4gICAgY29uc3Qgc29ja2V0ID0gbmV0LmNyZWF0ZUNvbm5lY3Rpb24oe2hvc3Q6IHRoaXMuaG9zdCwgcG9ydDogdGhpcy5wb3J0fSlcbiAgICB0aGlzLl9zb2NrZXQgPSBzb2NrZXRcbiAgICBjb25zdCBqc29uU29ja2V0ID0gbmV3IEpzb25Tb2NrZXQoc29ja2V0KVxuICAgIHRoaXMuX2pzb25Tb2NrZXQgPSBqc29uU29ja2V0XG5cbiAgICBzb2NrZXQub24oXCJjb25uZWN0XCIsICgpID0+IHtcbiAgICAgIHRoaXMuX2Nvbm5lY3RlZCA9IHRydWVcbiAgICAgIHRoaXMuX3JlY29ubmVjdERlbGF5TXMgPSB0aGlzLl9pbml0aWFsUmVjb25uZWN0RGVsYXlNc1xuXG4gICAgICBqc29uU29ja2V0LnNlbmQoe1xuICAgICAgICB0eXBlOiBcImhlbGxvXCIsXG4gICAgICAgIHJvbGU6IFwiY2xpZW50XCIsXG4gICAgICAgIHBlZXJJZDogdGhpcy5wZWVySWQsXG4gICAgICAgIHBlZXJUeXBlOiB0aGlzLnBlZXJUeXBlXG4gICAgICB9KVxuXG4gICAgICB0aGlzLmVtaXQoXCJjb25uZWN0XCIpXG4gICAgfSlcblxuICAgIGpzb25Tb2NrZXQub24oXCJtZXNzYWdlXCIsICgvKiogQHR5cGUge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmVhY29uU29ja2V0TWVzc2FnZX0gKi8gbWVzc2FnZSkgPT4ge1xuICAgICAgaWYgKG1lc3NhZ2U/LnR5cGUgPT09IFwiYnJvYWRjYXN0XCIpIHtcbiAgICAgICAgdGhpcy5lbWl0KFwiYnJvYWRjYXN0XCIsIG1lc3NhZ2UpXG4gICAgICB9XG4gICAgfSlcblxuICAgIGpzb25Tb2NrZXQub24oXCJlcnJvclwiLCAoZXJyb3IpID0+IHtcbiAgICAgIGlmICghdGhpcy5fY29ubmVjdGVkKSB7XG4gICAgICAgIC8vIEluaXRpYWwgY29ubmVjdCBmYWlsZWQg4oCUIHN1cmZhY2UgdG8gYGNvbm5lY3QoKWAncyBwcm9taXNlIHZpYSBgY29ubmVjdC1lcnJvcmAuXG4gICAgICAgIC8vIE5vIGBlcnJvcmAgZW1pdDogdGhhdCBjaGFubmVsIGlzIGZvciBlc3RhYmxpc2hlZC1zZXNzaW9uIGVycm9yczsgdGhlXG4gICAgICAgIC8vIGluaXRpYWwtY29ubmVjdCBwYXRoIGlzIG93bmVkIGJ5IGBjb25uZWN0LWVycm9yYC5cbiAgICAgICAgdGhpcy5lbWl0KFwiY29ubmVjdC1lcnJvclwiLCBlcnJvcilcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEVzdGFibGlzaGVkIHNlc3Npb24gZXJyb3IuIENhY2hlIHNvIHRoZSB1cGNvbWluZyBgY2xvc2VgIGV2ZW50IGNhblxuICAgICAgICAvLyBzdXJmYWNlIGl0IGFzIHRoZSBkaXNjb25uZWN0IHJlYXNvbi5cbiAgICAgICAgdGhpcy5fbGFzdFNvY2tldEVycm9yID0gZXJyb3JcbiAgICAgICAgdGhpcy5lbWl0KFwiZXJyb3JcIiwgZXJyb3IpXG4gICAgICB9XG4gICAgfSlcblxuICAgIGpzb25Tb2NrZXQub24oXCJjbG9zZVwiLCAoKSA9PiB7XG4gICAgICBjb25zdCB3YXNDb25uZWN0ZWQgPSB0aGlzLl9jb25uZWN0ZWRcblxuICAgICAgdGhpcy5fY29ubmVjdGVkID0gZmFsc2VcbiAgICAgIHRoaXMuX2pzb25Tb2NrZXQgPSB1bmRlZmluZWRcbiAgICAgIHRoaXMuX3NvY2tldCA9IHVuZGVmaW5lZFxuXG4gICAgICAvLyBFeHBsaWNpdCBjbG9zZSgpIHNldHMgYF9jbG9zZWQgPSB0cnVlYCBiZWZvcmUgZW5kaW5nIHRoZSBzb2NrZXQuXG4gICAgICAvLyBEb24ndCBzdXJmYWNlIHVzZXItaW5pdGlhdGVkIHRlYXJkb3duIGFzIGEgZGlzY29ubmVjdCBcImVycm9yXCIg4oCUXG4gICAgICAvLyBvbmx5IG5ldHdvcmstZHJpdmVuIGRyb3BzIHNob3VsZCBiZSByZXBvcnRlZC5cbiAgICAgIGlmICh3YXNDb25uZWN0ZWQgJiYgIXRoaXMuX2Nsb3NlZCkge1xuICAgICAgICBjb25zdCByZWFzb24gPSB0aGlzLl9sYXN0U29ja2V0RXJyb3IgfHwgbmV3IEVycm9yKFwiQmVhY29uIGJyb2tlciBkaXNjb25uZWN0ZWRcIilcblxuICAgICAgICB0aGlzLmVtaXQoXCJkaXNjb25uZWN0XCIsIHJlYXNvbilcbiAgICAgIH1cblxuICAgICAgdGhpcy5fbGFzdFNvY2tldEVycm9yID0gdW5kZWZpbmVkXG5cbiAgICAgIGlmICh0aGlzLl9jbG9zZWQpIHJldHVyblxuXG4gICAgICB0aGlzLl9zY2hlZHVsZVJlY29ubmVjdCgpXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9zY2hlZHVsZVJlY29ubmVjdCgpIHtcbiAgICBpZiAodGhpcy5fcmVjb25uZWN0VGltZXIpIHJldHVyblxuXG4gICAgY29uc3QgZGVsYXkgPSB0aGlzLl9yZWNvbm5lY3REZWxheU1zXG5cbiAgICB0aGlzLl9yZWNvbm5lY3RUaW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgdGhpcy5fcmVjb25uZWN0VGltZXIgPSB1bmRlZmluZWRcbiAgICAgIHRoaXMuX3JlY29ubmVjdERlbGF5TXMgPSBNYXRoLm1pbihkZWxheSAqIDIsIHRoaXMuX21heFJlY29ubmVjdERlbGF5TXMpXG4gICAgICB0aGlzLl9vcGVuU29ja2V0KClcbiAgICB9LCBkZWxheSlcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers a peer with the broker. Returns an unregister function.
|
|
3
|
+
* @param {InProcessPeer} peer - Peer instance.
|
|
4
|
+
* @returns {() => void} - Unregister function.
|
|
5
|
+
*/
|
|
6
|
+
export function registerInProcessPeer(peer: InProcessPeer): () => void;
|
|
7
|
+
/**
|
|
8
|
+
* Schedules a fan-out of the given message to every registered peer.
|
|
9
|
+
* Each delivery is its own microtask so handlers run in the order peers
|
|
10
|
+
* registered, but never synchronously inside the publish call.
|
|
11
|
+
* @param {import("./types.js").BeaconBroadcastMessage} message - Broadcast message.
|
|
12
|
+
* @returns {void}
|
|
13
|
+
*/
|
|
14
|
+
export function publishToInProcessPeers(message: import("./types.js").BeaconBroadcastMessage): void;
|
|
15
|
+
/**
|
|
16
|
+
* @returns {number} - Current peer count. Exposed for diagnostics and tests.
|
|
17
|
+
*/
|
|
18
|
+
export function getInProcessPeerCount(): number;
|
|
19
|
+
export type InProcessPeer = {
|
|
20
|
+
_receiveBroadcast: (message: import("./types.js").BeaconBroadcastMessage) => void;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=in-process-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-process-broker.d.ts","sourceRoot":"","sources":["../../../src/beacon/in-process-broker.js"],"names":[],"mappings":"AAyBA;;;;GAIG;AACH,4CAHW,aAAa,GACX,MAAM,IAAI,CAQtB;AAED;;;;;;GAMG;AACH,iDAHW,OAAO,YAAY,EAAE,sBAAsB,GACzC,IAAI,CAehB;AAED;;GAEG;AACH,yCAFa,MAAM,CAIlB;4BA7Ca;IAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,OAAO,YAAY,EAAE,sBAAsB,KAAK,IAAI,CAAA;CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* Module-level broker singleton for in-process Beacon mode.
|
|
4
|
+
*
|
|
5
|
+
* Every `InProcessBeaconClient` registers itself here on `connect()` and
|
|
6
|
+
* unregisters on `close()`. `publish(message)` schedules a microtask
|
|
7
|
+
* fan-out to every registered peer's `_receiveBroadcast(message)`. The
|
|
8
|
+
* microtask boundary keeps "publish, then receive" ordering safe even
|
|
9
|
+
* when the publisher and a subscriber are the same client — without it,
|
|
10
|
+
* a synchronous fan-out could re-enter the caller mid-publish if a
|
|
11
|
+
* broadcast handler synchronously published again.
|
|
12
|
+
*
|
|
13
|
+
* Designed for two scenarios:
|
|
14
|
+
* 1. Tests with multiple `Configuration` instances in one process
|
|
15
|
+
* (no TCP socket setup, deterministic ordering via microtasks).
|
|
16
|
+
* 2. Single-process production deployments that want the same
|
|
17
|
+
* `broadcastToChannel` ergonomics without running the daemon.
|
|
18
|
+
*/
|
|
19
|
+
/** @typedef {{_receiveBroadcast: (message: import("./types.js").BeaconBroadcastMessage) => void}} InProcessPeer */
|
|
20
|
+
/** @type {Set<InProcessPeer>} */
|
|
21
|
+
const peers = new Set();
|
|
22
|
+
/**
|
|
23
|
+
* Registers a peer with the broker. Returns an unregister function.
|
|
24
|
+
* @param {InProcessPeer} peer - Peer instance.
|
|
25
|
+
* @returns {() => void} - Unregister function.
|
|
26
|
+
*/
|
|
27
|
+
export function registerInProcessPeer(peer) {
|
|
28
|
+
peers.add(peer);
|
|
29
|
+
return () => {
|
|
30
|
+
peers.delete(peer);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Schedules a fan-out of the given message to every registered peer.
|
|
35
|
+
* Each delivery is its own microtask so handlers run in the order peers
|
|
36
|
+
* registered, but never synchronously inside the publish call.
|
|
37
|
+
* @param {import("./types.js").BeaconBroadcastMessage} message - Broadcast message.
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
export function publishToInProcessPeers(message) {
|
|
41
|
+
for (const peer of peers) {
|
|
42
|
+
queueMicrotask(() => {
|
|
43
|
+
try {
|
|
44
|
+
peer._receiveBroadcast(message);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// Mirrors the daemon's per-peer fan-out resilience: a thrown
|
|
48
|
+
// handler on one peer must not prevent delivery to others. The
|
|
49
|
+
// caller's framework-error path covers higher-level reporting.
|
|
50
|
+
console.error("In-process Beacon peer threw during broadcast delivery:", error);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* @returns {number} - Current peer count. Exposed for diagnostics and tests.
|
|
57
|
+
*/
|
|
58
|
+
export function getInProcessPeerCount() {
|
|
59
|
+
return peers.size;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW4tcHJvY2Vzcy1icm9rZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYmVhY29uL2luLXByb2Nlc3MtYnJva2VyLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWjs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUVILG1IQUFtSDtBQUVuSCxpQ0FBaUM7QUFDakMsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQTtBQUV2Qjs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLHFCQUFxQixDQUFDLElBQUk7SUFDeEMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUVmLE9BQU8sR0FBRyxFQUFFO1FBQ1YsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUNwQixDQUFDLENBQUE7QUFDSCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLHVCQUF1QixDQUFDLE9BQU87SUFDN0MsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUN6QixjQUFjLENBQUMsR0FBRyxFQUFFO1lBQ2xCLElBQUksQ0FBQztnQkFDSCxJQUFJLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLENBQUE7WUFDakMsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsNkRBQTZEO2dCQUM3RCwrREFBK0Q7Z0JBQy9ELCtEQUErRDtnQkFDL0QsT0FBTyxDQUFDLEtBQUssQ0FBQyx5REFBeUQsRUFBRSxLQUFLLENBQUMsQ0FBQTtZQUNqRixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDO0FBQ0gsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLHFCQUFxQjtJQUNuQyxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUE7QUFDbkIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG4vKipcbiAqIE1vZHVsZS1sZXZlbCBicm9rZXIgc2luZ2xldG9uIGZvciBpbi1wcm9jZXNzIEJlYWNvbiBtb2RlLlxuICpcbiAqIEV2ZXJ5IGBJblByb2Nlc3NCZWFjb25DbGllbnRgIHJlZ2lzdGVycyBpdHNlbGYgaGVyZSBvbiBgY29ubmVjdCgpYCBhbmRcbiAqIHVucmVnaXN0ZXJzIG9uIGBjbG9zZSgpYC4gYHB1Ymxpc2gobWVzc2FnZSlgIHNjaGVkdWxlcyBhIG1pY3JvdGFza1xuICogZmFuLW91dCB0byBldmVyeSByZWdpc3RlcmVkIHBlZXIncyBgX3JlY2VpdmVCcm9hZGNhc3QobWVzc2FnZSlgLiBUaGVcbiAqIG1pY3JvdGFzayBib3VuZGFyeSBrZWVwcyBcInB1Ymxpc2gsIHRoZW4gcmVjZWl2ZVwiIG9yZGVyaW5nIHNhZmUgZXZlblxuICogd2hlbiB0aGUgcHVibGlzaGVyIGFuZCBhIHN1YnNjcmliZXIgYXJlIHRoZSBzYW1lIGNsaWVudCDigJQgd2l0aG91dCBpdCxcbiAqIGEgc3luY2hyb25vdXMgZmFuLW91dCBjb3VsZCByZS1lbnRlciB0aGUgY2FsbGVyIG1pZC1wdWJsaXNoIGlmIGFcbiAqIGJyb2FkY2FzdCBoYW5kbGVyIHN5bmNocm9ub3VzbHkgcHVibGlzaGVkIGFnYWluLlxuICpcbiAqIERlc2lnbmVkIGZvciB0d28gc2NlbmFyaW9zOlxuICogICAxLiBUZXN0cyB3aXRoIG11bHRpcGxlIGBDb25maWd1cmF0aW9uYCBpbnN0YW5jZXMgaW4gb25lIHByb2Nlc3NcbiAqICAgICAgKG5vIFRDUCBzb2NrZXQgc2V0dXAsIGRldGVybWluaXN0aWMgb3JkZXJpbmcgdmlhIG1pY3JvdGFza3MpLlxuICogICAyLiBTaW5nbGUtcHJvY2VzcyBwcm9kdWN0aW9uIGRlcGxveW1lbnRzIHRoYXQgd2FudCB0aGUgc2FtZVxuICogICAgICBgYnJvYWRjYXN0VG9DaGFubmVsYCBlcmdvbm9taWNzIHdpdGhvdXQgcnVubmluZyB0aGUgZGFlbW9uLlxuICovXG5cbi8qKiBAdHlwZWRlZiB7e19yZWNlaXZlQnJvYWRjYXN0OiAobWVzc2FnZTogaW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CZWFjb25Ccm9hZGNhc3RNZXNzYWdlKSA9PiB2b2lkfX0gSW5Qcm9jZXNzUGVlciAqL1xuXG4vKiogQHR5cGUge1NldDxJblByb2Nlc3NQZWVyPn0gKi9cbmNvbnN0IHBlZXJzID0gbmV3IFNldCgpXG5cbi8qKlxuICogUmVnaXN0ZXJzIGEgcGVlciB3aXRoIHRoZSBicm9rZXIuIFJldHVybnMgYW4gdW5yZWdpc3RlciBmdW5jdGlvbi5cbiAqIEBwYXJhbSB7SW5Qcm9jZXNzUGVlcn0gcGVlciAtIFBlZXIgaW5zdGFuY2UuXG4gKiBAcmV0dXJucyB7KCkgPT4gdm9pZH0gLSBVbnJlZ2lzdGVyIGZ1bmN0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJJblByb2Nlc3NQZWVyKHBlZXIpIHtcbiAgcGVlcnMuYWRkKHBlZXIpXG5cbiAgcmV0dXJuICgpID0+IHtcbiAgICBwZWVycy5kZWxldGUocGVlcilcbiAgfVxufVxuXG4vKipcbiAqIFNjaGVkdWxlcyBhIGZhbi1vdXQgb2YgdGhlIGdpdmVuIG1lc3NhZ2UgdG8gZXZlcnkgcmVnaXN0ZXJlZCBwZWVyLlxuICogRWFjaCBkZWxpdmVyeSBpcyBpdHMgb3duIG1pY3JvdGFzayBzbyBoYW5kbGVycyBydW4gaW4gdGhlIG9yZGVyIHBlZXJzXG4gKiByZWdpc3RlcmVkLCBidXQgbmV2ZXIgc3luY2hyb25vdXNseSBpbnNpZGUgdGhlIHB1Ymxpc2ggY2FsbC5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CZWFjb25Ccm9hZGNhc3RNZXNzYWdlfSBtZXNzYWdlIC0gQnJvYWRjYXN0IG1lc3NhZ2UuXG4gKiBAcmV0dXJucyB7dm9pZH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHB1Ymxpc2hUb0luUHJvY2Vzc1BlZXJzKG1lc3NhZ2UpIHtcbiAgZm9yIChjb25zdCBwZWVyIG9mIHBlZXJzKSB7XG4gICAgcXVldWVNaWNyb3Rhc2soKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcGVlci5fcmVjZWl2ZUJyb2FkY2FzdChtZXNzYWdlKVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgLy8gTWlycm9ycyB0aGUgZGFlbW9uJ3MgcGVyLXBlZXIgZmFuLW91dCByZXNpbGllbmNlOiBhIHRocm93blxuICAgICAgICAvLyBoYW5kbGVyIG9uIG9uZSBwZWVyIG11c3Qgbm90IHByZXZlbnQgZGVsaXZlcnkgdG8gb3RoZXJzLiBUaGVcbiAgICAgICAgLy8gY2FsbGVyJ3MgZnJhbWV3b3JrLWVycm9yIHBhdGggY292ZXJzIGhpZ2hlci1sZXZlbCByZXBvcnRpbmcuXG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXCJJbi1wcm9jZXNzIEJlYWNvbiBwZWVyIHRocmV3IGR1cmluZyBicm9hZGNhc3QgZGVsaXZlcnk6XCIsIGVycm9yKVxuICAgICAgfVxuICAgIH0pXG4gIH1cbn1cblxuLyoqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIEN1cnJlbnQgcGVlciBjb3VudC4gRXhwb3NlZCBmb3IgZGlhZ25vc3RpY3MgYW5kIHRlc3RzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0SW5Qcm9jZXNzUGVlckNvdW50KCkge1xuICByZXR1cm4gcGVlcnMuc2l6ZVxufVxuIl19
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
declare const InProcessBeaconClient_base: typeof import("eventemitter3").EventEmitter;
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {function(import("./types.js").BeaconBroadcastMessage): void} BeaconBroadcastHandler
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* In-process counterpart to `BeaconClient`. Registers with the
|
|
7
|
+
* module-level `in-process-broker` singleton so peers in the same
|
|
8
|
+
* process exchange broadcasts without ever touching TCP. Implements the
|
|
9
|
+
* same external surface as `BeaconClient` (`connect`, `publish`,
|
|
10
|
+
* `onBroadcast`, `isConnected`, `getPeerId`, `close`) so
|
|
11
|
+
* `Configuration` can use either client interchangeably.
|
|
12
|
+
*
|
|
13
|
+
* Lifecycle differs from `BeaconClient` in two intentional ways:
|
|
14
|
+
* - `connect()` resolves immediately; there is no broker to wait for.
|
|
15
|
+
* - There is no reconnect loop and no `connect-error` / `disconnect`
|
|
16
|
+
* event surface, because nothing can fail.
|
|
17
|
+
*/
|
|
18
|
+
export default class InProcessBeaconClient extends InProcessBeaconClient_base {
|
|
19
|
+
/**
|
|
20
|
+
* @param {object} [args] - Options.
|
|
21
|
+
* @param {string} [args.peerType] - Optional human-readable peer label.
|
|
22
|
+
* @param {string} [args.peerId] - Optional explicit peer id (defaults to a random UUID).
|
|
23
|
+
*/
|
|
24
|
+
constructor({ peerType, peerId }?: {
|
|
25
|
+
peerType?: string | undefined;
|
|
26
|
+
peerId?: string | undefined;
|
|
27
|
+
});
|
|
28
|
+
peerType: string | undefined;
|
|
29
|
+
peerId: string;
|
|
30
|
+
_connected: boolean;
|
|
31
|
+
/** @type {(() => void) | undefined} */
|
|
32
|
+
_unregister: (() => void) | undefined;
|
|
33
|
+
/** @returns {string} - Peer id. */
|
|
34
|
+
getPeerId(): string;
|
|
35
|
+
/** @returns {boolean} - Whether the peer is registered with the broker. */
|
|
36
|
+
isConnected(): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Registers with the in-process broker. Idempotent.
|
|
39
|
+
* @returns {Promise<void>}
|
|
40
|
+
*/
|
|
41
|
+
connect(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Publishes a broadcast to every registered peer (including this
|
|
44
|
+
* one). Returns false if `connect()` hasn't been called yet, matching
|
|
45
|
+
* `BeaconClient` semantics.
|
|
46
|
+
* @param {object} args - Broadcast args.
|
|
47
|
+
* @param {string} args.channel - Channel name.
|
|
48
|
+
* @param {Record<string, any>} args.broadcastParams - Routing params.
|
|
49
|
+
* @param {any} args.body - Message body.
|
|
50
|
+
* @returns {boolean} - True when the broadcast was queued.
|
|
51
|
+
*/
|
|
52
|
+
publish({ channel, broadcastParams, body }: {
|
|
53
|
+
channel: string;
|
|
54
|
+
broadcastParams: Record<string, any>;
|
|
55
|
+
body: any;
|
|
56
|
+
}): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Registers a handler called once for every broadcast received.
|
|
59
|
+
* @param {BeaconBroadcastHandler} handler - Handler.
|
|
60
|
+
* @returns {() => void} - Unregister function.
|
|
61
|
+
*/
|
|
62
|
+
onBroadcast(handler: BeaconBroadcastHandler): () => void;
|
|
63
|
+
/**
|
|
64
|
+
* Receives a broadcast from the broker. Called by `in-process-broker`.
|
|
65
|
+
* @param {import("./types.js").BeaconBroadcastMessage} message - Broadcast message.
|
|
66
|
+
* @returns {void}
|
|
67
|
+
*/
|
|
68
|
+
_receiveBroadcast(message: import("./types.js").BeaconBroadcastMessage): void;
|
|
69
|
+
/** @returns {Promise<void>} - Unregisters from the broker. */
|
|
70
|
+
close(): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
export type BeaconBroadcastHandler = (arg0: import("./types.js").BeaconBroadcastMessage) => void;
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=in-process-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-process-client.d.ts","sourceRoot":"","sources":["../../../src/beacon/in-process-client.js"],"names":[],"mappings":";AAOA;;GAEG;AAEH;;;;;;;;;;;;GAYG;AACH;IACE;;;;OAIG;IACH,mCAHG;QAAsB,QAAQ;QACR,MAAM;KAC9B,EAQA;IALC,6BAAwB;IACxB,eAAoC;IACpC,oBAAuB;IACvB,uCAAuC;IACvC,aADW,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CACP;IAG9B,mCAAmC;IACnC,aADc,MAAM,CACc;IAElC,2EAA2E;IAC3E,eADc,OAAO,CACmB;IAExC;;;OAGG;IACH,WAFa,OAAO,CAAC,IAAI,CAAC,CAQzB;IAED;;;;;;;;;OASG;IACH,4CALG;QAAqB,OAAO,EAApB,MAAM;QACoB,eAAe,EAAzC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;QACT,IAAI,EAAd,GAAG;KACX,GAAU,OAAO,CAiBnB;IAED;;;;OAIG;IACH,qBAHW,sBAAsB,GACpB,MAAM,IAAI,CAKtB;IAED;;;;OAIG;IACH,2BAHW,OAAO,YAAY,EAAE,sBAAsB,GACzC,IAAI,CAIhB;IAED,8DAA8D;IAC9D,SADc,OAAO,CAAC,IAAI,CAAC,CAK1B;CACF;qCArGY,CAAS,IAA2C,EAA3C,OAAO,YAAY,EAAE,sBAAsB,KAAG,IAAI"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import EventEmitter from "../utils/event-emitter.js";
|
|
4
|
+
import { publishToInProcessPeers, registerInProcessPeer } from "./in-process-broker.js";
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {function(import("./types.js").BeaconBroadcastMessage): void} BeaconBroadcastHandler
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* In-process counterpart to `BeaconClient`. Registers with the
|
|
10
|
+
* module-level `in-process-broker` singleton so peers in the same
|
|
11
|
+
* process exchange broadcasts without ever touching TCP. Implements the
|
|
12
|
+
* same external surface as `BeaconClient` (`connect`, `publish`,
|
|
13
|
+
* `onBroadcast`, `isConnected`, `getPeerId`, `close`) so
|
|
14
|
+
* `Configuration` can use either client interchangeably.
|
|
15
|
+
*
|
|
16
|
+
* Lifecycle differs from `BeaconClient` in two intentional ways:
|
|
17
|
+
* - `connect()` resolves immediately; there is no broker to wait for.
|
|
18
|
+
* - There is no reconnect loop and no `connect-error` / `disconnect`
|
|
19
|
+
* event surface, because nothing can fail.
|
|
20
|
+
*/
|
|
21
|
+
export default class InProcessBeaconClient extends EventEmitter {
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} [args] - Options.
|
|
24
|
+
* @param {string} [args.peerType] - Optional human-readable peer label.
|
|
25
|
+
* @param {string} [args.peerId] - Optional explicit peer id (defaults to a random UUID).
|
|
26
|
+
*/
|
|
27
|
+
constructor({ peerType, peerId } = {}) {
|
|
28
|
+
super();
|
|
29
|
+
this.peerType = peerType;
|
|
30
|
+
this.peerId = peerId || randomUUID();
|
|
31
|
+
this._connected = false;
|
|
32
|
+
/** @type {(() => void) | undefined} */
|
|
33
|
+
this._unregister = undefined;
|
|
34
|
+
}
|
|
35
|
+
/** @returns {string} - Peer id. */
|
|
36
|
+
getPeerId() { return this.peerId; }
|
|
37
|
+
/** @returns {boolean} - Whether the peer is registered with the broker. */
|
|
38
|
+
isConnected() { return this._connected; }
|
|
39
|
+
/**
|
|
40
|
+
* Registers with the in-process broker. Idempotent.
|
|
41
|
+
* @returns {Promise<void>}
|
|
42
|
+
*/
|
|
43
|
+
async connect() {
|
|
44
|
+
if (this._connected)
|
|
45
|
+
return;
|
|
46
|
+
this._unregister = registerInProcessPeer(this);
|
|
47
|
+
this._connected = true;
|
|
48
|
+
this.emit("connect");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Publishes a broadcast to every registered peer (including this
|
|
52
|
+
* one). Returns false if `connect()` hasn't been called yet, matching
|
|
53
|
+
* `BeaconClient` semantics.
|
|
54
|
+
* @param {object} args - Broadcast args.
|
|
55
|
+
* @param {string} args.channel - Channel name.
|
|
56
|
+
* @param {Record<string, any>} args.broadcastParams - Routing params.
|
|
57
|
+
* @param {any} args.body - Message body.
|
|
58
|
+
* @returns {boolean} - True when the broadcast was queued.
|
|
59
|
+
*/
|
|
60
|
+
publish({ channel, broadcastParams, body }) {
|
|
61
|
+
if (!this._connected)
|
|
62
|
+
return false;
|
|
63
|
+
/** @type {import("./types.js").BeaconBroadcastMessage} */
|
|
64
|
+
const message = {
|
|
65
|
+
type: "broadcast",
|
|
66
|
+
channel,
|
|
67
|
+
broadcastParams,
|
|
68
|
+
body,
|
|
69
|
+
originPeerId: this.peerId
|
|
70
|
+
};
|
|
71
|
+
publishToInProcessPeers(message);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Registers a handler called once for every broadcast received.
|
|
76
|
+
* @param {BeaconBroadcastHandler} handler - Handler.
|
|
77
|
+
* @returns {() => void} - Unregister function.
|
|
78
|
+
*/
|
|
79
|
+
onBroadcast(handler) {
|
|
80
|
+
this.on("broadcast", handler);
|
|
81
|
+
return () => this.off("broadcast", handler);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Receives a broadcast from the broker. Called by `in-process-broker`.
|
|
85
|
+
* @param {import("./types.js").BeaconBroadcastMessage} message - Broadcast message.
|
|
86
|
+
* @returns {void}
|
|
87
|
+
*/
|
|
88
|
+
_receiveBroadcast(message) {
|
|
89
|
+
this.emit("broadcast", message);
|
|
90
|
+
}
|
|
91
|
+
/** @returns {Promise<void>} - Unregisters from the broker. */
|
|
92
|
+
async close() {
|
|
93
|
+
if (this._unregister)
|
|
94
|
+
this._unregister();
|
|
95
|
+
this._unregister = undefined;
|
|
96
|
+
this._connected = false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW4tcHJvY2Vzcy1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYmVhY29uL2luLXByb2Nlc3MtY2xpZW50LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sYUFBYSxDQUFBO0FBRXRDLE9BQU8sWUFBWSxNQUFNLDJCQUEyQixDQUFBO0FBQ3BELE9BQU8sRUFBQyx1QkFBdUIsRUFBRSxxQkFBcUIsRUFBQyxNQUFNLHdCQUF3QixDQUFBO0FBRXJGOztHQUVHO0FBRUg7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxxQkFBc0IsU0FBUSxZQUFZO0lBQzdEOzs7O09BSUc7SUFDSCxZQUFZLEVBQUMsUUFBUSxFQUFFLE1BQU0sRUFBQyxHQUFHLEVBQUU7UUFDakMsS0FBSyxFQUFFLENBQUE7UUFDUCxJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQTtRQUN4QixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sSUFBSSxVQUFVLEVBQUUsQ0FBQTtRQUNwQyxJQUFJLENBQUMsVUFBVSxHQUFHLEtBQUssQ0FBQTtRQUN2Qix1Q0FBdUM7UUFDdkMsSUFBSSxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUE7SUFDOUIsQ0FBQztJQUVELG1DQUFtQztJQUNuQyxTQUFTLEtBQUssT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFBLENBQUMsQ0FBQztJQUVsQywyRUFBMkU7SUFDM0UsV0FBVyxLQUFLLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQSxDQUFDLENBQUM7SUFFeEM7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLE9BQU87UUFDWCxJQUFJLElBQUksQ0FBQyxVQUFVO1lBQUUsT0FBTTtRQUUzQixJQUFJLENBQUMsV0FBVyxHQUFHLHFCQUFxQixDQUFDLElBQUksQ0FBQyxDQUFBO1FBQzlDLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFBO1FBQ3RCLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUE7SUFDdEIsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNILE9BQU8sQ0FBQyxFQUFDLE9BQU8sRUFBRSxlQUFlLEVBQUUsSUFBSSxFQUFDO1FBQ3RDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVTtZQUFFLE9BQU8sS0FBSyxDQUFBO1FBRWxDLDBEQUEwRDtRQUMxRCxNQUFNLE9BQU8sR0FBRztZQUNkLElBQUksRUFBRSxXQUFXO1lBQ2pCLE9BQU87WUFDUCxlQUFlO1lBQ2YsSUFBSTtZQUNKLFlBQVksRUFBRSxJQUFJLENBQUMsTUFBTTtTQUMxQixDQUFBO1FBRUQsdUJBQXVCLENBQUMsT0FBTyxDQUFDLENBQUE7UUFFaEMsT0FBTyxJQUFJLENBQUE7SUFDYixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILFdBQVcsQ0FBQyxPQUFPO1FBQ2pCLElBQUksQ0FBQyxFQUFFLENBQUMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFBO1FBQzdCLE9BQU8sR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUE7SUFDN0MsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxpQkFBaUIsQ0FBQyxPQUFPO1FBQ3ZCLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFBO0lBQ2pDLENBQUM7SUFFRCw4REFBOEQ7SUFDOUQsS0FBSyxDQUFDLEtBQUs7UUFDVCxJQUFJLElBQUksQ0FBQyxXQUFXO1lBQUUsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFBO1FBQ3hDLElBQUksQ0FBQyxXQUFXLEdBQUcsU0FBUyxDQUFBO1FBQzVCLElBQUksQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFBO0lBQ3pCLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQge3JhbmRvbVVVSUR9IGZyb20gXCJub2RlOmNyeXB0b1wiXG5cbmltcG9ydCBFdmVudEVtaXR0ZXIgZnJvbSBcIi4uL3V0aWxzL2V2ZW50LWVtaXR0ZXIuanNcIlxuaW1wb3J0IHtwdWJsaXNoVG9JblByb2Nlc3NQZWVycywgcmVnaXN0ZXJJblByb2Nlc3NQZWVyfSBmcm9tIFwiLi9pbi1wcm9jZXNzLWJyb2tlci5qc1wiXG5cbi8qKlxuICogQHR5cGVkZWYge2Z1bmN0aW9uKGltcG9ydChcIi4vdHlwZXMuanNcIikuQmVhY29uQnJvYWRjYXN0TWVzc2FnZSk6IHZvaWR9IEJlYWNvbkJyb2FkY2FzdEhhbmRsZXJcbiAqL1xuXG4vKipcbiAqIEluLXByb2Nlc3MgY291bnRlcnBhcnQgdG8gYEJlYWNvbkNsaWVudGAuIFJlZ2lzdGVycyB3aXRoIHRoZVxuICogbW9kdWxlLWxldmVsIGBpbi1wcm9jZXNzLWJyb2tlcmAgc2luZ2xldG9uIHNvIHBlZXJzIGluIHRoZSBzYW1lXG4gKiBwcm9jZXNzIGV4Y2hhbmdlIGJyb2FkY2FzdHMgd2l0aG91dCBldmVyIHRvdWNoaW5nIFRDUC4gSW1wbGVtZW50cyB0aGVcbiAqIHNhbWUgZXh0ZXJuYWwgc3VyZmFjZSBhcyBgQmVhY29uQ2xpZW50YCAoYGNvbm5lY3RgLCBgcHVibGlzaGAsXG4gKiBgb25Ccm9hZGNhc3RgLCBgaXNDb25uZWN0ZWRgLCBgZ2V0UGVlcklkYCwgYGNsb3NlYCkgc29cbiAqIGBDb25maWd1cmF0aW9uYCBjYW4gdXNlIGVpdGhlciBjbGllbnQgaW50ZXJjaGFuZ2VhYmx5LlxuICpcbiAqIExpZmVjeWNsZSBkaWZmZXJzIGZyb20gYEJlYWNvbkNsaWVudGAgaW4gdHdvIGludGVudGlvbmFsIHdheXM6XG4gKiAgIC0gYGNvbm5lY3QoKWAgcmVzb2x2ZXMgaW1tZWRpYXRlbHk7IHRoZXJlIGlzIG5vIGJyb2tlciB0byB3YWl0IGZvci5cbiAqICAgLSBUaGVyZSBpcyBubyByZWNvbm5lY3QgbG9vcCBhbmQgbm8gYGNvbm5lY3QtZXJyb3JgIC8gYGRpc2Nvbm5lY3RgXG4gKiAgICAgZXZlbnQgc3VyZmFjZSwgYmVjYXVzZSBub3RoaW5nIGNhbiBmYWlsLlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBJblByb2Nlc3NCZWFjb25DbGllbnQgZXh0ZW5kcyBFdmVudEVtaXR0ZXIge1xuICAvKipcbiAgICogQHBhcmFtIHtvYmplY3R9IFthcmdzXSAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbYXJncy5wZWVyVHlwZV0gLSBPcHRpb25hbCBodW1hbi1yZWFkYWJsZSBwZWVyIGxhYmVsLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3MucGVlcklkXSAtIE9wdGlvbmFsIGV4cGxpY2l0IHBlZXIgaWQgKGRlZmF1bHRzIHRvIGEgcmFuZG9tIFVVSUQpLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioe3BlZXJUeXBlLCBwZWVySWR9ID0ge30pIHtcbiAgICBzdXBlcigpXG4gICAgdGhpcy5wZWVyVHlwZSA9IHBlZXJUeXBlXG4gICAgdGhpcy5wZWVySWQgPSBwZWVySWQgfHwgcmFuZG9tVVVJRCgpXG4gICAgdGhpcy5fY29ubmVjdGVkID0gZmFsc2VcbiAgICAvKiogQHR5cGUgeygoKSA9PiB2b2lkKSB8IHVuZGVmaW5lZH0gKi9cbiAgICB0aGlzLl91bnJlZ2lzdGVyID0gdW5kZWZpbmVkXG4gIH1cblxuICAvKiogQHJldHVybnMge3N0cmluZ30gLSBQZWVyIGlkLiAqL1xuICBnZXRQZWVySWQoKSB7IHJldHVybiB0aGlzLnBlZXJJZCB9XG5cbiAgLyoqIEByZXR1cm5zIHtib29sZWFufSAtIFdoZXRoZXIgdGhlIHBlZXIgaXMgcmVnaXN0ZXJlZCB3aXRoIHRoZSBicm9rZXIuICovXG4gIGlzQ29ubmVjdGVkKCkgeyByZXR1cm4gdGhpcy5fY29ubmVjdGVkIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIHdpdGggdGhlIGluLXByb2Nlc3MgYnJva2VyLiBJZGVtcG90ZW50LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIGFzeW5jIGNvbm5lY3QoKSB7XG4gICAgaWYgKHRoaXMuX2Nvbm5lY3RlZCkgcmV0dXJuXG5cbiAgICB0aGlzLl91bnJlZ2lzdGVyID0gcmVnaXN0ZXJJblByb2Nlc3NQZWVyKHRoaXMpXG4gICAgdGhpcy5fY29ubmVjdGVkID0gdHJ1ZVxuICAgIHRoaXMuZW1pdChcImNvbm5lY3RcIilcbiAgfVxuXG4gIC8qKlxuICAgKiBQdWJsaXNoZXMgYSBicm9hZGNhc3QgdG8gZXZlcnkgcmVnaXN0ZXJlZCBwZWVyIChpbmNsdWRpbmcgdGhpc1xuICAgKiBvbmUpLiBSZXR1cm5zIGZhbHNlIGlmIGBjb25uZWN0KClgIGhhc24ndCBiZWVuIGNhbGxlZCB5ZXQsIG1hdGNoaW5nXG4gICAqIGBCZWFjb25DbGllbnRgIHNlbWFudGljcy5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBCcm9hZGNhc3QgYXJncy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuY2hhbm5lbCAtIENoYW5uZWwgbmFtZS5cbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCBhbnk+fSBhcmdzLmJyb2FkY2FzdFBhcmFtcyAtIFJvdXRpbmcgcGFyYW1zLlxuICAgKiBAcGFyYW0ge2FueX0gYXJncy5ib2R5IC0gTWVzc2FnZSBib2R5LlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIHdoZW4gdGhlIGJyb2FkY2FzdCB3YXMgcXVldWVkLlxuICAgKi9cbiAgcHVibGlzaCh7Y2hhbm5lbCwgYnJvYWRjYXN0UGFyYW1zLCBib2R5fSkge1xuICAgIGlmICghdGhpcy5fY29ubmVjdGVkKSByZXR1cm4gZmFsc2VcblxuICAgIC8qKiBAdHlwZSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CZWFjb25Ccm9hZGNhc3RNZXNzYWdlfSAqL1xuICAgIGNvbnN0IG1lc3NhZ2UgPSB7XG4gICAgICB0eXBlOiBcImJyb2FkY2FzdFwiLFxuICAgICAgY2hhbm5lbCxcbiAgICAgIGJyb2FkY2FzdFBhcmFtcyxcbiAgICAgIGJvZHksXG4gICAgICBvcmlnaW5QZWVySWQ6IHRoaXMucGVlcklkXG4gICAgfVxuXG4gICAgcHVibGlzaFRvSW5Qcm9jZXNzUGVlcnMobWVzc2FnZSlcblxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgaGFuZGxlciBjYWxsZWQgb25jZSBmb3IgZXZlcnkgYnJvYWRjYXN0IHJlY2VpdmVkLlxuICAgKiBAcGFyYW0ge0JlYWNvbkJyb2FkY2FzdEhhbmRsZXJ9IGhhbmRsZXIgLSBIYW5kbGVyLlxuICAgKiBAcmV0dXJucyB7KCkgPT4gdm9pZH0gLSBVbnJlZ2lzdGVyIGZ1bmN0aW9uLlxuICAgKi9cbiAgb25Ccm9hZGNhc3QoaGFuZGxlcikge1xuICAgIHRoaXMub24oXCJicm9hZGNhc3RcIiwgaGFuZGxlcilcbiAgICByZXR1cm4gKCkgPT4gdGhpcy5vZmYoXCJicm9hZGNhc3RcIiwgaGFuZGxlcilcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNlaXZlcyBhIGJyb2FkY2FzdCBmcm9tIHRoZSBicm9rZXIuIENhbGxlZCBieSBgaW4tcHJvY2Vzcy1icm9rZXJgLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmVhY29uQnJvYWRjYXN0TWVzc2FnZX0gbWVzc2FnZSAtIEJyb2FkY2FzdCBtZXNzYWdlLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9yZWNlaXZlQnJvYWRjYXN0KG1lc3NhZ2UpIHtcbiAgICB0aGlzLmVtaXQoXCJicm9hZGNhc3RcIiwgbWVzc2FnZSlcbiAgfVxuXG4gIC8qKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBVbnJlZ2lzdGVycyBmcm9tIHRoZSBicm9rZXIuICovXG4gIGFzeW5jIGNsb3NlKCkge1xuICAgIGlmICh0aGlzLl91bnJlZ2lzdGVyKSB0aGlzLl91bnJlZ2lzdGVyKClcbiAgICB0aGlzLl91bnJlZ2lzdGVyID0gdW5kZWZpbmVkXG4gICAgdGhpcy5fY29ubmVjdGVkID0gZmFsc2VcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beacon broker daemon.
|
|
3
|
+
*
|
|
4
|
+
* Accepts JsonSocket connections from any number of peer processes and
|
|
5
|
+
* fans every `broadcast` message out to every connected peer.
|
|
6
|
+
* Intentionally stateless — there is no persistence, no replay, no
|
|
7
|
+
* channel-name filtering on the daemon. Per-process subscription
|
|
8
|
+
* matching (already implemented by `_broadcastToChannelLocal`) does the
|
|
9
|
+
* filtering.
|
|
10
|
+
*/
|
|
11
|
+
export default class BeaconServer {
|
|
12
|
+
/**
|
|
13
|
+
* @param {object} args - Options.
|
|
14
|
+
* @param {import("../configuration.js").default} args.configuration - Configuration.
|
|
15
|
+
* @param {string} [args.host] - Hostname to bind. Defaults to the configured beacon host.
|
|
16
|
+
* @param {number} [args.port] - Port to bind. Defaults to the configured beacon port.
|
|
17
|
+
*/
|
|
18
|
+
constructor({ configuration, host, port }: {
|
|
19
|
+
configuration: import("../configuration.js").default;
|
|
20
|
+
host?: string | undefined;
|
|
21
|
+
port?: number | undefined;
|
|
22
|
+
});
|
|
23
|
+
configuration: import("../configuration.js").default;
|
|
24
|
+
host: string;
|
|
25
|
+
port: number;
|
|
26
|
+
logger: Logger;
|
|
27
|
+
/** @type {Set<JsonSocket>} */
|
|
28
|
+
peers: Set<JsonSocket>;
|
|
29
|
+
/** @type {net.Server | undefined} */
|
|
30
|
+
server: net.Server | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* @returns {Promise<void>} - Resolves when listening.
|
|
33
|
+
*/
|
|
34
|
+
start(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* @returns {Promise<void>} - Resolves when closed.
|
|
37
|
+
*/
|
|
38
|
+
stop(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* @returns {number} - Bound port.
|
|
41
|
+
*/
|
|
42
|
+
getPort(): number;
|
|
43
|
+
/**
|
|
44
|
+
* @returns {number} - Number of connected peers.
|
|
45
|
+
*/
|
|
46
|
+
getPeerCount(): number;
|
|
47
|
+
/**
|
|
48
|
+
* @param {import("net").Socket} socket - Socket.
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
51
|
+
_handleConnection(socket: import("net").Socket): void;
|
|
52
|
+
/**
|
|
53
|
+
* @param {import("./types.js").BeaconBroadcastMessage} message - Broadcast message.
|
|
54
|
+
* @returns {void}
|
|
55
|
+
*/
|
|
56
|
+
_fanOut(message: import("./types.js").BeaconBroadcastMessage): void;
|
|
57
|
+
}
|
|
58
|
+
import Logger from "../logger.js";
|
|
59
|
+
import JsonSocket from "../background-jobs/json-socket.js";
|
|
60
|
+
import net from "net";
|
|
61
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/beacon/server.js"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH;IACE;;;;;OAKG;IACH,2CAJG;QAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO;QACvB,IAAI;QACJ,IAAI;KAC5B,EAWA;IATC,qDAAkC;IAElC,aAA+B;IAC/B,aAAyD;IACzD,eAA8B;IAC9B,8BAA8B;IAC9B,OADW,GAAG,CAAC,UAAU,CAAC,CACJ;IACtB,qCAAqC;IACrC,QADW,GAAG,CAAC,MAAM,GAAG,SAAS,CACV;IAGzB;;OAEG;IACH,SAFa,OAAO,CAAC,IAAI,CAAC,CAgBzB;IAED;;OAEG;IACH,QAFa,OAAO,CAAC,IAAI,CAAC,CAYzB;IAED;;OAEG;IACH,WAFa,MAAM,CAIlB;IAED;;OAEG;IACH,gBAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,0BAHW,OAAO,KAAK,EAAE,MAAM,GAClB,IAAI,CA6BhB;IAED;;;OAGG;IACH,iBAHW,OAAO,YAAY,EAAE,sBAAsB,GACzC,IAAI,CAUhB;CACF;mBA7HkB,cAAc;uBADV,mCAAmC;gBAF1C,KAAK"}
|