@honeybbq/teamspeak 1.0.0-beta.12 → 1.0.0-beta.13
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 +40 -5
- package/dist/client/capabilities.d.ts +4 -4
- package/dist/client/capabilities.d.ts.map +1 -1
- package/dist/client/capabilities.js +3 -8
- package/dist/client/capabilities.js.map +1 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +50 -9
- package/dist/client/client.js.map +1 -1
- package/dist/client/crypto/identity.d.ts +2 -0
- package/dist/client/crypto/identity.d.ts.map +1 -1
- package/dist/client/crypto/identity.js +2 -2
- package/dist/client/crypto/identity.js.map +1 -1
- package/dist/client/crypto/index.d.ts +1 -1
- package/dist/client/crypto/index.d.ts.map +1 -1
- package/dist/client/crypto/index.js +1 -1
- package/dist/client/crypto/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/trace.d.ts +7 -0
- package/dist/client/trace.d.ts.map +1 -1
- package/dist/client/transport/handler.d.ts +2 -0
- package/dist/client/transport/handler.d.ts.map +1 -1
- package/dist/client/transport/handler.js +4 -0
- package/dist/client/transport/handler.js.map +1 -1
- package/dist/client/types.d.ts +9 -2
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -1
- package/dist/client/view.d.ts +3 -3
- package/dist/client/view.d.ts.map +1 -1
- package/dist/client/view.js.map +1 -1
- package/dist/core/api/channels.d.ts +7 -3
- package/dist/core/api/channels.d.ts.map +1 -1
- package/dist/core/api/channels.js +5 -3
- package/dist/core/api/channels.js.map +1 -1
- package/dist/core/api/clients.d.ts +38 -1
- package/dist/core/api/clients.d.ts.map +1 -1
- package/dist/core/api/clients.js +33 -2
- package/dist/core/api/clients.js.map +1 -1
- package/dist/core/api/command.d.ts +18 -1
- package/dist/core/api/command.d.ts.map +1 -1
- package/dist/core/api/command.js +7 -1
- package/dist/core/api/command.js.map +1 -1
- package/dist/core/api/instance.d.ts +10 -2
- package/dist/core/api/instance.d.ts.map +1 -1
- package/dist/core/api/instance.js +6 -2
- package/dist/core/api/instance.js.map +1 -1
- package/dist/core/api/moderation.d.ts +7 -0
- package/dist/core/api/moderation.d.ts.map +1 -1
- package/dist/core/api/moderation.js +7 -0
- package/dist/core/api/moderation.js.map +1 -1
- package/dist/core/api/parse.d.ts.map +1 -1
- package/dist/core/api/parse.js +1 -0
- package/dist/core/api/parse.js.map +1 -1
- package/dist/core/api/server.d.ts +9 -0
- package/dist/core/api/server.d.ts.map +1 -1
- package/dist/core/api/server.js +3 -0
- package/dist/core/api/server.js.map +1 -1
- package/dist/core/command/responses.d.ts.map +1 -1
- package/dist/core/command/responses.js +1 -0
- package/dist/core/command/responses.js.map +1 -1
- package/dist/core/events.d.ts +47 -5
- package/dist/core/events.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/notification-name.d.ts +4 -0
- package/dist/core/notification-name.d.ts.map +1 -1
- package/dist/core/notification-name.js +4 -0
- package/dist/core/notification-name.js.map +1 -1
- package/dist/core/notifications.d.ts +6 -2
- package/dist/core/notifications.d.ts.map +1 -1
- package/dist/core/notifications.js +32 -1
- package/dist/core/notifications.js.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +16 -0
- package/dist/core/types.js.map +1 -1
- package/dist/query/client.d.ts +12 -3
- package/dist/query/client.d.ts.map +1 -1
- package/dist/query/client.js +22 -15
- package/dist/query/client.js.map +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/options.d.ts +17 -5
- package/dist/query/options.d.ts.map +1 -1
- package/dist/query/options.js +1 -0
- package/dist/query/options.js.map +1 -1
- package/dist/query/ssh.d.ts.map +1 -1
- package/dist/query/ssh.js +4 -1
- package/dist/query/ssh.js.map +1 -1
- package/dist/query/stream.d.ts +8 -3
- package/dist/query/stream.d.ts.map +1 -1
- package/dist/query/stream.js +26 -3
- package/dist/query/stream.js.map +1 -1
- package/dist/query/tcp.js +1 -1
- package/dist/query/tcp.js.map +1 -1
- package/dist/query/transport.d.ts +35 -0
- package/dist/query/transport.d.ts.map +1 -1
- package/dist/query/web.d.ts +17 -5
- package/dist/query/web.d.ts.map +1 -1
- package/dist/query/web.js +38 -9
- package/dist/query/web.js.map +1 -1
- package/package.json +6 -5
- package/src/client/capabilities.ts +14 -11
- package/src/client/client.ts +47 -6
- package/src/client/crypto/identity.ts +2 -2
- package/src/client/crypto/index.ts +1 -0
- package/src/client/index.ts +2 -0
- package/src/client/trace.ts +7 -0
- package/src/client/transport/handler.ts +5 -0
- package/src/client/types.ts +9 -0
- package/src/client/view.ts +2 -3
- package/src/core/api/channels.ts +9 -3
- package/src/core/api/clients.ts +73 -2
- package/src/core/api/command.ts +22 -1
- package/src/core/api/instance.ts +20 -2
- package/src/core/api/moderation.ts +7 -0
- package/src/core/api/parse.ts +1 -0
- package/src/core/api/server.ts +12 -0
- package/src/core/command/responses.ts +1 -0
- package/src/core/events.ts +48 -5
- package/src/core/index.ts +2 -1
- package/src/core/notification-name.ts +4 -0
- package/src/core/notifications.ts +48 -17
- package/src/core/types.ts +14 -0
- package/src/query/client.ts +28 -15
- package/src/query/index.ts +1 -1
- package/src/query/options.ts +19 -5
- package/src/query/ssh.ts +6 -1
- package/src/query/stream.ts +31 -4
- package/src/query/tcp.ts +1 -1
- package/src/query/testing/live.ts +311 -0
- package/src/query/testing/ssh-keys.ts +67 -0
- package/src/query/transport.ts +36 -0
- package/src/query/web.ts +57 -14
package/dist/query/stream.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { QueryTransport } from "./transport.js";
|
|
1
|
+
import type { QueryTransport, QueryTransportStats } from "./transport.js";
|
|
2
2
|
/**
|
|
3
3
|
* The part of a duplex byte stream a line transport uses. A `node:net` socket
|
|
4
4
|
* is one; so is an SSH shell channel, or a test double.
|
|
@@ -29,10 +29,15 @@ export declare class StreamTransport implements QueryTransport {
|
|
|
29
29
|
onClose: ((error: Error | undefined) => void) | null;
|
|
30
30
|
/**
|
|
31
31
|
* `onEnd` runs once the stream is closed, to release what the stream sits
|
|
32
|
-
* on: an SSH session's connection, say.
|
|
32
|
+
* on: an SSH session's connection, say. `identity` says which kind of
|
|
33
|
+
* connection this is and where it points, for {@link StreamTransport.stats}.
|
|
33
34
|
*/
|
|
34
|
-
constructor(stream: QueryStream, onEnd?: () => void
|
|
35
|
+
constructor(stream: QueryStream, onEnd?: () => void, identity?: {
|
|
36
|
+
readonly transport: "tcp" | "ssh";
|
|
37
|
+
readonly address: string;
|
|
38
|
+
});
|
|
35
39
|
send(line: string): Promise<void>;
|
|
40
|
+
stats(): QueryTransportStats;
|
|
36
41
|
/**
|
|
37
42
|
* Say `quit`, give the server a moment to end the session, and close.
|
|
38
43
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/query/stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/query/stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1E;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,+FAA+F;IAC/F,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC;IACvE,wBAAwB;IACxB,GAAG,IAAI,OAAO,CAAC;IACf,sCAAsC;IACtC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC;IAClE,uDAAuD;IACvD,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;IAC9D,qCAAqC;IACrC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CACnD;AAKD;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,cAAc;;IACpD,+EAA+E;IAC/E,QAAQ,CAAC,qBAAqB,QAAQ;IACtC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC/C,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAiB5D;;;;OAIG;IACH,YACE,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,IAAI,EAClB,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAU3E;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhC;IAED,KAAK,IAAI,mBAAmB,CAQ3B;IAED;;;;;;;OAOG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA2B3B;CAoBF"}
|
package/dist/query/stream.js
CHANGED
|
@@ -22,13 +22,19 @@ export class StreamTransport {
|
|
|
22
22
|
/** Wakes a `close()` that is waiting for the server to end the session. */
|
|
23
23
|
#onEnded;
|
|
24
24
|
#error;
|
|
25
|
+
#identity;
|
|
26
|
+
#openedAt = new Date();
|
|
27
|
+
#bytesSent = 0;
|
|
28
|
+
#bytesReceived = 0;
|
|
25
29
|
/**
|
|
26
30
|
* `onEnd` runs once the stream is closed, to release what the stream sits
|
|
27
|
-
* on: an SSH session's connection, say.
|
|
31
|
+
* on: an SSH session's connection, say. `identity` says which kind of
|
|
32
|
+
* connection this is and where it points, for {@link StreamTransport.stats}.
|
|
28
33
|
*/
|
|
29
|
-
constructor(stream, onEnd) {
|
|
34
|
+
constructor(stream, onEnd, identity) {
|
|
30
35
|
this.#stream = stream;
|
|
31
36
|
this.#onEnd = onEnd;
|
|
37
|
+
this.#identity = identity;
|
|
32
38
|
stream.on("data", (chunk) => this.#handleData(chunk));
|
|
33
39
|
stream.on("error", (error) => {
|
|
34
40
|
this.#error = error;
|
|
@@ -39,9 +45,25 @@ export class StreamTransport {
|
|
|
39
45
|
if (this.#ended)
|
|
40
46
|
return Promise.reject(new Error("the query connection is closed"));
|
|
41
47
|
return new Promise((resolve, reject) => {
|
|
42
|
-
this.#stream.write(`${line}\n`, (error) =>
|
|
48
|
+
this.#stream.write(`${line}\n`, (error) => {
|
|
49
|
+
if (error)
|
|
50
|
+
reject(error);
|
|
51
|
+
else {
|
|
52
|
+
this.#bytesSent += Buffer.byteLength(line) + 1;
|
|
53
|
+
resolve();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
43
56
|
});
|
|
44
57
|
}
|
|
58
|
+
stats() {
|
|
59
|
+
return {
|
|
60
|
+
connectedAt: this.#openedAt,
|
|
61
|
+
transport: this.#identity?.transport,
|
|
62
|
+
address: this.#identity?.address ?? "",
|
|
63
|
+
bytesSent: this.#bytesSent,
|
|
64
|
+
bytesReceived: this.#bytesReceived,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
45
67
|
/**
|
|
46
68
|
* Say `quit`, give the server a moment to end the session, and close.
|
|
47
69
|
*
|
|
@@ -78,6 +100,7 @@ export class StreamTransport {
|
|
|
78
100
|
this.#handleClose();
|
|
79
101
|
}
|
|
80
102
|
#handleData(chunk) {
|
|
103
|
+
this.#bytesReceived += chunk.byteLength;
|
|
81
104
|
for (const line of this.#reader.push(chunk)) {
|
|
82
105
|
if (this.#banner > 0) {
|
|
83
106
|
this.#banner--;
|
package/dist/query/stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/query/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAoBxC,+EAA+E;AAC/E,MAAM,YAAY,GAAG,KAAK,CAAC;AAE3B;;;;;GAKG;AACH,MAAM,OAAO,eAAe;IAC1B,+EAA+E;IACtE,qBAAqB,GAAG,IAAI,CAAC;IACtC,MAAM,GAAoC,IAAI,CAAC;IAC/C,OAAO,GAAgD,IAAI,CAAC;IAE5D,OAAO,CAAc;IACrB,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC;IAC3B,MAAM,CAA2B;IACjC,0DAA0D;IAC1D,OAAO,GAAG,CAAC,CAAC;IACZ,QAAQ,GAAG,KAAK,CAAC;IACjB,MAAM,GAAG,KAAK,CAAC;IACf,2EAA2E;IAC3E,QAAQ,CAA2B;IACnC,MAAM,CAAoB;
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/query/stream.ts"],"names":[],"mappings":"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"}
|
package/dist/query/tcp.js
CHANGED
|
@@ -11,7 +11,7 @@ export async function connectTcp(options) {
|
|
|
11
11
|
signal?.throwIfAborted();
|
|
12
12
|
const stream = await connect(host, port);
|
|
13
13
|
await opened(stream, signal);
|
|
14
|
-
return new StreamTransport(stream);
|
|
14
|
+
return new StreamTransport(stream, undefined, { transport: "tcp", address: `${host}:${port}` });
|
|
15
15
|
}
|
|
16
16
|
/** A connected `node:net` socket, with whole lines sent as they are written. */
|
|
17
17
|
function defaultConnect(host, port) {
|
package/dist/query/tcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tcp.js","sourceRoot":"","sources":["../../src/query/tcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAgC9C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAmB;IAClD,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,KAAK,EAAE,MAAM,EAAE,OAAO,GAAG,cAAc,EAAE,GAAG,OAAO,CAAC;IACzE,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACzC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"tcp.js","sourceRoot":"","sources":["../../src/query/tcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAgC9C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAmB;IAClD,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,KAAK,EAAE,MAAM,EAAE,OAAO,GAAG,cAAc,EAAE,GAAG,OAAO,CAAC;IACzE,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACzC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,IAAI,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;AAClG,CAAC;AAED,gFAAgF;AAChF,SAAS,cAAc,CAAC,IAAY,EAAE,IAAY;IAChD,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,uEAAuE;IACvE,YAAY;IACZ,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,MAAM,CAAC,MAAc,EAAE,MAA+B;IAC7D,MAAM,UAAU,GAAI,MAA4C,CAAC,UAAU,CAAC;IAC5E,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAClD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,CAAC,KAAY,EAAQ,EAAE;YACpC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,CAAC,MAAO,CAAC,MAAe,CAAC,CAAC;QAClC,CAAC,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE;YAC1B,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a connection reports about itself, enough for a metrics exporter to
|
|
3
|
+
* publish without reaching into the client.
|
|
4
|
+
*
|
|
5
|
+
* @experimental Part of the `@honeybbq/teamspeak/query` entry, which may
|
|
6
|
+
* change in a minor release.
|
|
7
|
+
*/
|
|
8
|
+
export interface QueryTransportStats {
|
|
9
|
+
/** When the current connection was opened; undefined when there is none. */
|
|
10
|
+
readonly connectedAt: Date | undefined;
|
|
11
|
+
/** The kind of connection; undefined when there is none, or for a transport that does not say. */
|
|
12
|
+
readonly transport: "tcp" | "ssh" | "web" | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Where it points: `host:port` for raw TCP, `user@host:port` for SSH, the
|
|
15
|
+
* base URL for WebQuery. Never a password or an api key; "" when there is
|
|
16
|
+
* no connection.
|
|
17
|
+
*/
|
|
18
|
+
readonly address: string;
|
|
19
|
+
/**
|
|
20
|
+
* What the line protocol has written, line terminators included; for
|
|
21
|
+
* WebQuery, the request bodies. It is what the transport sent, not what the
|
|
22
|
+
* socket did: headers and retries are not counted.
|
|
23
|
+
*/
|
|
24
|
+
readonly bytesSent: number;
|
|
25
|
+
/**
|
|
26
|
+
* What the server has sent, banner included: the bytes read from the stream
|
|
27
|
+
* for raw TCP and SSH, the response bodies for WebQuery.
|
|
28
|
+
*/
|
|
29
|
+
readonly bytesReceived: number;
|
|
30
|
+
}
|
|
1
31
|
/**
|
|
2
32
|
* The connection a ServerQuery client talks over.
|
|
3
33
|
*
|
|
@@ -33,5 +63,10 @@ export interface QueryTransport {
|
|
|
33
63
|
onClose: ((error: Error | undefined) => void) | null;
|
|
34
64
|
/** End the connection. */
|
|
35
65
|
close(): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* What the connection reports about itself, for `QueryClient.stats`. A
|
|
68
|
+
* transport that does not keep counts leaves it out.
|
|
69
|
+
*/
|
|
70
|
+
stats?(): QueryTransportStats;
|
|
36
71
|
}
|
|
37
72
|
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/query/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;IACxC,qEAAqE;IACrE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC;;;OAGG;IACH,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,oFAAoF;IACpF,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACrD,0BAA0B;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/query/transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,IAAI,GAAG,SAAS,CAAC;IACvC,kGAAkG;IAClG,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;IACtD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;IACxC,qEAAqE;IACrE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC;;;OAGG;IACH,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,oFAAoF;IACpF,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACrD,0BAA0B;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,IAAI,mBAAmB,CAAC;CAC/B"}
|
package/dist/query/web.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TeamspeakError } from "@honeybbq/teamspeak";
|
|
2
|
-
import type { QueryTransport } from "./transport.js";
|
|
2
|
+
import type { QueryTransport, QueryTransportStats } from "./transport.js";
|
|
3
3
|
/**
|
|
4
4
|
* A WebQuery request that failed with an HTTP status and no TeamSpeak error in
|
|
5
5
|
* its answer: a wrong or missing api key, a server that is not a WebQuery. A
|
|
@@ -41,7 +41,11 @@ export type WebCredentials = {
|
|
|
41
41
|
};
|
|
42
42
|
/** How a WebQuery is reached. */
|
|
43
43
|
export type WebQueryOptions = WebCredentials & {
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* The server's base URL, such as `http://127.0.0.1:10080` or
|
|
46
|
+
* `https://ts.example.com:10443`. The default `fetch` speaks `http:` and
|
|
47
|
+
* `https:` and refuses any other scheme.
|
|
48
|
+
*/
|
|
45
49
|
readonly url: string;
|
|
46
50
|
/**
|
|
47
51
|
* The virtual server to address; `use` changes it. Unset leaves the server out
|
|
@@ -51,9 +55,16 @@ export type WebQueryOptions = WebCredentials & {
|
|
|
51
55
|
*/
|
|
52
56
|
readonly serverID?: number | bigint | undefined;
|
|
53
57
|
/**
|
|
54
|
-
* The `fetch` to use. By default a small one over `node:http
|
|
55
|
-
* `
|
|
56
|
-
* standard blocks.
|
|
58
|
+
* The `fetch` to use. By default a small one over `node:http` for an `http:`
|
|
59
|
+
* URL and `node:https` for an `https:` one: the global `fetch` refuses port
|
|
60
|
+
* 10080, WebQuery's own default, as a port the Fetch standard blocks.
|
|
61
|
+
*
|
|
62
|
+
* Over HTTPS the default verifies the server's certificate against the
|
|
63
|
+
* trust store Node uses, and never skips that. A server whose certificate
|
|
64
|
+
* comes from a private CA, or is self-signed, is trusted one of two ways:
|
|
65
|
+
* start Node with `NODE_EXTRA_CA_CERTS=ca.pem`, or pass a `fetch` that
|
|
66
|
+
* trusts the CA, such as one over `node:https` with an `https.Agent` whose
|
|
67
|
+
* `ca` is that certificate.
|
|
57
68
|
*/
|
|
58
69
|
readonly fetch?: typeof fetch | undefined;
|
|
59
70
|
};
|
|
@@ -79,6 +90,7 @@ export declare class WebQueryTransport implements QueryTransport {
|
|
|
79
90
|
onClose: ((error: Error | undefined) => void) | null;
|
|
80
91
|
constructor(options: WebQueryOptions);
|
|
81
92
|
send(line: string): Promise<void>;
|
|
93
|
+
stats(): QueryTransportStats;
|
|
82
94
|
close(): Promise<void>;
|
|
83
95
|
}
|
|
84
96
|
//# sourceMappingURL=web.d.ts.map
|
package/dist/query/web.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/query/web.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/query/web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1E;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;IACnD,4DAA4D;IAC5D,gBAAyB,IAAI,EAAE,MAAM,CAAuB;IAU5D,uBAAuB;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,YAAY,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAG7C;CACF;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GACtB;IACE,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB,GACD;IACE,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC;CACzB,CAAC;AAEN,iCAAiC;AACjC,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,GAAG,SAAS,CAAC;CAC3C,CAAC;AAYF;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAkB,YAAW,cAAc;;IACtD,wEAAwE;IACxE,QAAQ,CAAC,qBAAqB,SAAS;IACvC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC/C,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAW5D,YAAY,OAAO,EAAE,eAAe,EAsBnC;IAEK,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDtC;IAED,KAAK,IAAI,mBAAmB,CAY3B;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAKrB;CAmBF"}
|
package/dist/query/web.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { request } from "node:http";
|
|
1
|
+
import { request as httpRequest } from "node:http";
|
|
2
|
+
import { request as httpsRequest } from "node:https";
|
|
2
3
|
import { TeamspeakError } from "@honeybbq/teamspeak";
|
|
3
4
|
import { buildCommandOrdered, parseCommand, splitCommandRows } from "../core/internal.js";
|
|
4
5
|
/**
|
|
@@ -49,6 +50,9 @@ export class WebQueryTransport {
|
|
|
49
50
|
#prefix;
|
|
50
51
|
#fetch;
|
|
51
52
|
#closed = false;
|
|
53
|
+
#openedAt = new Date();
|
|
54
|
+
#bytesSent = 0;
|
|
55
|
+
#bytesReceived = 0;
|
|
52
56
|
constructor(options) {
|
|
53
57
|
this.#base = options.url.replace(/\/+$/, "");
|
|
54
58
|
if (options.guest === true) {
|
|
@@ -61,7 +65,13 @@ export class WebQueryTransport {
|
|
|
61
65
|
}
|
|
62
66
|
this.#apiKey = options.apiKey;
|
|
63
67
|
this.#prefix = options.serverID === undefined ? "" : `/${options.serverID}`;
|
|
64
|
-
|
|
68
|
+
if (options.fetch === undefined) {
|
|
69
|
+
const { protocol } = new URL(options.url);
|
|
70
|
+
if (protocol !== "http:" && protocol !== "https:") {
|
|
71
|
+
throw new TypeError(`WebQuery is reached over http: or https:, not ${protocol}; pass a fetch to use another scheme`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this.#fetch = options.fetch ?? nodeFetch;
|
|
65
75
|
}
|
|
66
76
|
async send(line) {
|
|
67
77
|
if (this.#closed)
|
|
@@ -75,17 +85,21 @@ export class WebQueryTransport {
|
|
|
75
85
|
return;
|
|
76
86
|
}
|
|
77
87
|
const params = rows.map((row) => ({ ...row.params }));
|
|
88
|
+
const body = JSON.stringify(params.length === 1 ? params[0] : params);
|
|
78
89
|
const response = await this.#fetch(`${this.#base}${this.#prefix}/${name}`, {
|
|
79
90
|
method: "POST",
|
|
80
91
|
headers: {
|
|
81
92
|
...(this.#apiKey === undefined ? {} : { "x-api-key": this.#apiKey }),
|
|
82
93
|
"content-type": "application/json",
|
|
83
94
|
},
|
|
84
|
-
body
|
|
95
|
+
body,
|
|
85
96
|
});
|
|
97
|
+
this.#bytesSent += Buffer.byteLength(body);
|
|
86
98
|
let answer;
|
|
87
99
|
try {
|
|
88
|
-
|
|
100
|
+
const text = await response.text();
|
|
101
|
+
this.#bytesReceived += Buffer.byteLength(text);
|
|
102
|
+
answer = JSON.parse(text);
|
|
89
103
|
}
|
|
90
104
|
catch {
|
|
91
105
|
// Not JSON: nothing the server said about the command.
|
|
@@ -93,8 +107,8 @@ export class WebQueryTransport {
|
|
|
93
107
|
const code = answer?.status?.code;
|
|
94
108
|
if (typeof code !== "number")
|
|
95
109
|
throw new WebQueryHttpError(response.status, response.statusText);
|
|
96
|
-
const
|
|
97
|
-
for (const row of Array.isArray(
|
|
110
|
+
const rowsBody = answer?.body;
|
|
111
|
+
for (const row of Array.isArray(rowsBody) ? rowsBody : []) {
|
|
98
112
|
if (typeof row === "object" && row !== null)
|
|
99
113
|
this.onLine?.(rowLine(row));
|
|
100
114
|
}
|
|
@@ -107,6 +121,19 @@ export class WebQueryTransport {
|
|
|
107
121
|
...Object.entries(tail),
|
|
108
122
|
]));
|
|
109
123
|
}
|
|
124
|
+
stats() {
|
|
125
|
+
// The base URL as given, without a user or password a caller put in it.
|
|
126
|
+
const url = new URL(this.#base);
|
|
127
|
+
url.username = "";
|
|
128
|
+
url.password = "";
|
|
129
|
+
return {
|
|
130
|
+
connectedAt: this.#openedAt,
|
|
131
|
+
transport: "web",
|
|
132
|
+
address: url.toString().replace(/\/+$/, ""),
|
|
133
|
+
bytesSent: this.#bytesSent,
|
|
134
|
+
bytesReceived: this.#bytesReceived,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
110
137
|
close() {
|
|
111
138
|
if (this.#closed)
|
|
112
139
|
return Promise.resolve();
|
|
@@ -139,10 +166,12 @@ function rowLine(row) {
|
|
|
139
166
|
return buildCommandOrdered("", fields).slice(1);
|
|
140
167
|
}
|
|
141
168
|
/**
|
|
142
|
-
* The part of `fetch` this transport uses, over `node:http
|
|
143
|
-
* string body, answered with a `Response`.
|
|
169
|
+
* The part of `fetch` this transport uses, over `node:http` or `node:https`
|
|
170
|
+
* by the URL's scheme: a POST with a string body, answered with a `Response`.
|
|
171
|
+
* An HTTPS certificate is verified the way Node does by default.
|
|
144
172
|
*/
|
|
145
|
-
const
|
|
173
|
+
const nodeFetch = ((url, init) => new Promise((resolve, reject) => {
|
|
174
|
+
const request = new URL(url).protocol === "https:" ? httpsRequest : httpRequest;
|
|
146
175
|
const req = request(url, { method: init.method, headers: init.headers }, (res) => {
|
|
147
176
|
const chunks = [];
|
|
148
177
|
res.on("data", (chunk) => chunks.push(chunk));
|
package/dist/query/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/query/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/query/web.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@honeybbq/teamspeak",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.13",
|
|
4
4
|
"description": "TeamSpeak SDK for TypeScript: a clean-room voice client for Node.js, and the typed commands, ids and errors every connection shares",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"client",
|
|
@@ -94,16 +94,17 @@
|
|
|
94
94
|
},
|
|
95
95
|
"scripts": {
|
|
96
96
|
"build": "tsc -p tsconfig.build.json",
|
|
97
|
-
"test": "vitest run",
|
|
97
|
+
"test": "vitest run --project @honeybbq/teamspeak --project scripts",
|
|
98
98
|
"test:watch": "vitest",
|
|
99
99
|
"lint": "oxlint --deny-warnings .",
|
|
100
100
|
"fmt": "oxfmt .",
|
|
101
101
|
"fmt:check": "oxfmt --check .",
|
|
102
102
|
"typecheck": "tsc --noEmit",
|
|
103
103
|
"check": "pnpm typecheck && pnpm lint && pnpm fmt:check",
|
|
104
|
-
"test:integration": "vitest run
|
|
105
|
-
"test:integration:client": "vitest run
|
|
106
|
-
"test:integration:query": "vitest run src/query/integration.test.ts --reporter=verbose",
|
|
104
|
+
"test:integration": "vitest run --project integration --exclude src/query/instance.integration.test.ts --reporter=verbose",
|
|
105
|
+
"test:integration:client": "vitest run --project integration src/client/ --reporter=verbose",
|
|
106
|
+
"test:integration:query": "vitest run --project integration src/query/ --exclude src/query/instance.integration.test.ts --reporter=verbose",
|
|
107
|
+
"test:integration:instance": "TEAMSPEAK_QUERY_DESTRUCTIVE=1 TEAMSPEAK_QUERY_STOP_INSTANCE=1 vitest run --project integration src/query/instance.integration.test.ts --reporter=verbose",
|
|
107
108
|
"test:integration:ts3": "TEAMSPEAK_ADDR=127.0.0.1:9987 TEAMSPEAK_DEFAULT_CHANNEL=Alpha TEAMSPEAK_PASSWORD_CHANNEL=Secret TEAMSPEAK_PASSWORD_CHANNEL_PASSWORD=hunter2 pnpm test:integration",
|
|
108
109
|
"test:integration:ts6": "TEAMSPEAK_ADDR=127.0.0.1:9988 TEAMSPEAK_DEFAULT_CHANNEL=Alpha TEAMSPEAK_PASSWORD_CHANNEL=Secret TEAMSPEAK_PASSWORD_CHANNEL_PASSWORD=hunter2 pnpm test:integration",
|
|
109
110
|
"test:integration:both": "pnpm test:integration:ts3 && pnpm test:integration:ts6",
|
|
@@ -12,7 +12,14 @@
|
|
|
12
12
|
* round trip.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
asServerID,
|
|
17
|
+
HostMessageMode,
|
|
18
|
+
serverFamilyForVersion,
|
|
19
|
+
type IconID,
|
|
20
|
+
type ServerFamily,
|
|
21
|
+
type ServerID,
|
|
22
|
+
} from "@honeybbq/teamspeak";
|
|
16
23
|
import { parseIconID, parseUint64 } from "../core/internal.js";
|
|
17
24
|
import { uidAlgorithmForVersion, type UIDAlgorithm } from "./crypto/identity.js";
|
|
18
25
|
|
|
@@ -51,10 +58,10 @@ export interface ServerCapabilities {
|
|
|
51
58
|
readonly hostMessage: string;
|
|
52
59
|
/**
|
|
53
60
|
* How the host message is to be shown (`virtualserver_hostmessage_mode`).
|
|
54
|
-
*
|
|
55
|
-
*
|
|
61
|
+
* A value the `HostMessageMode` table does not name is passed through as the
|
|
62
|
+
* server sent it.
|
|
56
63
|
*/
|
|
57
|
-
readonly hostMessageMode:
|
|
64
|
+
readonly hostMessageMode: HostMessageMode;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
/**
|
|
@@ -70,14 +77,10 @@ export function parseServerCapabilities(
|
|
|
70
77
|
const parsed = Number.parseInt(version, 10);
|
|
71
78
|
const major = Number.isNaN(parsed) ? 0 : parsed;
|
|
72
79
|
|
|
73
|
-
let family: ServerFamily = "unknown";
|
|
74
|
-
if (major === 3) family = "ts3";
|
|
75
|
-
else if (major >= 6) family = "ts6";
|
|
76
|
-
|
|
77
80
|
return {
|
|
78
81
|
version,
|
|
79
82
|
major,
|
|
80
|
-
family,
|
|
83
|
+
family: serverFamilyForVersion(version),
|
|
81
84
|
uidAlgorithm: uidAlgorithmForVersion(version),
|
|
82
85
|
name: params["virtualserver_name"] ?? "",
|
|
83
86
|
iconID: parseIconID(params["virtualserver_icon_id"]),
|
|
@@ -100,7 +103,7 @@ export function unknownServerCapabilities(): ServerCapabilities {
|
|
|
100
103
|
return parseServerCapabilities("");
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
function hostMessageMode(value: string | undefined):
|
|
106
|
+
function hostMessageMode(value: string | undefined): HostMessageMode {
|
|
104
107
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
105
|
-
return Number.isNaN(parsed) ?
|
|
108
|
+
return (Number.isNaN(parsed) ? HostMessageMode.None : parsed) as HostMessageMode;
|
|
106
109
|
}
|
package/src/client/client.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { callHook } from "./hooks.js";
|
|
|
16
16
|
import { puzzleOf } from "./handshake/crypt-handshake.js";
|
|
17
17
|
import {
|
|
18
18
|
type ChannelID,
|
|
19
|
-
type
|
|
19
|
+
type ClientEntry,
|
|
20
20
|
type ClientID,
|
|
21
21
|
type Codec,
|
|
22
22
|
type KickedEvent,
|
|
@@ -152,7 +152,7 @@ export interface ClientInternals {
|
|
|
152
152
|
nickname: string;
|
|
153
153
|
clid: number;
|
|
154
154
|
/** The clients this one knows of, as its events and `invokerGroups` read them. */
|
|
155
|
-
readonly clients: ReadonlyMap<number,
|
|
155
|
+
readonly clients: ReadonlyMap<number, ClientEntry>;
|
|
156
156
|
capabilities: ServerCapabilities;
|
|
157
157
|
/** Mark the attempt in progress connected. */
|
|
158
158
|
markConnected(initServer?: Readonly<Record<string, string>>): void;
|
|
@@ -217,6 +217,27 @@ interface AttemptTrace {
|
|
|
217
217
|
welcomeDone: boolean;
|
|
218
218
|
/** The channel rows the welcome sequence has delivered so far. */
|
|
219
219
|
channels: number;
|
|
220
|
+
/** Where the key exchange stands, before the shared secret exists. */
|
|
221
|
+
stage: "init1" | "puzzleSolving" | "puzzleAnswered";
|
|
222
|
+
/** The last Init1 step of the server's that was answered; -1 for none. */
|
|
223
|
+
init1Step: number;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** What an attempt is waiting for now, for the report of an attempt that ended. */
|
|
227
|
+
function describeWaiting(trace: AttemptTrace, clientEkPending: boolean): string {
|
|
228
|
+
if (trace.phase === ConnectPhase.Open) {
|
|
229
|
+
return trace.target === "" ? "the server address to resolve" : "the connection to open";
|
|
230
|
+
}
|
|
231
|
+
if (trace.phase === ConnectPhase.Join) {
|
|
232
|
+
return clientEkPending
|
|
233
|
+
? "the server's answer to clientek"
|
|
234
|
+
: "the server's answer to clientinit";
|
|
235
|
+
}
|
|
236
|
+
if (trace.stage === "puzzleSolving") return "the RSA puzzle to be solved";
|
|
237
|
+
if (trace.stage === "puzzleAnswered") return "the server's answer to the RSA puzzle";
|
|
238
|
+
return trace.init1Step < 0
|
|
239
|
+
? "the server's first answer to init1"
|
|
240
|
+
: `the server's init1 step after step ${trace.init1Step}`;
|
|
220
241
|
}
|
|
221
242
|
|
|
222
243
|
/** A session that began with `sessionStart`, until its `sessionEnd`. */
|
|
@@ -407,7 +428,7 @@ export class Client implements CommandExecutor, AsyncDisposable {
|
|
|
407
428
|
#throttle = new CommandThrottle();
|
|
408
429
|
#cmdTrack = new CommandTracker(() => this.#clid);
|
|
409
430
|
#viewStore: ViewStore;
|
|
410
|
-
#clients: Map<number,
|
|
431
|
+
#clients: Map<number, ClientEntry>;
|
|
411
432
|
#attempt: ConnectAttempt = newConnectAttempt();
|
|
412
433
|
|
|
413
434
|
#listeners = new Map<keyof EventMap, Subscription[]>();
|
|
@@ -577,17 +598,27 @@ export class Client implements CommandExecutor, AsyncDisposable {
|
|
|
577
598
|
sessionStarted: false,
|
|
578
599
|
welcomeDone: false,
|
|
579
600
|
channels: 0,
|
|
601
|
+
stage: "init1",
|
|
602
|
+
init1Step: -1,
|
|
580
603
|
};
|
|
581
604
|
attempt.trace = trace;
|
|
582
|
-
attempt.onFail = (error) =>
|
|
605
|
+
attempt.onFail = (error) => {
|
|
606
|
+
const waitingFor = describeWaiting(trace, this.#handler.clientEkPending);
|
|
607
|
+
this.#logger.info("connection attempt ended", {
|
|
608
|
+
phase: trace.phase,
|
|
609
|
+
waitingFor,
|
|
610
|
+
error: String(error),
|
|
611
|
+
});
|
|
583
612
|
this.#callHook(this.#trace?.attemptFailed, () => ({
|
|
584
613
|
attempt: trace.attempt,
|
|
585
614
|
reconnect: trace.reconnect,
|
|
586
615
|
phase: trace.phase,
|
|
587
616
|
error,
|
|
588
617
|
target: trace.target,
|
|
618
|
+
waitingFor,
|
|
589
619
|
durationMs: performance.now() - trace.startedAt,
|
|
590
620
|
}));
|
|
621
|
+
};
|
|
591
622
|
|
|
592
623
|
// The handshake may complete just as the signal fires, in which case
|
|
593
624
|
// #failConnection leaves the session alone and the caller gets the
|
|
@@ -1376,12 +1407,18 @@ export class Client implements CommandExecutor, AsyncDisposable {
|
|
|
1376
1407
|
if (this.#crypt.cryptoInitComplete) return;
|
|
1377
1408
|
const signal = this.#attempt.signal;
|
|
1378
1409
|
const handler = this.#handler;
|
|
1410
|
+
const trace = this.#attempt.trace;
|
|
1411
|
+
const step = p.data[0] ?? 0x7f;
|
|
1379
1412
|
const response = processInit1(this.#crypt, p.data, signal);
|
|
1380
1413
|
if (response instanceof Promise) {
|
|
1381
1414
|
// The puzzle: solved in slices, so the loop keeps running meanwhile.
|
|
1415
|
+
if (trace !== undefined) trace.stage = "puzzleSolving";
|
|
1382
1416
|
response.then(
|
|
1383
1417
|
(answer) => {
|
|
1384
|
-
if (answer && !signal.aborted)
|
|
1418
|
+
if (answer && !signal.aborted) {
|
|
1419
|
+
handler.sendPacket(PacketType.Init1, answer, 0);
|
|
1420
|
+
if (trace !== undefined) trace.stage = "puzzleAnswered";
|
|
1421
|
+
}
|
|
1385
1422
|
},
|
|
1386
1423
|
(error: unknown) => {
|
|
1387
1424
|
// An attempt that ended did so for its own reason; the abandoned
|
|
@@ -1391,6 +1428,7 @@ export class Client implements CommandExecutor, AsyncDisposable {
|
|
|
1391
1428
|
);
|
|
1392
1429
|
} else if (response) {
|
|
1393
1430
|
handler.sendPacket(PacketType.Init1, response, 0);
|
|
1431
|
+
if (trace !== undefined) trace.init1Step = step === 0x7f ? -1 : step;
|
|
1394
1432
|
}
|
|
1395
1433
|
return;
|
|
1396
1434
|
}
|
|
@@ -1628,7 +1666,7 @@ export class Client implements CommandExecutor, AsyncDisposable {
|
|
|
1628
1666
|
}
|
|
1629
1667
|
}
|
|
1630
1668
|
|
|
1631
|
-
#processNotificationResult(result: NotificationResult, leaving?:
|
|
1669
|
+
#processNotificationResult(result: NotificationResult, leaving?: ClientEntry): void {
|
|
1632
1670
|
switch (result.kind) {
|
|
1633
1671
|
case "clientEnter": {
|
|
1634
1672
|
const info = result.info;
|
|
@@ -1741,6 +1779,9 @@ export class Client implements CommandExecutor, AsyncDisposable {
|
|
|
1741
1779
|
case "clientChatComposing":
|
|
1742
1780
|
this.#emitServerEvent({ name: "clientChatComposing", payload: result.event });
|
|
1743
1781
|
break;
|
|
1782
|
+
case "clientChatClosed":
|
|
1783
|
+
this.#emitServerEvent({ name: "clientChatClosed", payload: result.event });
|
|
1784
|
+
break;
|
|
1744
1785
|
case "poked":
|
|
1745
1786
|
this.#emitServerEvent({ name: "poked", payload: result.event });
|
|
1746
1787
|
break;
|
|
@@ -13,8 +13,8 @@ const P256_SCALAR_SIZE = 32;
|
|
|
13
13
|
const P256_UNCOMPRESSED_KEY_SIZE = 65;
|
|
14
14
|
const P256_POINT_PREFIX = 0x04;
|
|
15
15
|
|
|
16
|
-
/** The
|
|
17
|
-
const MAX_SECURITY_LEVEL = 160;
|
|
16
|
+
/** The highest security level there is: the level counts zero bits of a SHA-1 digest, which has 160. */
|
|
17
|
+
export const MAX_SECURITY_LEVEL = 160;
|
|
18
18
|
|
|
19
19
|
let createIdentity: (privateKey: KeyObject, offset: bigint) => Identity;
|
|
20
20
|
let privateKeyOf: (identity: Identity) => KeyObject;
|
package/src/client/index.ts
CHANGED
package/src/client/trace.ts
CHANGED
|
@@ -136,6 +136,13 @@ export interface AttemptFailedInfo extends AttemptInfo {
|
|
|
136
136
|
readonly error: unknown;
|
|
137
137
|
/** The `host:port` being dialled; empty when the attempt failed before `resolveDone`. */
|
|
138
138
|
readonly target: string;
|
|
139
|
+
/**
|
|
140
|
+
* What the attempt was waiting for when it ended, in words for a person or a
|
|
141
|
+
* log, such as `the server's answer to clientek`. It is finer than
|
|
142
|
+
* {@link AttemptFailedInfo.phase}, and its wording may change in any release:
|
|
143
|
+
* do not match on it.
|
|
144
|
+
*/
|
|
145
|
+
readonly waitingFor: string;
|
|
139
146
|
/** Milliseconds since `resolveStart`. */
|
|
140
147
|
readonly durationMs: number;
|
|
141
148
|
}
|