@source-repo/rpc-cli 3.0.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/LICENSE +21 -0
- package/README.md +1131 -0
- package/dist/bench.d.ts +66 -0
- package/dist/bench.d.ts.map +1 -0
- package/dist/bench.js +109 -0
- package/dist/bench.js.map +1 -0
- package/dist/broker.d.ts +61 -0
- package/dist/broker.d.ts.map +1 -0
- package/dist/broker.js +56 -0
- package/dist/broker.js.map +1 -0
- package/dist/bus.d.ts +142 -0
- package/dist/bus.d.ts.map +1 -0
- package/dist/bus.js +272 -0
- package/dist/bus.js.map +1 -0
- package/dist/bus.types.json +269 -0
- package/dist/conform.d.ts +75 -0
- package/dist/conform.d.ts.map +1 -0
- package/dist/conform.js +152 -0
- package/dist/conform.js.map +1 -0
- package/dist/console.d.ts +285 -0
- package/dist/console.d.ts.map +1 -0
- package/dist/console.js +686 -0
- package/dist/console.js.map +1 -0
- package/dist/console.types.json +1730 -0
- package/dist/extract.d.ts +37 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +272 -0
- package/dist/extract.js.map +1 -0
- package/dist/fake.d.ts +58 -0
- package/dist/fake.d.ts.map +1 -0
- package/dist/fake.js +164 -0
- package/dist/fake.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +905 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +15 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +507 -0
- package/dist/mcp.js.map +1 -0
- package/dist/network.d.ts +58 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +64 -0
- package/dist/network.js.map +1 -0
- package/dist/record.d.ts +74 -0
- package/dist/record.d.ts.map +1 -0
- package/dist/record.js +221 -0
- package/dist/record.js.map +1 -0
- package/dist/tapping.d.ts +11 -0
- package/dist/tapping.d.ts.map +1 -0
- package/dist/tapping.js +71 -0
- package/dist/tapping.js.map +1 -0
- package/dist/verbs.d.ts +59 -0
- package/dist/verbs.d.ts.map +1 -0
- package/dist/verbs.js +324 -0
- package/dist/verbs.js.map +1 -0
- package/dist/web/app.css +1 -0
- package/dist/web/app.js +2063 -0
- package/dist/web/app.js.map +1 -0
- package/dist/web/index.html +13 -0
- package/package.json +65 -0
package/dist/bench.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type NetworkOptions } from './network.js';
|
|
2
|
+
/**
|
|
3
|
+
* Calling one method over and over, and reporting what it cost.
|
|
4
|
+
*
|
|
5
|
+
* `call` already returns the time a single call took, and the console shows it once and forgets it.
|
|
6
|
+
* The question a plant actually asks is the one a single call cannot answer: this device is fine at
|
|
7
|
+
* one call a second - what does it do at twenty? Finding the rate at which a device falls over is
|
|
8
|
+
* ordinarily done by writing a script, and the script is always the same script.
|
|
9
|
+
*
|
|
10
|
+
* Percentiles rather than an average, because an average hides exactly the calls worth knowing
|
|
11
|
+
* about: a device answering in 2 ms with one reply a second taking 4 seconds averages out to
|
|
12
|
+
* something that looks healthy.
|
|
13
|
+
*/
|
|
14
|
+
export interface BenchOptions extends NetworkOptions {
|
|
15
|
+
peer: string;
|
|
16
|
+
namespace: string;
|
|
17
|
+
method: string;
|
|
18
|
+
/** Already coerced; `runBench` does that from the contract when given words. */
|
|
19
|
+
args: unknown[];
|
|
20
|
+
/** Calls per second to aim for. */
|
|
21
|
+
rate: number;
|
|
22
|
+
/** How long to keep going. */
|
|
23
|
+
forMs: number;
|
|
24
|
+
/** How many calls may be outstanding at once before the rest are counted as fallen behind. */
|
|
25
|
+
concurrency: number;
|
|
26
|
+
wait?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface BenchReport {
|
|
29
|
+
peer: string;
|
|
30
|
+
method: string;
|
|
31
|
+
calls: number;
|
|
32
|
+
ok: number;
|
|
33
|
+
failed: number;
|
|
34
|
+
/**
|
|
35
|
+
* Calls not sent because too many were already outstanding. A device that cannot keep up shows
|
|
36
|
+
* here rather than in the latencies, where it would look like the network was fine.
|
|
37
|
+
*/
|
|
38
|
+
behind: number;
|
|
39
|
+
/** How the failures broke down, by RPC error code. */
|
|
40
|
+
codes: {
|
|
41
|
+
[code: string]: number;
|
|
42
|
+
};
|
|
43
|
+
ms: {
|
|
44
|
+
min: number;
|
|
45
|
+
p50: number;
|
|
46
|
+
p90: number;
|
|
47
|
+
p95: number;
|
|
48
|
+
p99: number;
|
|
49
|
+
max: number;
|
|
50
|
+
mean: number;
|
|
51
|
+
};
|
|
52
|
+
rate: {
|
|
53
|
+
asked: number;
|
|
54
|
+
achieved: number;
|
|
55
|
+
};
|
|
56
|
+
ranForMs: number;
|
|
57
|
+
}
|
|
58
|
+
export declare const bench: (options: BenchOptions) => Promise<BenchReport>;
|
|
59
|
+
/** Coerces the words a shell gives against the peer's own contract, the way `call` does. */
|
|
60
|
+
export declare const benchArguments: (options: NetworkOptions & {
|
|
61
|
+
peer: string;
|
|
62
|
+
namespace: string;
|
|
63
|
+
method: string;
|
|
64
|
+
texts: string[];
|
|
65
|
+
}) => Promise<unknown[]>;
|
|
66
|
+
//# sourceMappingURL=bench.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bench.d.ts","sourceRoot":"","sources":["../src/bench.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAG7E;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,YAAa,SAAQ,cAAc;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,8FAA8F;IAC9F,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;IACd,sDAAsD;IACtD,KAAK,EAAE;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACjC,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAClG,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;CACnB;AAYD,eAAO,MAAM,KAAK,YAAmB,YAAY,KAAG,OAAO,CAAC,WAAW,CAgFtE,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,cAAc,YAAmB,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,uBAYlI,CAAA"}
|
package/dist/bench.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { awaitPeer, connectNetwork } from './network.js';
|
|
2
|
+
import { coerceArguments } from './verbs.js';
|
|
3
|
+
const percentile = (sorted, fraction) => {
|
|
4
|
+
if (!sorted.length)
|
|
5
|
+
return 0;
|
|
6
|
+
// Nearest-rank: with 20 samples the 95th is the 19th, which is a sample that happened rather
|
|
7
|
+
// than an interpolation between two that did.
|
|
8
|
+
const rank = Math.min(sorted.length - 1, Math.ceil(fraction * sorted.length) - 1);
|
|
9
|
+
return sorted[Math.max(0, rank)];
|
|
10
|
+
};
|
|
11
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
export const bench = async (options) => {
|
|
13
|
+
const connected = await connectNetwork(options);
|
|
14
|
+
try {
|
|
15
|
+
if (!(await awaitPeer(connected, options.peer, options.wait ?? 5000)))
|
|
16
|
+
throw Object.assign(new Error(`${options.peer} did not appear within ${options.wait ?? 5000} ms`), { code: 'ClassNotFound' });
|
|
17
|
+
const proxy = await connected.network.proxy(options.namespace, options.peer);
|
|
18
|
+
const remote = proxy.remote;
|
|
19
|
+
const latencies = [];
|
|
20
|
+
const codes = {};
|
|
21
|
+
let ok = 0;
|
|
22
|
+
let failed = 0;
|
|
23
|
+
let behind = 0;
|
|
24
|
+
let inFlight = 0;
|
|
25
|
+
const outstanding = [];
|
|
26
|
+
const fire = () => {
|
|
27
|
+
inFlight++;
|
|
28
|
+
const started = Date.now();
|
|
29
|
+
const call = remote[options.method](...options.args)
|
|
30
|
+
.then(() => {
|
|
31
|
+
ok++;
|
|
32
|
+
latencies.push(Date.now() - started);
|
|
33
|
+
})
|
|
34
|
+
.catch((e) => {
|
|
35
|
+
failed++;
|
|
36
|
+
// Counted by code rather than lumped together: a device refusing arguments and
|
|
37
|
+
// a device that stopped answering are different findings with the same shape.
|
|
38
|
+
const code = e.code ?? 'Exception';
|
|
39
|
+
codes[code] = (codes[code] ?? 0) + 1;
|
|
40
|
+
latencies.push(Date.now() - started);
|
|
41
|
+
})
|
|
42
|
+
.finally(() => {
|
|
43
|
+
inFlight--;
|
|
44
|
+
});
|
|
45
|
+
outstanding.push(call);
|
|
46
|
+
};
|
|
47
|
+
const interval = options.rate > 0 ? 1000 / options.rate : 0;
|
|
48
|
+
const began = Date.now();
|
|
49
|
+
const deadline = began + options.forMs;
|
|
50
|
+
let next = began;
|
|
51
|
+
while (Date.now() < deadline) {
|
|
52
|
+
const waiting = next - Date.now();
|
|
53
|
+
if (waiting > 0)
|
|
54
|
+
await sleep(Math.min(waiting, deadline - Date.now()));
|
|
55
|
+
if (Date.now() >= deadline)
|
|
56
|
+
break;
|
|
57
|
+
// Not sent rather than queued: piling calls onto a device that is already behind
|
|
58
|
+
// measures the queue rather than the device.
|
|
59
|
+
if (inFlight >= options.concurrency)
|
|
60
|
+
behind++;
|
|
61
|
+
else
|
|
62
|
+
fire();
|
|
63
|
+
next = interval > 0 ? next + interval : Date.now();
|
|
64
|
+
}
|
|
65
|
+
// The last calls are waited out, so a run does not report latencies it never collected.
|
|
66
|
+
await Promise.allSettled(outstanding);
|
|
67
|
+
const ranForMs = Date.now() - began;
|
|
68
|
+
const sorted = [...latencies].sort((a, b) => a - b);
|
|
69
|
+
return {
|
|
70
|
+
peer: options.peer,
|
|
71
|
+
method: `${options.namespace}.${options.method}`,
|
|
72
|
+
calls: ok + failed,
|
|
73
|
+
ok,
|
|
74
|
+
failed,
|
|
75
|
+
behind,
|
|
76
|
+
codes,
|
|
77
|
+
ms: {
|
|
78
|
+
min: sorted[0] ?? 0,
|
|
79
|
+
p50: percentile(sorted, 0.5),
|
|
80
|
+
p90: percentile(sorted, 0.9),
|
|
81
|
+
p95: percentile(sorted, 0.95),
|
|
82
|
+
p99: percentile(sorted, 0.99),
|
|
83
|
+
max: sorted[sorted.length - 1] ?? 0,
|
|
84
|
+
mean: sorted.length ? Math.round(sorted.reduce((total, value) => total + value, 0) / sorted.length) : 0
|
|
85
|
+
},
|
|
86
|
+
rate: { asked: options.rate, achieved: ranForMs ? Math.round(((ok + failed) / ranForMs) * 1000 * 10) / 10 : 0 },
|
|
87
|
+
ranForMs
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await connected.close();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
/** Coerces the words a shell gives against the peer's own contract, the way `call` does. */
|
|
95
|
+
export const benchArguments = async (options) => {
|
|
96
|
+
const connected = await connectNetwork(options);
|
|
97
|
+
try {
|
|
98
|
+
const description = await connected.network
|
|
99
|
+
.proxy('msgrpc', options.peer)
|
|
100
|
+
.then((proxy) => proxy.remote.describe())
|
|
101
|
+
.catch(() => undefined);
|
|
102
|
+
const method = description?.namespaces.find((namespace) => namespace.name === options.namespace)?.methods.find((entry) => entry.name === options.method);
|
|
103
|
+
return coerceArguments(options.texts, method, description?.types);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
await connected.close();
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=bench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bench.js","sourceRoot":"","sources":["../src/bench.ts"],"names":[],"mappings":"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"}
|
package/dist/broker.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { RpcServer, type RpcAuthenticator } from '@source-repo/rpc';
|
|
2
|
+
import type { ServerOptions as TlsServerOptions } from 'node:https';
|
|
3
|
+
import { BusService } from './bus.js';
|
|
4
|
+
/**
|
|
5
|
+
* A socket.io broker: an RpcServer that relays, and says what it is relaying when asked.
|
|
6
|
+
*
|
|
7
|
+
* There is no separate broker implementation, and there should not be. A server already learns who
|
|
8
|
+
* is connected, forwards a frame addressed to another peer, and tells every peer who else is there.
|
|
9
|
+
* Running one that way is what makes it a bus rather than a service - and that is worth a command
|
|
10
|
+
* of its own only because typing it out is tedious, not because it is a different thing.
|
|
11
|
+
*
|
|
12
|
+
* It used to expose nothing at all, so a peer addressing it by name got ClassNotFound - true, and
|
|
13
|
+
* the plainest possible statement that this is a switchboard. It now exposes exactly one namespace,
|
|
14
|
+
* `bus`, because the alternative was a --tap flag, and a plant bus that has to be restarted before
|
|
15
|
+
* it can be watched will not be watched: the run worth looking at is the one already going wrong.
|
|
16
|
+
* The consequence is stated on startup, since anyone who can reach an unauthenticated broker can
|
|
17
|
+
* now mirror everything crossing it - which they could always have done by impersonating a peer,
|
|
18
|
+
* but not this conveniently. `authenticate` and `relay` are what gate it.
|
|
19
|
+
*
|
|
20
|
+
* With `authenticate` it gates the whole bus rather than just the tap: a peer that presents no
|
|
21
|
+
* token this broker knows never reaches the RPC layer, and one that does may only claim the name
|
|
22
|
+
* its token was issued for. That is the difference between a bus behind a trusted network and a bus
|
|
23
|
+
* that can be put on one that is not.
|
|
24
|
+
*
|
|
25
|
+
* With `upstream` it dials another broker as well, which makes the two one network: each side's
|
|
26
|
+
* peers are advertised to the other, and a call crosses without either end knowing there was a hop.
|
|
27
|
+
*/
|
|
28
|
+
export interface BrokerOptions {
|
|
29
|
+
/** Listens on every interface: the library's socket.io transport takes no bind address. */
|
|
30
|
+
port: number;
|
|
31
|
+
name: string;
|
|
32
|
+
/** Brokers to join. Peers here become reachable from there, and the other way round. */
|
|
33
|
+
upstream?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Certificate and key, which is what makes this bus wss:// rather than ws://. Absent means
|
|
36
|
+
* plaintext, which is right on a segment that is already isolated or behind a terminating proxy.
|
|
37
|
+
*/
|
|
38
|
+
tls?: TlsServerOptions;
|
|
39
|
+
/**
|
|
40
|
+
* Verify what a peer presents when it dials in. Without it the broker relays for anyone who can
|
|
41
|
+
* reach the port, and every peer name on it is an unchecked claim.
|
|
42
|
+
*/
|
|
43
|
+
authenticate?: RpcAuthenticator;
|
|
44
|
+
/**
|
|
45
|
+
* Presented to each upstream, for joining a broker that authenticates. One value for all of
|
|
46
|
+
* them: a token names the peer that holds it, and this broker is one peer however many brokers
|
|
47
|
+
* it joins, so the same token is the right thing to send to each.
|
|
48
|
+
*/
|
|
49
|
+
upstreamCredentials?: unknown;
|
|
50
|
+
/** Called for every arrival and departure, so a command line can show the network filling up. */
|
|
51
|
+
onPeer?: (peer: string, state: 'online' | 'offline', where: string) => void;
|
|
52
|
+
}
|
|
53
|
+
export declare const startBroker: (options: BrokerOptions) => Promise<{
|
|
54
|
+
server: RpcServer;
|
|
55
|
+
/** The tap, so an embedder can watch frames without going through RPC to reach it. */
|
|
56
|
+
bus: BusService;
|
|
57
|
+
/** Peers reachable through this broker right now, including any learned from an upstream. */
|
|
58
|
+
peers: () => string[];
|
|
59
|
+
close: () => Promise<void>;
|
|
60
|
+
}>;
|
|
61
|
+
//# sourceMappingURL=broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broker.d.ts","sourceRoot":"","sources":["../src/broker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqC,KAAK,gBAAgB,EAAkC,MAAM,kBAAkB,CAAA;AACtI,OAAO,KAAK,EAAE,aAAa,IAAI,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAKrC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,WAAW,aAAa;IAC1B,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB;;;OAGG;IACH,GAAG,CAAC,EAAE,gBAAgB,CAAA;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAA;IAC/B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,iGAAiG;IACjG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAC9E;AAED,eAAO,MAAM,WAAW,YAAmB,aAAa;;IAyChD,sFAAsF;;IAEtF,6FAA6F;;;EAOpG,CAAA"}
|
package/dist/broker.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { RpcServer, TransportEvent } from '@source-repo/rpc';
|
|
2
|
+
import { BusService } from './bus.js';
|
|
3
|
+
// Extracted from bus.ts by `npm run contract` and committed, so a console pointed at a broker gets
|
|
4
|
+
// argument fields for tap() rather than `tap(…)`.
|
|
5
|
+
import contract from './bus.types.json' with { type: 'json' };
|
|
6
|
+
export const startBroker = async (options) => {
|
|
7
|
+
const upstream = options.upstream ?? [];
|
|
8
|
+
const bus = new BusService(options.name);
|
|
9
|
+
const server = new RpcServer({
|
|
10
|
+
name: options.name,
|
|
11
|
+
transports: [
|
|
12
|
+
{ port: options.port, ...(options.tls ? { tls: options.tls } : {}) },
|
|
13
|
+
...upstream.map((url) => ({ connect: url, ...(options.upstreamCredentials ? { credentials: options.upstreamCredentials } : {}) }))
|
|
14
|
+
],
|
|
15
|
+
...(options.authenticate ? { authenticate: options.authenticate } : {}),
|
|
16
|
+
// The tap is the only thing here, and it describes itself: a console pointed at a broker
|
|
17
|
+
// used to be told ClassNotFound, which is indistinguishable from a device whose server was
|
|
18
|
+
// started without exposeIntrospection.
|
|
19
|
+
schema: contract,
|
|
20
|
+
exposeIntrospection: true,
|
|
21
|
+
readyTimeout: 15000
|
|
22
|
+
});
|
|
23
|
+
server.exposeClassInstance(bus);
|
|
24
|
+
try {
|
|
25
|
+
// ready() reports a listener that cannot bind rather than waiting it out.
|
|
26
|
+
await server.ready();
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
// A broker that could not start still holds a socket.io server and whatever upstream links
|
|
30
|
+
// it opened. Throwing without closing them leaves them behind for the life of the process.
|
|
31
|
+
await server.close().catch(() => undefined);
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
34
|
+
const where = (transport, index) => (index === 0 ? `:${options.port}` : (upstream[index - 1] ?? transport.getName()));
|
|
35
|
+
server.transports.forEach((transport, index) => {
|
|
36
|
+
transport.on(TransportEvent.peerOnline, (peer) => options.onPeer?.(peer, 'online', where(transport, index)));
|
|
37
|
+
transport.on(TransportEvent.peerGone, (peer) => options.onPeer?.(peer, 'offline', where(transport, index)));
|
|
38
|
+
// Attached for the life of the broker rather than added and removed with each tap: the
|
|
39
|
+
// transport only emits this when something is listening, and `observe` returns on the first
|
|
40
|
+
// line when no tap matches. Attaching on demand would mean a tap started mid-flight seeing
|
|
41
|
+
// the traffic from whenever the listener happened to land instead.
|
|
42
|
+
transport.on(TransportEvent.relayed, (relayed) => bus.observe(relayed));
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
server,
|
|
46
|
+
/** The tap, so an embedder can watch frames without going through RPC to reach it. */
|
|
47
|
+
bus,
|
|
48
|
+
/** Peers reachable through this broker right now, including any learned from an upstream. */
|
|
49
|
+
peers: () => server.peers.names().filter((peer) => peer !== options.name).sort(),
|
|
50
|
+
close: async () => {
|
|
51
|
+
await bus.releaseAll();
|
|
52
|
+
await server.close();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broker.js","sourceRoot":"","sources":["../src/broker.ts"],"names":[],"mappings":"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"}
|
package/dist/bus.d.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { type RelayedFrame } from '@source-repo/rpc';
|
|
3
|
+
/**
|
|
4
|
+
* The traffic tap: what the broker is relaying, turned on while it runs.
|
|
5
|
+
*
|
|
6
|
+
* A console can only ever see its own calls and the events it subscribed to, which on a real
|
|
7
|
+
* network is a small fraction of what is happening. The broker sees everything by definition - it
|
|
8
|
+
* is the thing forwarding it - so this is where the rest becomes visible.
|
|
9
|
+
*
|
|
10
|
+
* **It is turned on by a call, not by a flag.** A plant bus that has to be restarted to be observed
|
|
11
|
+
* will not be observed: the run you want to look at is the one already going wrong. The cost of
|
|
12
|
+
* that decision is that the broker now exposes something, where it used to expose nothing at all
|
|
13
|
+
* and answer ClassNotFound to anyone who addressed it - see the note in broker.ts.
|
|
14
|
+
*
|
|
15
|
+
* What this offers over pointing a generic MQTT monitor at the same wire is that it knows what a
|
|
16
|
+
* frame *is*. A call and its reply share a correlation id, so they can be paired and the reply
|
|
17
|
+
* reported with the time it took and the method it answers - neither of which is in the reply
|
|
18
|
+
* itself. A topic browser shows you a MsgPack blob; this shows you
|
|
19
|
+
* `plantServer.plant.writeSetpoint(1200) -> ok 42ms`.
|
|
20
|
+
*/
|
|
21
|
+
/** What a tap asks to be shown. Everything is optional; a tap with no filter sees all of it. */
|
|
22
|
+
export interface TapFilter {
|
|
23
|
+
/** Only frames this peer sent or received - "mirror that device", which is the usual request. */
|
|
24
|
+
peer?: string;
|
|
25
|
+
/** Only this namespace. Applies to replies too, since a reply is paired with its call first. */
|
|
26
|
+
namespace?: string;
|
|
27
|
+
/** Only these kinds: POST, SUCCESS, ERROR, EVENT. */
|
|
28
|
+
kinds?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Include arguments, results and event payloads.
|
|
31
|
+
*
|
|
32
|
+
* Off by default, and deliberately: the metadata is what a debugging session usually needs, and
|
|
33
|
+
* a plant bus carries values that nobody meant to hand to whoever happened to be tapping.
|
|
34
|
+
*/
|
|
35
|
+
payloads?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Seconds before this tap drops itself. A console that closes without untapping would otherwise
|
|
38
|
+
* leave the broker building and emitting frames for a subscriber that is not there any more.
|
|
39
|
+
*/
|
|
40
|
+
ttl?: number;
|
|
41
|
+
}
|
|
42
|
+
/** One frame the broker passed between two peers. */
|
|
43
|
+
export interface TappedFrame {
|
|
44
|
+
at: number;
|
|
45
|
+
source: string;
|
|
46
|
+
target: string;
|
|
47
|
+
/** POST, SUCCESS, ERROR or EVENT. */
|
|
48
|
+
kind: string;
|
|
49
|
+
/** The namespace, taken from the call when this is a reply - a reply does not carry one. */
|
|
50
|
+
namespace?: string;
|
|
51
|
+
method?: string;
|
|
52
|
+
event?: string;
|
|
53
|
+
/** Correlation id, so a call and its reply can be lined up by whoever is reading. */
|
|
54
|
+
id?: string;
|
|
55
|
+
/** Milliseconds since the call this answers, when that call was seen. */
|
|
56
|
+
ms?: number;
|
|
57
|
+
/** The error code on a refused call. */
|
|
58
|
+
code?: string;
|
|
59
|
+
error?: string;
|
|
60
|
+
/** Only when a matching tap asked for payloads. */
|
|
61
|
+
params?: unknown[];
|
|
62
|
+
result?: unknown;
|
|
63
|
+
/** Which taps this frame matched, so several people can watch with different filters at once. */
|
|
64
|
+
taps: string[];
|
|
65
|
+
}
|
|
66
|
+
export interface TapRecord {
|
|
67
|
+
token: string;
|
|
68
|
+
filter: TapFilter;
|
|
69
|
+
/** Epoch milliseconds. */
|
|
70
|
+
expires: number;
|
|
71
|
+
frames: number;
|
|
72
|
+
}
|
|
73
|
+
/** Exported so the console can give its own record of a tap the same life as the tap itself. */
|
|
74
|
+
export declare const DEFAULT_TAP_TTL = 300;
|
|
75
|
+
export declare class BusService extends EventEmitter {
|
|
76
|
+
/** Named in what `taps()` reports, so a reader knows which broker they are looking at. */
|
|
77
|
+
private readonly busName;
|
|
78
|
+
rpcEvents: {
|
|
79
|
+
frame: [frame: TappedFrame];
|
|
80
|
+
};
|
|
81
|
+
private readonly active;
|
|
82
|
+
/** Calls seen and not yet answered, so a reply can be given the time it took and the method. */
|
|
83
|
+
private readonly pending;
|
|
84
|
+
private counter;
|
|
85
|
+
constructor(
|
|
86
|
+
/** Named in what `taps()` reports, so a reader knows which broker they are looking at. */
|
|
87
|
+
busName: string);
|
|
88
|
+
/**
|
|
89
|
+
* Opened when the first tap starts and closed after the last one ends, for a source that costs
|
|
90
|
+
* something to hold open - an MQTT subscription to every peer's traffic, in particular. A broker
|
|
91
|
+
* needs none of this: it is already forwarding the frames, and `observe` returns on its first
|
|
92
|
+
* line when nothing is watching.
|
|
93
|
+
*/
|
|
94
|
+
onDemand?: {
|
|
95
|
+
start: () => Promise<void>;
|
|
96
|
+
stop: () => Promise<void>;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Starts or stops the source to match whether anything is watching.
|
|
100
|
+
*
|
|
101
|
+
* Failures on the way down are swallowed and failures on the way up are not: a tap that could
|
|
102
|
+
* not subscribe has to say so, or it reports an empty network and looks like a quiet one.
|
|
103
|
+
*/
|
|
104
|
+
private settleSource;
|
|
105
|
+
/**
|
|
106
|
+
* Start watching. Returns a token: several people can tap at once with different filters, and
|
|
107
|
+
* untapping has to remove yours rather than everyone's.
|
|
108
|
+
*/
|
|
109
|
+
tap(filter?: TapFilter): Promise<{
|
|
110
|
+
token: string;
|
|
111
|
+
expires: number;
|
|
112
|
+
filter: TapFilter;
|
|
113
|
+
}>;
|
|
114
|
+
untap(token: string): Promise<{
|
|
115
|
+
tapping: boolean;
|
|
116
|
+
already: boolean;
|
|
117
|
+
}>;
|
|
118
|
+
/** Who is watching what, including how much each tap has seen. */
|
|
119
|
+
taps(): Promise<{
|
|
120
|
+
bus: string;
|
|
121
|
+
taps: TapRecord[];
|
|
122
|
+
pending: number;
|
|
123
|
+
}>;
|
|
124
|
+
/** Whether anything is listening, so the broker can leave the transport hook unattached. */
|
|
125
|
+
get tapping(): boolean;
|
|
126
|
+
private expire;
|
|
127
|
+
/**
|
|
128
|
+
* One relayed frame, from the transport that forwarded it.
|
|
129
|
+
*
|
|
130
|
+
* Everything here is best-effort reading of someone else's message: a frame whose payload is
|
|
131
|
+
* not the shape we expect is reported with what could be read rather than dropped, since a
|
|
132
|
+
* malformed frame on the bus is exactly the sort of thing a tap exists to show.
|
|
133
|
+
*/
|
|
134
|
+
observe(relayed: RelayedFrame): void;
|
|
135
|
+
private matches;
|
|
136
|
+
private readFrame;
|
|
137
|
+
/** Fills in what a reply does not carry: the method it answers, and how long it took. */
|
|
138
|
+
private pair;
|
|
139
|
+
/** Drops every tap, so a broker shutting down stops building frames for nobody. */
|
|
140
|
+
releaseAll(): Promise<void>;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../src/bus.ts"],"names":[],"mappings":"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"}
|