@honeybbq/teamspeak 1.0.0-beta.12 → 1.0.0-beta.14

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.
Files changed (160) hide show
  1. package/README.md +59 -6
  2. package/dist/client/api/files.d.ts +89 -0
  3. package/dist/client/api/files.d.ts.map +1 -0
  4. package/dist/client/api/files.js +125 -0
  5. package/dist/client/api/files.js.map +1 -0
  6. package/dist/client/capabilities.d.ts +4 -4
  7. package/dist/client/capabilities.d.ts.map +1 -1
  8. package/dist/client/capabilities.js +3 -8
  9. package/dist/client/capabilities.js.map +1 -1
  10. package/dist/client/client.d.ts +2 -2
  11. package/dist/client/client.d.ts.map +1 -1
  12. package/dist/client/client.js +50 -9
  13. package/dist/client/client.js.map +1 -1
  14. package/dist/client/crypto/identity.d.ts +2 -0
  15. package/dist/client/crypto/identity.d.ts.map +1 -1
  16. package/dist/client/crypto/identity.js +2 -2
  17. package/dist/client/crypto/identity.js.map +1 -1
  18. package/dist/client/crypto/index.d.ts +1 -1
  19. package/dist/client/crypto/index.d.ts.map +1 -1
  20. package/dist/client/crypto/index.js +1 -1
  21. package/dist/client/crypto/index.js.map +1 -1
  22. package/dist/client/index.d.ts +2 -1
  23. package/dist/client/index.d.ts.map +1 -1
  24. package/dist/client/index.js +2 -1
  25. package/dist/client/index.js.map +1 -1
  26. package/dist/client/trace.d.ts +7 -0
  27. package/dist/client/trace.d.ts.map +1 -1
  28. package/dist/client/transport/handler.d.ts +2 -0
  29. package/dist/client/transport/handler.d.ts.map +1 -1
  30. package/dist/client/transport/handler.js +4 -0
  31. package/dist/client/transport/handler.js.map +1 -1
  32. package/dist/client/types.d.ts +9 -2
  33. package/dist/client/types.d.ts.map +1 -1
  34. package/dist/client/types.js +2 -0
  35. package/dist/client/types.js.map +1 -1
  36. package/dist/client/view.d.ts +3 -3
  37. package/dist/client/view.d.ts.map +1 -1
  38. package/dist/client/view.js.map +1 -1
  39. package/dist/core/api/channels.d.ts +60 -4
  40. package/dist/core/api/channels.d.ts.map +1 -1
  41. package/dist/core/api/channels.js +69 -4
  42. package/dist/core/api/channels.js.map +1 -1
  43. package/dist/core/api/clients.d.ts +125 -2
  44. package/dist/core/api/clients.d.ts.map +1 -1
  45. package/dist/core/api/clients.js +127 -2
  46. package/dist/core/api/clients.js.map +1 -1
  47. package/dist/core/api/command.d.ts +58 -2
  48. package/dist/core/api/command.d.ts.map +1 -1
  49. package/dist/core/api/command.js +33 -2
  50. package/dist/core/api/command.js.map +1 -1
  51. package/dist/core/api/files.d.ts +39 -1
  52. package/dist/core/api/files.d.ts.map +1 -1
  53. package/dist/core/api/files.js +46 -1
  54. package/dist/core/api/files.js.map +1 -1
  55. package/dist/core/api/groups.d.ts +28 -3
  56. package/dist/core/api/groups.d.ts.map +1 -1
  57. package/dist/core/api/groups.js +32 -3
  58. package/dist/core/api/groups.js.map +1 -1
  59. package/dist/core/api/instance.d.ts +10 -2
  60. package/dist/core/api/instance.d.ts.map +1 -1
  61. package/dist/core/api/instance.js +6 -2
  62. package/dist/core/api/instance.js.map +1 -1
  63. package/dist/core/api/moderation.d.ts +41 -0
  64. package/dist/core/api/moderation.d.ts.map +1 -1
  65. package/dist/core/api/moderation.js +41 -1
  66. package/dist/core/api/moderation.js.map +1 -1
  67. package/dist/core/api/parse.d.ts.map +1 -1
  68. package/dist/core/api/parse.js +1 -0
  69. package/dist/core/api/parse.js.map +1 -1
  70. package/dist/core/api/server.d.ts +103 -1
  71. package/dist/core/api/server.d.ts.map +1 -1
  72. package/dist/core/api/server.js +108 -1
  73. package/dist/core/api/server.js.map +1 -1
  74. package/dist/core/command/responses.d.ts.map +1 -1
  75. package/dist/core/command/responses.js +1 -0
  76. package/dist/core/command/responses.js.map +1 -1
  77. package/dist/core/events.d.ts +47 -5
  78. package/dist/core/events.d.ts.map +1 -1
  79. package/dist/core/index.d.ts +3 -3
  80. package/dist/core/index.d.ts.map +1 -1
  81. package/dist/core/index.js +1 -1
  82. package/dist/core/index.js.map +1 -1
  83. package/dist/core/notification-name.d.ts +4 -0
  84. package/dist/core/notification-name.d.ts.map +1 -1
  85. package/dist/core/notification-name.js +4 -0
  86. package/dist/core/notification-name.js.map +1 -1
  87. package/dist/core/notifications.d.ts +6 -2
  88. package/dist/core/notifications.d.ts.map +1 -1
  89. package/dist/core/notifications.js +32 -1
  90. package/dist/core/notifications.js.map +1 -1
  91. package/dist/core/types.d.ts +7 -0
  92. package/dist/core/types.d.ts.map +1 -1
  93. package/dist/core/types.js +16 -0
  94. package/dist/core/types.js.map +1 -1
  95. package/dist/query/client.d.ts +12 -3
  96. package/dist/query/client.d.ts.map +1 -1
  97. package/dist/query/client.js +98 -20
  98. package/dist/query/client.js.map +1 -1
  99. package/dist/query/index.d.ts +1 -1
  100. package/dist/query/index.d.ts.map +1 -1
  101. package/dist/query/late-rows.d.ts +31 -0
  102. package/dist/query/late-rows.d.ts.map +1 -0
  103. package/dist/query/late-rows.js +49 -0
  104. package/dist/query/late-rows.js.map +1 -0
  105. package/dist/query/options.d.ts +17 -5
  106. package/dist/query/options.d.ts.map +1 -1
  107. package/dist/query/options.js +1 -0
  108. package/dist/query/options.js.map +1 -1
  109. package/dist/query/ssh.d.ts.map +1 -1
  110. package/dist/query/ssh.js +4 -1
  111. package/dist/query/ssh.js.map +1 -1
  112. package/dist/query/stream.d.ts +8 -3
  113. package/dist/query/stream.d.ts.map +1 -1
  114. package/dist/query/stream.js +26 -3
  115. package/dist/query/stream.js.map +1 -1
  116. package/dist/query/tcp.js +1 -1
  117. package/dist/query/tcp.js.map +1 -1
  118. package/dist/query/transport.d.ts +35 -0
  119. package/dist/query/transport.d.ts.map +1 -1
  120. package/dist/query/web.d.ts +17 -5
  121. package/dist/query/web.d.ts.map +1 -1
  122. package/dist/query/web.js +38 -9
  123. package/dist/query/web.js.map +1 -1
  124. package/package.json +6 -5
  125. package/src/client/api/files.ts +249 -0
  126. package/src/client/capabilities.ts +14 -11
  127. package/src/client/client.ts +49 -8
  128. package/src/client/crypto/identity.ts +2 -2
  129. package/src/client/crypto/index.ts +1 -0
  130. package/src/client/index.ts +3 -0
  131. package/src/client/trace.ts +7 -0
  132. package/src/client/transport/handler.ts +5 -0
  133. package/src/client/types.ts +9 -0
  134. package/src/client/view.ts +2 -3
  135. package/src/core/api/channels.ts +131 -4
  136. package/src/core/api/clients.ts +240 -3
  137. package/src/core/api/command.ts +96 -2
  138. package/src/core/api/files.ts +72 -1
  139. package/src/core/api/groups.ts +64 -3
  140. package/src/core/api/instance.ts +20 -2
  141. package/src/core/api/moderation.ts +57 -1
  142. package/src/core/api/parse.ts +1 -0
  143. package/src/core/api/server.ts +206 -1
  144. package/src/core/command/responses.ts +1 -0
  145. package/src/core/events.ts +48 -5
  146. package/src/core/index.ts +10 -2
  147. package/src/core/notification-name.ts +4 -0
  148. package/src/core/notifications.ts +48 -17
  149. package/src/core/types.ts +14 -0
  150. package/src/query/client.ts +110 -19
  151. package/src/query/index.ts +1 -1
  152. package/src/query/late-rows.ts +64 -0
  153. package/src/query/options.ts +19 -5
  154. package/src/query/ssh.ts +6 -1
  155. package/src/query/stream.ts +31 -4
  156. package/src/query/tcp.ts +1 -1
  157. package/src/query/testing/live.ts +311 -0
  158. package/src/query/testing/ssh-keys.ts +67 -0
  159. package/src/query/transport.ts +36 -0
  160. package/src/query/web.ts +57 -14
package/README.md CHANGED
@@ -296,7 +296,25 @@ sends a raw command line and resolves to the rows that answer it.
296
296
 
297
297
  ### File Transfers
298
298
 
299
- Negotiate with the `files` module, then move the bytes over TCP:
299
+ `uploadFile` and `downloadFile` (from `@honeybbq/teamspeak/client`) negotiate the
300
+ transfer and move the bytes in one call, and `listFilesRecursive` (from the
301
+ package) lists a directory and everything under it, each entry with its full path:
302
+
303
+ ```typescript
304
+ import { downloadFile, uploadFile } from "@honeybbq/teamspeak/client";
305
+ import { listFilesRecursive } from "@honeybbq/teamspeak";
306
+
307
+ await uploadFile(client, cid, "/docs/notes.txt", data);
308
+ const bytes = await downloadFile(client, cid, "/docs/notes.txt");
309
+ const tree = await listFilesRecursive(client, cid, "/", { maxDepth: 4 });
310
+ ```
311
+
312
+ They work over a voice `Client`, and over a `QueryClient` on raw TCP or SSH when
313
+ passed the file transfer host as `{ host }`; WebQuery refuses every file command.
314
+ `downloadFile` takes `{ dest }` to stream into a `Writable` instead of holding
315
+ the file, and refuses a file over `maxSize` (256 MiB) in memory.
316
+
317
+ To control the two halves, negotiate with the `files` module, then move the bytes over TCP:
300
318
 
301
319
  ```typescript
302
320
  import { Readable } from "node:stream";
@@ -537,11 +555,11 @@ the way the voice protocol cannot: the same typed commands the core exports,
537
555
  plus the ones only an administrator may run, over the three transports a
538
556
  ServerQuery speaks.
539
557
 
540
- | Transport | Where | Server | Login | Notifications |
541
- | --------- | ------------ | ---------------- | ----------------- | ------------- |
542
- | `tcp` | 10011 | TeamSpeak 3 only | `transport.login` | yes |
543
- | `ssh` | 10022 | both | the SSH login | yes |
544
- | `web` | 10080 (HTTP) | both | the api key | **no** |
558
+ | Transport | Where | Server | Login | Notifications |
559
+ | --------- | --------------------------- | ---------------- | ----------------- | ------------- |
560
+ | `tcp` | 10011 | TeamSpeak 3 only | `transport.login` | yes |
561
+ | `ssh` | 10022 | both | the SSH login | yes |
562
+ | `web` | 10080 (HTTP), 10443 (HTTPS) | both | the api key | **no** |
545
563
 
546
564
  TeamSpeak 3 (3.12 and later for WebQuery, 3.3 and later for SSH) serves SSH and
547
565
  WebQuery as well as raw TCP, but only the raw port is on until
@@ -550,6 +568,41 @@ WebQuery as well as raw TCP, but only the raw port is on until
550
568
  WebQuery also answers a request with no api key as a guest; TeamSpeak 3 refuses
551
569
  one.
552
570
 
571
+ WebQuery is reached over `http:` or `https:`, by the scheme of `url`. TeamSpeak 3
572
+ serves HTTPS on 10443 once `query_protocols` lists `https` and
573
+ `query_https_certificate_file` and `query_https_private_key_file` are set. The
574
+ built-in `fetch` verifies the certificate against the trust store Node uses and
575
+ never skips that. A certificate from a private CA, or a self-signed one, is
576
+ trusted by starting Node with `NODE_EXTRA_CA_CERTS=ca.pem`, or by a `fetch`
577
+ that trusts the CA:
578
+
579
+ ```typescript
580
+ import { readFileSync } from "node:fs";
581
+ import { Agent, request } from "node:https";
582
+
583
+ const agent = new Agent({ ca: readFileSync("ca.pem") });
584
+ const trusting = ((url: string, init: RequestInit) =>
585
+ new Promise<Response>((resolve, reject) => {
586
+ const req = request(
587
+ url,
588
+ { method: init.method, headers: init.headers as never, agent },
589
+ (res) => {
590
+ const chunks: Buffer[] = [];
591
+ res.on("data", (chunk: Buffer) => chunks.push(chunk));
592
+ res.on("end", () =>
593
+ resolve(new Response(Buffer.concat(chunks), { status: res.statusCode })),
594
+ );
595
+ },
596
+ );
597
+ req.on("error", reject);
598
+ req.end(init.body as string);
599
+ })) as typeof fetch;
600
+
601
+ new QueryClient({
602
+ transport: { kind: "web", url: "https://ts.example.com:10443", apiKey, fetch: trusting },
603
+ });
604
+ ```
605
+
553
606
  Only raw TCP and SSH carry notifications: the server pushes them down the same
554
607
  connection. WebQuery has no session to push to and refuses to register, so the
555
608
  client never tries, and a listener for a server event there throws rather than
@@ -0,0 +1,89 @@
1
+ /**
2
+ * One-call file transfers: negotiate the transfer, move the bytes, and return.
3
+ *
4
+ * `initFileUpload` and `uploadFileData` (and the download twins) are the two
5
+ * halves of a transfer; these run both, over the connection that sends the
6
+ * commands. That is a voice `Client`, or a `QueryClient` over raw TCP or SSH:
7
+ * measured on TeamSpeak 3 3.13.8 (raw TCP, SSH, voice) and TeamSpeak 6
8
+ * beta13.1 (SSH, voice), the key a query connection negotiates opens the same
9
+ * file transfer port a voice client's does. WebQuery refuses every file command
10
+ * (error 5120), on both servers.
11
+ */
12
+ import { Readable, Writable } from "node:stream";
13
+ import type { ChannelID, CommandExecutor, FileDownloadOptions, FileUploadOptions } from "@honeybbq/teamspeak";
14
+ import type { FileTransferOptions } from "../transfer.js";
15
+ /**
16
+ * How {@link uploadFile} negotiates and moves the file: the options of
17
+ * `initFileUpload`, without `resume` (the whole file is sent), and those of
18
+ * `uploadFileData`. `signal` and `timeoutMs` apply to the negotiation, and the
19
+ * signal to the transfer too.
20
+ */
21
+ export interface UploadFileOptions extends Omit<FileUploadOptions, "resume">, FileTransferOptions {
22
+ /**
23
+ * The host of the file transfer port. A voice client sends to the host it
24
+ * connected to, so it needs none; any other connection must name it. A host
25
+ * given here wins over the client's own, and so does `socketFactory`.
26
+ */
27
+ readonly host?: string | undefined;
28
+ /**
29
+ * The size in bytes of a stream passed as the data, which the server needs
30
+ * before it takes the file. Required for a `Readable`; for bytes it is their
31
+ * length, and a different value here is refused.
32
+ */
33
+ readonly size?: bigint | number | undefined;
34
+ }
35
+ /** How {@link downloadFile} negotiates and moves the file. */
36
+ export interface DownloadFileOptions extends Omit<FileDownloadOptions, "seekPosition">, FileTransferOptions {
37
+ /** The host of the file transfer port, as for {@link UploadFileOptions}. */
38
+ readonly host?: string | undefined;
39
+ /**
40
+ * Write the file into this stream instead of returning its bytes. The stream
41
+ * is not ended, as for `downloadFileData`.
42
+ */
43
+ readonly dest?: Writable | undefined;
44
+ /**
45
+ * The largest file, in bytes, to hold in memory when no `dest` is given;
46
+ * 268435456 (256 MiB) by default. A larger file is refused with a
47
+ * {@link FileTransferError} before any byte is read; the transfer the server
48
+ * negotiated is left unused.
49
+ */
50
+ readonly maxSize?: number | undefined;
51
+ }
52
+ /**
53
+ * Upload a file into a channel's file area: negotiate the transfer
54
+ * (`initFileUpload`) and send the bytes (`uploadFileData`), in one call.
55
+ *
56
+ * `data` is the bytes, or a `Readable` with `options.size` the number of bytes
57
+ * it will give. An existing file is not replaced unless `overwrite` is set, and
58
+ * the directory the file goes in must exist (a missing one is refused with
59
+ * 2054, invalid file path): make it with `createDirectory` first. A refusal of
60
+ * the negotiation is thrown as the ServerError it is, before anything is sent;
61
+ * a failure of the connection to the file transfer port is a
62
+ * {@link FileTransferError}.
63
+ *
64
+ * Over a voice `Client` the bytes go through its own transfer sockets and to the
65
+ * host it connected to. Over any other connection, such as a `QueryClient`,
66
+ * name the host of the file transfer port in `options.host`.
67
+ */
68
+ export declare function uploadFile(client: CommandExecutor, channelID: ChannelID, path: string, data: Uint8Array | Readable, options?: UploadFileOptions): Promise<void>;
69
+ /**
70
+ * Download a file from a channel's file area into memory: negotiate the
71
+ * transfer (`initFileDownload`) and read the bytes (`downloadFileData`), in one
72
+ * call. With `options.dest` the bytes go into that stream instead and nothing
73
+ * is held, which is the way to fetch a large file.
74
+ *
75
+ * A file over `maxSize` (256 MiB unless set) is refused when no `dest` is given.
76
+ * A file that is not there is the ServerError of the negotiation: 2051 on TS3
77
+ * 3.13.8 and TS6 beta13.1. A path that names a directory is not refused by the
78
+ * server: it announces a size and sends nothing, so the call rejects with a
79
+ * {@link FileTransferError} for the bytes that never came.
80
+ *
81
+ * Over a voice `Client` the bytes come through its own transfer sockets from
82
+ * the host it connected to. Over any other connection, name the host of the
83
+ * file transfer port in `options.host`.
84
+ */
85
+ export declare function downloadFile(client: CommandExecutor, channelID: ChannelID, path: string, options: DownloadFileOptions & {
86
+ readonly dest: Writable;
87
+ }): Promise<void>;
88
+ export declare function downloadFile(client: CommandExecutor, channelID: ChannelID, path: string, options?: DownloadFileOptions): Promise<Uint8Array>;
89
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../src/client/api/files.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EAEf,mBAAmB,EAEnB,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,EAAE,mBAAmB;IAC/F;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED,8DAA8D;AAC9D,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,mBAAmB,EAAE,cAAc,CAAC,EAAE,mBAAmB;IACtE,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAqFD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,GAAG,QAAQ,EAC3B,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GACzD,OAAO,CAAC,IAAI,CAAC,CAAC;AACjB,wBAAgB,YAAY,CAC1B,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,UAAU,CAAC,CAAC"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * One-call file transfers: negotiate the transfer, move the bytes, and return.
3
+ *
4
+ * `initFileUpload` and `uploadFileData` (and the download twins) are the two
5
+ * halves of a transfer; these run both, over the connection that sends the
6
+ * commands. That is a voice `Client`, or a `QueryClient` over raw TCP or SSH:
7
+ * measured on TeamSpeak 3 3.13.8 (raw TCP, SSH, voice) and TeamSpeak 6
8
+ * beta13.1 (SSH, voice), the key a query connection negotiates opens the same
9
+ * file transfer port a voice client's does. WebQuery refuses every file command
10
+ * (error 5120), on both servers.
11
+ */
12
+ import { Readable, Writable } from "node:stream";
13
+ import { initFileDownload, initFileUpload } from "@honeybbq/teamspeak";
14
+ import { FileTransferError } from "../errors.js";
15
+ import { downloadFileData, uploadFileData } from "../transfer.js";
16
+ function isTransferCapable(client) {
17
+ const candidate = client;
18
+ return (typeof candidate.host === "string" &&
19
+ typeof candidate.uploadFileData === "function" &&
20
+ typeof candidate.downloadFileData === "function");
21
+ }
22
+ /**
23
+ * How to move the bytes over this connection: through the client's own
24
+ * transfer sockets when it has them and the caller named neither a host nor a
25
+ * socket factory, else by dialling `options.host`.
26
+ */
27
+ function routeFor(client, options) {
28
+ if (isTransferCapable(client) && options.host === undefined && !options.socketFactory) {
29
+ return {
30
+ upload: (info, data, o) => client.uploadFileData(client.host, info, data, o),
31
+ download: (info, dest, o) => client.downloadFileData(client.host, info, dest, o),
32
+ };
33
+ }
34
+ const host = options.host ?? (isTransferCapable(client) ? client.host : undefined);
35
+ if (host === undefined || host === "") {
36
+ throw new TypeError("the file transfer port is on a host this connection does not name: pass it as options.host");
37
+ }
38
+ return {
39
+ upload: (info, data, o) => uploadFileData(host, info, data, o),
40
+ download: (info, dest, o) => downloadFileData(host, info, dest, o),
41
+ };
42
+ }
43
+ /** The part of the options a transfer's data phase reads. */
44
+ function transferOptions(options) {
45
+ return {
46
+ signal: options.signal,
47
+ socketFactory: options.socketFactory,
48
+ trace: options.trace,
49
+ idleTimeoutMs: options.idleTimeoutMs,
50
+ };
51
+ }
52
+ function sizeOf(data, declared) {
53
+ const size = declared === undefined ? undefined : BigInt(declared);
54
+ if (size !== undefined && (size < 0n || !Number.isInteger(Number(declared)))) {
55
+ throw new RangeError(`size is a non-negative whole number of bytes, not ${String(declared)}`);
56
+ }
57
+ if (data instanceof Uint8Array) {
58
+ if (size !== undefined && size !== BigInt(data.length)) {
59
+ throw new TypeError(`size says ${size} bytes and the data is ${data.length}`);
60
+ }
61
+ return BigInt(data.length);
62
+ }
63
+ if (size === undefined) {
64
+ throw new TypeError("a stream has no known length: pass options.size, in bytes");
65
+ }
66
+ return size;
67
+ }
68
+ /**
69
+ * Upload a file into a channel's file area: negotiate the transfer
70
+ * (`initFileUpload`) and send the bytes (`uploadFileData`), in one call.
71
+ *
72
+ * `data` is the bytes, or a `Readable` with `options.size` the number of bytes
73
+ * it will give. An existing file is not replaced unless `overwrite` is set, and
74
+ * the directory the file goes in must exist (a missing one is refused with
75
+ * 2054, invalid file path): make it with `createDirectory` first. A refusal of
76
+ * the negotiation is thrown as the ServerError it is, before anything is sent;
77
+ * a failure of the connection to the file transfer port is a
78
+ * {@link FileTransferError}.
79
+ *
80
+ * Over a voice `Client` the bytes go through its own transfer sockets and to the
81
+ * host it connected to. Over any other connection, such as a `QueryClient`,
82
+ * name the host of the file transfer port in `options.host`.
83
+ */
84
+ export async function uploadFile(client, channelID, path, data, options = {}) {
85
+ const size = sizeOf(data, options.size);
86
+ const route = routeFor(client, options);
87
+ const info = await initFileUpload(client, channelID, path, size, {
88
+ password: options.password,
89
+ overwrite: options.overwrite,
90
+ signal: options.signal,
91
+ timeoutMs: options.timeoutMs,
92
+ });
93
+ const source = data instanceof Uint8Array ? Readable.from([data]) : data;
94
+ await route.upload(info, source, transferOptions(options));
95
+ }
96
+ export async function downloadFile(client, channelID, path, options = {}) {
97
+ const maxSize = options.maxSize ?? DEFAULT_MAX_SIZE;
98
+ if (!Number.isFinite(maxSize) || maxSize < 0) {
99
+ throw new RangeError(`maxSize is a non-negative number of bytes, not ${String(maxSize)}`);
100
+ }
101
+ const route = routeFor(client, options);
102
+ const info = await initFileDownload(client, channelID, path, {
103
+ password: options.password,
104
+ signal: options.signal,
105
+ timeoutMs: options.timeoutMs,
106
+ });
107
+ if (options.dest !== undefined) {
108
+ await route.download(info, options.dest, transferOptions(options));
109
+ return;
110
+ }
111
+ if (info.size > BigInt(maxSize)) {
112
+ throw new FileTransferError(`the file is ${info.size} bytes, over the ${maxSize} bytes maxSize allows in memory`);
113
+ }
114
+ const chunks = [];
115
+ const sink = new Writable({
116
+ write(chunk, _encoding, done) {
117
+ chunks.push(chunk);
118
+ done();
119
+ },
120
+ });
121
+ await route.download(info, sink, transferOptions(options));
122
+ return Buffer.concat(chunks);
123
+ }
124
+ const DEFAULT_MAX_SIZE = 256 * 1024 * 1024;
125
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../../src/client/api/files.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@
11
11
  * its name, icon, banner and welcome message — so reading it costs no extra
12
12
  * round trip.
13
13
  */
14
- import { type IconID, type ServerFamily, type ServerID } from "@honeybbq/teamspeak";
14
+ import { HostMessageMode, type IconID, type ServerFamily, type ServerID } from "@honeybbq/teamspeak";
15
15
  import { type UIDAlgorithm } from "./crypto/identity.js";
16
16
  /** What a server said about itself in the handshake. */
17
17
  export interface ServerCapabilities {
@@ -48,10 +48,10 @@ export interface ServerCapabilities {
48
48
  readonly hostMessage: string;
49
49
  /**
50
50
  * How the host message is to be shown (`virtualserver_hostmessage_mode`).
51
- * Compare it with `HostMessageMode`; a server may send a value the table
52
- * does not name.
51
+ * A value the `HostMessageMode` table does not name is passed through as the
52
+ * server sent it.
53
53
  */
54
- readonly hostMessageMode: number;
54
+ readonly hostMessageMode: HostMessageMode;
55
55
  }
56
56
  /**
57
57
  * Read what a server says about itself in `initserver`, or a version string
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/client/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAc,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEhG,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEjF,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,uFAAuF;IACvF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,gFAAgF;IAChF,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,uGAAuG;IACvG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2FAA2F;IAC3F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACpD,kBAAkB,CAyBpB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,kBAAkB,CAE9D"}
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/client/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAEL,eAAe,EAEf,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEjF,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,uFAAuF;IACvF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,gFAAgF;IAChF,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,uGAAuG;IACvG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2FAA2F;IAC3F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACpD,kBAAkB,CAqBpB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,kBAAkB,CAE9D"}
@@ -11,7 +11,7 @@
11
11
  * its name, icon, banner and welcome message — so reading it costs no extra
12
12
  * round trip.
13
13
  */
14
- import { asServerID } from "@honeybbq/teamspeak";
14
+ import { asServerID, HostMessageMode, serverFamilyForVersion, } from "@honeybbq/teamspeak";
15
15
  import { parseIconID, parseUint64 } from "../core/internal.js";
16
16
  import { uidAlgorithmForVersion } from "./crypto/identity.js";
17
17
  /**
@@ -23,15 +23,10 @@ export function parseServerCapabilities(initserver) {
23
23
  const version = params["virtualserver_version"] ?? "";
24
24
  const parsed = Number.parseInt(version, 10);
25
25
  const major = Number.isNaN(parsed) ? 0 : parsed;
26
- let family = "unknown";
27
- if (major === 3)
28
- family = "ts3";
29
- else if (major >= 6)
30
- family = "ts6";
31
26
  return {
32
27
  version,
33
28
  major,
34
- family,
29
+ family: serverFamilyForVersion(version),
35
30
  uidAlgorithm: uidAlgorithmForVersion(version),
36
31
  name: params["virtualserver_name"] ?? "",
37
32
  iconID: parseIconID(params["virtualserver_icon_id"]),
@@ -54,6 +49,6 @@ export function unknownServerCapabilities() {
54
49
  }
55
50
  function hostMessageMode(value) {
56
51
  const parsed = Number.parseInt(value ?? "", 10);
57
- return Number.isNaN(parsed) ? 0 : parsed;
52
+ return (Number.isNaN(parsed) ? HostMessageMode.None : parsed);
58
53
  }
59
54
  //# sourceMappingURL=capabilities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/client/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAiD,MAAM,qBAAqB,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAqB,MAAM,sBAAsB,CAAC;AA2CjF;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,UAAqD;IAErD,MAAM,MAAM,GACV,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IACtF,MAAM,OAAO,GAAG,MAAM,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAEhD,IAAI,MAAM,GAAiB,SAAS,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC;QAAE,MAAM,GAAG,KAAK,CAAC;SAC3B,IAAI,KAAK,IAAI,CAAC;QAAE,MAAM,GAAG,KAAK,CAAC;IAEpC,OAAO;QACL,OAAO;QACP,KAAK;QACL,MAAM;QACN,YAAY,EAAE,sBAAsB,CAAC,OAAO,CAAC;QAC7C,IAAI,EAAE,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE;QACxC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;QACpD,kBAAkB,EAAE,MAAM,CAAC,kCAAkC,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS;QACnF,cAAc,EAAE,MAAM,CAAC,8BAA8B,CAAC,IAAI,EAAE;QAC5D,QAAQ,EAAE,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,SAAS,EAAE,MAAM,CAAC,iCAAiC,CAAC,IAAI,EAAE;QAC1D,WAAW,EAAE,MAAM,CAAC,2BAA2B,CAAC,IAAI,EAAE;QACtD,eAAe,EAAE,eAAe,CAAC,MAAM,CAAC,gCAAgC,CAAC,CAAC;KAC3E,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,uBAAuB,CAAC,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,eAAe,CAAC,KAAyB;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/client/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,UAAU,EACV,eAAe,EACf,sBAAsB,GAIvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAqB,MAAM,sBAAsB,CAAC;AA2CjF;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,UAAqD;IAErD,MAAM,MAAM,GACV,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IACtF,MAAM,OAAO,GAAG,MAAM,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAEhD,OAAO;QACL,OAAO;QACP,KAAK;QACL,MAAM,EAAE,sBAAsB,CAAC,OAAO,CAAC;QACvC,YAAY,EAAE,sBAAsB,CAAC,OAAO,CAAC;QAC7C,IAAI,EAAE,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE;QACxC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;QACpD,kBAAkB,EAAE,MAAM,CAAC,kCAAkC,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS;QACnF,cAAc,EAAE,MAAM,CAAC,8BAA8B,CAAC,IAAI,EAAE;QAC5D,QAAQ,EAAE,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,SAAS,EAAE,MAAM,CAAC,iCAAiC,CAAC,IAAI,EAAE;QAC1D,WAAW,EAAE,MAAM,CAAC,2BAA2B,CAAC,IAAI,EAAE;QACtD,eAAe,EAAE,eAAe,CAAC,MAAM,CAAC,gCAAgC,CAAC,CAAC;KAC3E,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,uBAAuB,CAAC,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,eAAe,CAAC,KAAyB;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAoB,CAAC;AACnF,CAAC"}
@@ -225,11 +225,11 @@ export declare class Client implements CommandExecutor, AsyncDisposable {
225
225
  * Upload `data` over a negotiated transfer, through the client's
226
226
  * `fileTransferSocketFactory` if it has one.
227
227
  */
228
- uploadFileData(host: string, info: FileUploadInfo, data: Readable, options?: Pick<FileTransferOptions, "signal" | "trace">): Promise<void>;
228
+ uploadFileData(host: string, info: FileUploadInfo, data: Readable, options?: Pick<FileTransferOptions, "signal" | "trace" | "idleTimeoutMs">): Promise<void>;
229
229
  /**
230
230
  * Download a negotiated transfer into `dest`, through the client's
231
231
  * `fileTransferSocketFactory` if it has one.
232
232
  */
233
- downloadFileData(host: string, info: FileDownloadInfo, dest: Writable, options?: Pick<FileTransferOptions, "signal" | "trace">): Promise<void>;
233
+ downloadFileData(host: string, info: FileDownloadInfo, dest: Writable, options?: Pick<FileTransferOptions, "signal" | "trace" | "idleTimeoutMs">): Promise<void>;
234
234
  }
235
235
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
@@ -83,6 +83,24 @@ const DEFAULT_COMMAND_TIMEOUT_MS = 10_000;
83
83
  const DISCONNECT_TIMEOUT_MS = 1_000;
84
84
  /** How long each reconnection attempt may take. */
85
85
  const RECONNECT_ATTEMPT_TIMEOUT_MS = 30_000;
86
+ /** What an attempt is waiting for now, for the report of an attempt that ended. */
87
+ function describeWaiting(trace, clientEkPending) {
88
+ if (trace.phase === ConnectPhase.Open) {
89
+ return trace.target === "" ? "the server address to resolve" : "the connection to open";
90
+ }
91
+ if (trace.phase === ConnectPhase.Join) {
92
+ return clientEkPending
93
+ ? "the server's answer to clientek"
94
+ : "the server's answer to clientinit";
95
+ }
96
+ if (trace.stage === "puzzleSolving")
97
+ return "the RSA puzzle to be solved";
98
+ if (trace.stage === "puzzleAnswered")
99
+ return "the server's answer to the RSA puzzle";
100
+ return trace.init1Step < 0
101
+ ? "the server's first answer to init1"
102
+ : `the server's init1 step after step ${trace.init1Step}`;
103
+ }
86
104
  /** The `initserver` parameters a trace never sees, because they could carry a secret. */
87
105
  const SECRET_INITSERVER_PARAMS = new Set([
88
106
  "client_server_password",
@@ -364,16 +382,27 @@ export class Client {
364
382
  sessionStarted: false,
365
383
  welcomeDone: false,
366
384
  channels: 0,
385
+ stage: "init1",
386
+ init1Step: -1,
367
387
  };
368
388
  attempt.trace = trace;
369
- attempt.onFail = (error) => this.#callHook(this.#trace?.attemptFailed, () => ({
370
- attempt: trace.attempt,
371
- reconnect: trace.reconnect,
372
- phase: trace.phase,
373
- error,
374
- target: trace.target,
375
- durationMs: performance.now() - trace.startedAt,
376
- }));
389
+ attempt.onFail = (error) => {
390
+ const waitingFor = describeWaiting(trace, this.#handler.clientEkPending);
391
+ this.#logger.info("connection attempt ended", {
392
+ phase: trace.phase,
393
+ waitingFor,
394
+ error: String(error),
395
+ });
396
+ this.#callHook(this.#trace?.attemptFailed, () => ({
397
+ attempt: trace.attempt,
398
+ reconnect: trace.reconnect,
399
+ phase: trace.phase,
400
+ error,
401
+ target: trace.target,
402
+ waitingFor,
403
+ durationMs: performance.now() - trace.startedAt,
404
+ }));
405
+ };
377
406
  // The handshake may complete just as the signal fires, in which case
378
407
  // #failConnection leaves the session alone and the caller gets the
379
408
  // connection it made rather than one it would believe had failed.
@@ -1080,12 +1109,19 @@ export class Client {
1080
1109
  return;
1081
1110
  const signal = this.#attempt.signal;
1082
1111
  const handler = this.#handler;
1112
+ const trace = this.#attempt.trace;
1113
+ const step = p.data[0] ?? 0x7f;
1083
1114
  const response = processInit1(this.#crypt, p.data, signal);
1084
1115
  if (response instanceof Promise) {
1085
1116
  // The puzzle: solved in slices, so the loop keeps running meanwhile.
1117
+ if (trace !== undefined)
1118
+ trace.stage = "puzzleSolving";
1086
1119
  response.then((answer) => {
1087
- if (answer && !signal.aborted)
1120
+ if (answer && !signal.aborted) {
1088
1121
  handler.sendPacket(PacketType.Init1, answer, 0);
1122
+ if (trace !== undefined)
1123
+ trace.stage = "puzzleAnswered";
1124
+ }
1089
1125
  }, (error) => {
1090
1126
  // An attempt that ended did so for its own reason; the abandoned
1091
1127
  // solve has nothing more to report.
@@ -1095,6 +1131,8 @@ export class Client {
1095
1131
  }
1096
1132
  else if (response) {
1097
1133
  handler.sendPacket(PacketType.Init1, response, 0);
1134
+ if (trace !== undefined)
1135
+ trace.init1Step = step === 0x7f ? -1 : step;
1098
1136
  }
1099
1137
  return;
1100
1138
  }
@@ -1433,6 +1471,9 @@ export class Client {
1433
1471
  case "clientChatComposing":
1434
1472
  this.#emitServerEvent({ name: "clientChatComposing", payload: result.event });
1435
1473
  break;
1474
+ case "clientChatClosed":
1475
+ this.#emitServerEvent({ name: "clientChatClosed", payload: result.event });
1476
+ break;
1436
1477
  case "poked":
1437
1478
  this.#emitServerEvent({ name: "poked", payload: result.event });
1438
1479
  break;