@honeybbq/teamspeak-client 0.3.1 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +322 -60
  2. package/dist/abort.d.ts +5 -0
  3. package/dist/abort.d.ts.map +1 -0
  4. package/dist/address.d.ts +8 -0
  5. package/dist/address.d.ts.map +1 -1
  6. package/dist/api/voice.d.ts +24 -0
  7. package/dist/api/voice.d.ts.map +1 -0
  8. package/dist/capabilities.d.ts +62 -0
  9. package/dist/capabilities.d.ts.map +1 -0
  10. package/dist/client.d.ts +172 -39
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/commands.d.ts +45 -27
  13. package/dist/commands.d.ts.map +1 -1
  14. package/dist/crypto/crypt.d.ts +19 -4
  15. package/dist/crypto/crypt.d.ts.map +1 -1
  16. package/dist/crypto/identity.d.ts +87 -8
  17. package/dist/crypto/identity.d.ts.map +1 -1
  18. package/dist/crypto/index.d.ts +2 -1
  19. package/dist/crypto/index.d.ts.map +1 -1
  20. package/dist/diagnostics.d.ts +45 -0
  21. package/dist/diagnostics.d.ts.map +1 -0
  22. package/dist/discovery/resolver.d.ts +21 -3
  23. package/dist/discovery/resolver.d.ts.map +1 -1
  24. package/dist/errors.d.ts +70 -16
  25. package/dist/errors.d.ts.map +1 -1
  26. package/dist/events.d.ts +4 -5
  27. package/dist/events.d.ts.map +1 -1
  28. package/dist/handshake/crypt-handshake.d.ts +1 -0
  29. package/dist/handshake/crypt-handshake.d.ts.map +1 -1
  30. package/dist/handshake/crypt-init2.d.ts.map +1 -1
  31. package/dist/handshake/license.d.ts +7 -0
  32. package/dist/handshake/license.d.ts.map +1 -1
  33. package/dist/handshake.d.ts +14 -5
  34. package/dist/handshake.d.ts.map +1 -1
  35. package/dist/helpers.d.ts +0 -8
  36. package/dist/helpers.d.ts.map +1 -1
  37. package/dist/index.d.ts +20 -6
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +3967 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/notifications.d.ts +7 -15
  42. package/dist/notifications.d.ts.map +1 -1
  43. package/dist/reconnect.d.ts +69 -0
  44. package/dist/reconnect.d.ts.map +1 -0
  45. package/dist/throttle.d.ts +9 -0
  46. package/dist/throttle.d.ts.map +1 -1
  47. package/dist/transfer.d.ts +27 -25
  48. package/dist/transfer.d.ts.map +1 -1
  49. package/dist/transport/handler.d.ts +37 -5
  50. package/dist/transport/handler.d.ts.map +1 -1
  51. package/dist/transport/quicklz.d.ts +19 -0
  52. package/dist/transport/quicklz.d.ts.map +1 -1
  53. package/dist/types.d.ts +277 -97
  54. package/dist/types.d.ts.map +1 -1
  55. package/package.json +21 -56
  56. package/src/abort.ts +24 -0
  57. package/src/address.ts +74 -0
  58. package/src/api/voice.ts +61 -0
  59. package/src/capabilities.ts +92 -0
  60. package/src/client.ts +1392 -0
  61. package/src/commands.ts +193 -0
  62. package/src/crypto/crypt-ops.ts +6 -0
  63. package/src/crypto/crypt.ts +274 -0
  64. package/src/crypto/eax.ts +152 -0
  65. package/src/crypto/identity.ts +484 -0
  66. package/src/crypto/index.ts +21 -0
  67. package/src/crypto/primitives.ts +141 -0
  68. package/src/diagnostics.ts +58 -0
  69. package/src/discovery/index.ts +1 -0
  70. package/src/discovery/resolver.ts +272 -0
  71. package/src/errors.ts +156 -0
  72. package/src/events.ts +28 -0
  73. package/src/handshake/crypt-handshake.ts +135 -0
  74. package/src/handshake/crypt-init2.ts +48 -0
  75. package/src/handshake/index.ts +3 -0
  76. package/src/handshake/license.test.ts +49 -0
  77. package/src/handshake/license.ts +208 -0
  78. package/src/handshake.ts +136 -0
  79. package/src/helpers.ts +10 -0
  80. package/src/index.ts +74 -0
  81. package/src/notifications.ts +136 -0
  82. package/src/reconnect.ts +141 -0
  83. package/src/throttle.ts +92 -0
  84. package/src/transfer.ts +127 -0
  85. package/src/transport/generation-window.ts +77 -0
  86. package/src/transport/handler.ts +914 -0
  87. package/src/transport/index.ts +14 -0
  88. package/src/transport/packet.ts +69 -0
  89. package/src/transport/quicklz.ts +209 -0
  90. package/src/types.ts +349 -0
  91. package/dist/address-BR-5unVo.cjs +0 -2
  92. package/dist/address-BR-5unVo.cjs.map +0 -1
  93. package/dist/address-BfmovVpp.js +0 -65
  94. package/dist/address-BfmovVpp.js.map +0 -1
  95. package/dist/api.d.ts +0 -17
  96. package/dist/api.d.ts.map +0 -1
  97. package/dist/command/command.d.ts +0 -12
  98. package/dist/command/command.d.ts.map +0 -1
  99. package/dist/command/index.cjs +0 -1
  100. package/dist/command/index.d.ts +0 -4
  101. package/dist/command/index.d.ts.map +0 -1
  102. package/dist/command/index.mjs +0 -3
  103. package/dist/command/parser.d.ts +0 -3
  104. package/dist/command/parser.d.ts.map +0 -1
  105. package/dist/command-BLe-C2L2.js +0 -74
  106. package/dist/command-BLe-C2L2.js.map +0 -1
  107. package/dist/command-DAK4pUBP.cjs +0 -4
  108. package/dist/command-DAK4pUBP.cjs.map +0 -1
  109. package/dist/crypt-handshake-CQkQreLx.cjs +0 -2
  110. package/dist/crypt-handshake-CQkQreLx.cjs.map +0 -1
  111. package/dist/crypt-handshake-DziOY4Lq.js +0 -55
  112. package/dist/crypt-handshake-DziOY4Lq.js.map +0 -1
  113. package/dist/crypt-init2-D70ha4LB.js +0 -158
  114. package/dist/crypt-init2-D70ha4LB.js.map +0 -1
  115. package/dist/crypt-init2-DparLekm.cjs +0 -2
  116. package/dist/crypt-init2-DparLekm.cjs.map +0 -1
  117. package/dist/crypto/index.cjs +0 -1
  118. package/dist/crypto/index.mjs +0 -3
  119. package/dist/crypto-Bg02m4ZE.js +0 -165
  120. package/dist/crypto-Bg02m4ZE.js.map +0 -1
  121. package/dist/crypto-CDlweUL3.cjs +0 -2
  122. package/dist/crypto-CDlweUL3.cjs.map +0 -1
  123. package/dist/discovery/index.cjs +0 -1
  124. package/dist/discovery/index.mjs +0 -2
  125. package/dist/handler-DI_abe9c.js +0 -483
  126. package/dist/handler-DI_abe9c.js.map +0 -1
  127. package/dist/handler-DuwoO5RC.cjs +0 -2
  128. package/dist/handler-DuwoO5RC.cjs.map +0 -1
  129. package/dist/handshake/index.cjs +0 -1
  130. package/dist/handshake/index.mjs +0 -3
  131. package/dist/index.cjs +0 -4
  132. package/dist/index.cjs.map +0 -1
  133. package/dist/index.mjs +0 -827
  134. package/dist/index.mjs.map +0 -1
  135. package/dist/parser-CL33R9tr.cjs +0 -2
  136. package/dist/parser-CL33R9tr.cjs.map +0 -1
  137. package/dist/parser-DZMv3Jm-.js +0 -33
  138. package/dist/parser-DZMv3Jm-.js.map +0 -1
  139. package/dist/primitives-cNNAeFCo.cjs +0 -2
  140. package/dist/primitives-cNNAeFCo.cjs.map +0 -1
  141. package/dist/primitives-xIr6Tjk4.js +0 -1457
  142. package/dist/primitives-xIr6Tjk4.js.map +0 -1
  143. package/dist/resolver-6B8dI9gr.js +0 -141
  144. package/dist/resolver-6B8dI9gr.js.map +0 -1
  145. package/dist/resolver-DCcmyWSK.cjs +0 -4
  146. package/dist/resolver-DCcmyWSK.cjs.map +0 -1
  147. package/dist/transport/index.cjs +0 -1
  148. package/dist/transport/index.mjs +0 -2
package/dist/client.d.ts CHANGED
@@ -1,47 +1,180 @@
1
- import { type EventMap, type ClientOptions, type CommandMiddleware, type EventMiddleware, type Logger, ClientStatus } from "./types.js";
2
- import { Identity, Crypt } from "./crypto/index.js";
3
- import { PacketHandler } from "./transport/handler.js";
4
- import type { FileUploadInfo, FileDownloadInfo } from "./types.js";
1
+ import { type EventMap, type ClientOptions, type Listener, type Unsubscribe, ClientStatus } from "./types.js";
2
+ import { type ChannelID, type ClientID, type Codec, type CommandOptions, type UID } from "@honeybbq/teamspeak-core";
3
+ import { Identity } from "./crypto/index.js";
4
+ import type { CommandExecutor, CommandRequest } from "@honeybbq/teamspeak-core";
5
+ import type { FileUploadInfo, FileDownloadInfo } from "@honeybbq/teamspeak-core";
5
6
  import type { Readable, Writable } from "node:stream";
7
+ import { type ServerCapabilities } from "./capabilities.js";
6
8
  export { ClientStatus };
7
- export interface ClientState {
8
- status: ClientStatus;
9
- clid: number;
9
+ /**
10
+ * What clientinit sends about the connection, with the passwords already in
11
+ * their wire form, `base64(sha1(password))`: hashed once, when the client is
12
+ * made, so the plain text is never held.
13
+ */
14
+ export interface ClientInitOptions {
15
+ readonly serverPasswordHash: string;
16
+ readonly defaultChannel: string;
17
+ readonly defaultChannelPasswordHash: string;
10
18
  }
11
- export declare class Client {
19
+ /**
20
+ * A TeamSpeak client over the voice protocol.
21
+ *
22
+ * Listeners run one at a time, in the order their events arrived, after the
23
+ * packet that carried them has been handled, so a listener may call the client
24
+ * and await an answer.
25
+ */
26
+ export declare class Client implements CommandExecutor, AsyncDisposable {
12
27
  #private;
13
- /** @internal */ crypt: Crypt;
14
- /** @internal */ handler: PacketHandler;
15
- /** @internal */ logger: Logger;
16
- /** @internal */ nickname: string;
17
- /** @internal */ clid: number;
18
28
  constructor(identity: Identity, addr: string, nickname: string, options?: ClientOptions);
29
+ /** Where the client is in its connection's life. */
19
30
  get status(): ClientStatus;
20
- /** @internal */
21
- getClientInitOptions(): Readonly<{
22
- serverPassword: string;
23
- defaultChannel: string;
24
- defaultChannelPassword: string;
25
- }>;
26
- connect(): Promise<void>;
27
- disconnect(): Promise<void>;
28
- waitConnected(signal?: AbortSignal): Promise<void>;
29
- sendCommandNoWait(cmd: string): Promise<void>;
30
- execCommand(cmd: string, timeoutMs?: number): Promise<void>;
31
- execCommandWithResponse(cmd: string, timeoutMs?: number): Promise<Record<string, string>[]>;
32
- on<K extends keyof EventMap>(event: K, handler: EventMap[K] extends void ? () => void : (payload: EventMap[K]) => void): this;
33
- useCommandMiddleware(...mw: CommandMiddleware[]): this;
34
- useEventMiddleware(...mw: EventMiddleware[]): this;
35
- clientID(): number;
36
- channelID(): bigint;
37
- /** Send Opus voice directly to specific clients, bypassing channel routing. */
38
- sendWhisper(data: Uint8Array, targetClientIds: number[], codec: number): void;
39
- sendVoice(data: Uint8Array, codec: number): void;
40
- fileTransferInitUpload(channelID: bigint, path: string, password: string, size: bigint, overwrite?: boolean): Promise<FileUploadInfo>;
41
- fileTransferInitDownload(channelID: bigint, path: string, password: string): Promise<FileDownloadInfo>;
42
- uploadFileData(host: string, info: FileUploadInfo, data: Readable): Promise<void>;
43
- downloadFileData(host: string, info: FileDownloadInfo, dest: Writable): Promise<void>;
44
- /** @internal */
45
- _markConnected(): void;
31
+ /**
32
+ * Connect to the server. Resolves once the handshake completes.
33
+ *
34
+ * An address that cannot be one — no host, a port outside 1–65535 — rejects
35
+ * with an InvalidAddressError at once, before anything starts. A host name
36
+ * that does not resolve fails the attempt with a TeamspeakError whose
37
+ * `cause` is the lookup's error.
38
+ *
39
+ * A failed attempt rejects with its cause, such as the server's error for a
40
+ * rejected password. Aborting `signal` abandons the attempt and rejects with
41
+ * the signal's reason. The signal bounds only the connecting: once this
42
+ * resolves, the connection lasts until `disconnect()` or a drop.
43
+ * `connect()` does not retry; the `reconnect` option only retries a
44
+ * connection that drops after it was established.
45
+ *
46
+ * Called while a `disconnect()` is still saying goodbye, or while an earlier
47
+ * session's `disconnected` is still waiting to be delivered, this waits for
48
+ * that first, so the old session is closed, and its `disconnected`
49
+ * delivered, before the new one starts: no listener or `events()` iterator
50
+ * of the new session hears the old one end. A `disconnect()` called during
51
+ * that wait wins: this then rejects with a NotConnectedError.
52
+ *
53
+ * `connected` is delivered after this resolves, so a listener added right
54
+ * after `await client.connect()` still hears it.
55
+ */
56
+ connect(options?: {
57
+ readonly signal?: AbortSignal | undefined;
58
+ }): Promise<void>;
59
+ /**
60
+ * Leave the server and close the connection.
61
+ *
62
+ * Says goodbye first, waiting for the server's acknowledgement for a second
63
+ * at most, or less if `signal` aborts sooner. The connection is closed
64
+ * either way. A `connect()` still waiting for its handshake rejects with a
65
+ * NotConnectedError, and so do the commands still waiting for an answer.
66
+ * Resolves once `disconnected` has been delivered to the listeners, so one
67
+ * added after `await client.disconnect()` does not hear it. Does nothing on
68
+ * a client that is not connected; called again while an earlier call is
69
+ * still saying goodbye, it waits for that one.
70
+ */
71
+ disconnect(options?: {
72
+ readonly signal?: AbortSignal | undefined;
73
+ }): Promise<void>;
74
+ /** `await using client = …` disconnects when the scope ends. */
75
+ [Symbol.asyncDispose](): Promise<void>;
76
+ /**
77
+ * Send a raw command line and return the rows that answer it: the escape
78
+ * hatch for a command the typed functions do not cover. The line is sent as
79
+ * given, so its values must already be escaped and any channel password
80
+ * already hashed.
81
+ *
82
+ * Rejects with a NotConnectedError unless the client is connected: while it
83
+ * is still connecting, the session's keys may not exist yet.
84
+ */
85
+ exec(command: string, options?: CommandOptions): Promise<Record<string, string>[]>;
86
+ /**
87
+ * Send a raw command line without waiting for an answer, for the few
88
+ * commands the server answers with nothing at all.
89
+ *
90
+ * Commands are paced by a flood throttle; aborting `signal` while this waits
91
+ * for its turn rejects with the signal's reason and sends nothing. Rejects
92
+ * with a NotConnectedError unless the client is connected.
93
+ */
94
+ execNoWait(command: string, options?: {
95
+ readonly signal?: AbortSignal | undefined;
96
+ }): Promise<void>;
97
+ /**
98
+ * Send a typed command; this is the CommandExecutor the typed commands run on.
99
+ *
100
+ * The request passes through the `commandMiddleware` first. Channel
101
+ * passwords are then hashed, because that is this protocol's rule: the voice
102
+ * protocol expects `base64(sha1(password))`, where ServerQuery takes the
103
+ * password itself.
104
+ */
105
+ send(request: CommandRequest, options?: CommandOptions): Promise<Record<string, string>[]>;
106
+ /**
107
+ * Listen for an event. Returns a function that removes the listener.
108
+ *
109
+ * A listener that throws does not stop the others: the rest of the event's
110
+ * listeners, and the events after it, are still delivered, and the exception
111
+ * is rethrown on its own, from a microtask, so it surfaces as an
112
+ * `uncaughtException` just as an EventEmitter listener's would. An async
113
+ * listener's rejection is not caught, and surfaces as an unhandled
114
+ * rejection.
115
+ */
116
+ on<K extends keyof EventMap>(event: K, listener: Listener<K>): Unsubscribe;
117
+ /** Listen for the next occurrence of an event only. */
118
+ once<K extends keyof EventMap>(event: K, listener: Listener<K>): Unsubscribe;
119
+ /**
120
+ * An event as an async iterator, for `for await`:
121
+ *
122
+ * ```ts
123
+ * for await (const message of client.events("textMessage", { signal })) { … }
124
+ * ```
125
+ *
126
+ * Events that arrive while the loop body runs are buffered, so none is
127
+ * missed — except voice, of which at most 256 frames are buffered; the rest
128
+ * are dropped, as for a listener that falls behind. Iteration ends when
129
+ * `signal` aborts, when the loop exits early (`break`, `return`, a throw),
130
+ * and — for every event but `disconnected` — when the client disconnects.
131
+ * An abort stops listening at once and discards whatever is buffered.
132
+ */
133
+ events<K extends keyof EventMap>(event: K, options?: {
134
+ readonly signal?: AbortSignal | undefined;
135
+ }): AsyncIterableIterator<EventMap[K]>;
136
+ /** This client's id on the server, assigned in the handshake; 0 before it. */
137
+ get clientID(): ClientID;
138
+ /**
139
+ * What the connected server is: version, generation, and the uid hash it
140
+ * uses. Read from `initserver`; before connecting the family is `unknown`
141
+ * and the uid hash SHA-1.
142
+ */
143
+ get serverCapabilities(): ServerCapabilities;
144
+ /**
145
+ * Our own `client_unique_identifier`, hashed with the algorithm the connected
146
+ * server uses.
147
+ *
148
+ * TS3 uses SHA-1 and TS6 SHA-256, so this reads the algorithm learned during
149
+ * initserver rather than assuming one. Comparing a hard-coded SHA-1 UID to a
150
+ * TS6 server silently never matches.
151
+ */
152
+ get uid(): UID;
153
+ /** The channel this client is in, as it last learned; 0 before connecting. */
154
+ get channelID(): ChannelID;
155
+ /**
156
+ * Send Opus voice directly to specific clients, bypassing channel routing.
157
+ * Throws a NotConnectedError unless the client is connected.
158
+ */
159
+ sendWhisper(data: Uint8Array, targetClientIDs: readonly ClientID[], codec: Codec): void;
160
+ /**
161
+ * Send one encoded audio frame to the channel this client is in. Throws a
162
+ * NotConnectedError unless the client is connected.
163
+ */
164
+ sendVoice(data: Uint8Array, codec: Codec): void;
165
+ /**
166
+ * Upload `data` over a negotiated transfer, through the client's
167
+ * `fileTransferSocketFactory` if it has one.
168
+ */
169
+ uploadFileData(host: string, info: FileUploadInfo, data: Readable, options?: {
170
+ readonly signal?: AbortSignal | undefined;
171
+ }): Promise<void>;
172
+ /**
173
+ * Download a negotiated transfer into `dest`, through the client's
174
+ * `fileTransferSocketFactory` if it has one.
175
+ */
176
+ downloadFileData(host: string, info: FileDownloadInfo, dest: Writable, options?: {
177
+ readonly signal?: AbortSignal | undefined;
178
+ }): Promise<void>;
46
179
  }
47
180
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -1,48 +1,66 @@
1
+ import type { ResponseSpec } from "@honeybbq/teamspeak-core/internal";
1
2
  export interface CommandResult {
2
3
  err: Error | null;
3
4
  data: Record<string, string>[];
4
5
  }
5
6
  /**
6
- * Tracks in-flight commands by return_code.
7
+ * Matches the server's answers to the commands that asked for them.
7
8
  *
8
- * The TS3/TS5 server sends a "welcome sequence" of unsolicited data immediately
9
- * after the connection handshake (channellist, channelclientlist, etc.). This
10
- * data arrives on the event loop AFTER we may have registered our first pending
11
- * RC, which would contaminate our command responses.
9
+ * The server handles one client's commands in order and closes each answer
10
+ * with `error … return_code=N`, so every row seen since the previous `error`
11
+ * belongs to command N. Rows are therefore held, tagged with where they came
12
+ * from, until that `error` says whose they are — attributing them on arrival
13
+ * would mean guessing which in-flight command the server is answering, and a
14
+ * command can be registered here well before it is sent (it first waits on the
15
+ * throttle and the middleware chain).
12
16
  *
13
- * Solution: gate all row buffering on a `#welcomeComplete` flag. The flag is
14
- * set when `notifycliententerview` for our own clid arrives — the last event
15
- * the TS3/TS5 server sends in its welcome sequence. Any data arriving before
16
- * that is silently discarded.
17
+ * A nameless row always belongs to the command. A notification row belongs to
18
+ * it only if the command declared that notification as its answer (see
19
+ * command/responses.ts): the same `notifyclientmoved` that answers nothing can
20
+ * arrive between any two commands.
21
+ *
22
+ * The welcome sequence is the exception. The server pushes channellist,
23
+ * channelgrouplist and the rest unprompted right after the handshake, before
24
+ * `channellistfinished`; without the gate they would land in the first
25
+ * command's answer as duplicate rows and a phantom cid=0.
17
26
  */
18
27
  export declare class CommandTracker {
19
28
  #private;
20
- register(): [rc: number, promise: Promise<CommandResult>];
29
+ /** @param ownClientID the client's clid, for answers filtered by invoker. */
30
+ constructor(ownClientID?: () => number);
31
+ register(spec?: ResponseSpec, correlation?: string): [rc: number, promise: Promise<CommandResult>];
21
32
  unregister(rc: number): void;
22
- /**
23
- * Called when `notifycliententerview` for our own clid arrives.
24
- * Marks the welcome sequence as complete and discards any accumulated data.
25
- */
33
+ /** Marks the end of the welcome sequence and drops what it left behind. */
26
34
  signalWelcomeComplete(): void;
35
+ /** Whether some in-flight command declared `name` as its answer. */
36
+ expects(name: string): boolean;
37
+ /** Hold a row until the next `error` says which command it answered. */
38
+ buffer(params: Record<string, string>, source?: string): void;
27
39
  /**
28
- * Buffer a data row from the server. Rows arriving before the welcome
29
- * sequence is complete are silently discarded to prevent contamination.
40
+ * Offer a notification to commands answered after their `error`.
41
+ *
42
+ * Matching is on the echoed request field, so it does not matter whether the
43
+ * notification beats the `error` or follows it.
30
44
  */
31
- buffer(params: Record<string, string>): void;
45
+ offerCorrelated(name: string, params: Record<string, string>): void;
32
46
  resolve(rc: number, err: Error | null): void;
33
47
  discardBuffer(): void;
34
- reset(): void;
48
+ /**
49
+ * Forget every command and fail it with `err`.
50
+ *
51
+ * Dropping them silently would leave each caller waiting out its full
52
+ * timeout for an answer that can no longer come.
53
+ */
54
+ reset(err: Error): void;
35
55
  }
36
56
  /**
37
- * Parse and handle an `error` command from the server.
38
- * Returns the error (or null on success) and the resolved return_code.
39
- */
40
- export declare function parseServerError(params: Record<string, string>): {
41
- err: Error | null;
42
- rc: number | null;
43
- };
44
- /**
45
- * Append a return_code parameter to a command string if not already present.
57
+ * Append the tracker's return_code to a command.
58
+ *
59
+ * The return_code belongs to the tracker: it is how an answer finds its
60
+ * command, so a caller-supplied one would detach the command from its answer.
61
+ * Checked as a parameter rather than as text, because a message body that
62
+ * merely contains "return_code=" used to suppress the real one and time the
63
+ * command out.
46
64
  */
47
65
  export declare function appendReturnCode(cmd: string, rc: number): string;
48
66
  //# sourceMappingURL=commands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;CAChC;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;;IAWzB,QAAQ,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAOxD;IAED,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3B;IAED;;;OAGG;IACH,qBAAqB,IAAI,IAAI,CAG5B;IAED;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAI3C;IAED,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAU3C;IAED,aAAa,IAAI,IAAI,CAEpB;IAED,KAAK,IAAI,IAAI,CAKZ;CACF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG;IAChE,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAClB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB,CAiBA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAGhE"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;CAChC;AAoBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,cAAc;;IAOzB,6EAA6E;IAC7E,YAAY,WAAW,GAAE,MAAM,MAAgB,EAE9C;IAED,QAAQ,CACN,IAAI,CAAC,EAAE,YAAY,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAO/C;IAED,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3B;IAED,2EAA2E;IAC3E,qBAAqB,IAAI,IAAI,CAG5B;IAED,oEAAoE;IACpE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAM7B;IAED,wEAAwE;IACxE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,SAAK,GAAG,IAAI,CAIxD;IAED;;;;;OAKG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAiBlE;IAED,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAkB3C;IAED,aAAa,IAAI,IAAI,CAEpB;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAOtB;CAaF;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAMhE"}
@@ -1,17 +1,32 @@
1
1
  import type { Identity } from "./identity.js";
2
+ /**
3
+ * The most squarings the Init1 puzzle may ask for. TS3 3.13 and TS6 6.0 both
4
+ * ask for 10000; a level far beyond that is a server trying to pin the event
5
+ * loop, since the squarings run synchronously.
6
+ */
7
+ export declare const MAX_RSA_CHALLENGE_LEVEL: number;
2
8
  export interface KeyNonce {
3
9
  key: Uint8Array;
4
10
  nonce: Uint8Array;
5
11
  gen: number;
6
12
  }
13
+ /**
14
+ * A connection's encryption state. The session material — the IV the keys
15
+ * derive from — lives in private fields, so it shows in no `util.inspect()`
16
+ * or `JSON.stringify()` of anything holding a Crypt.
17
+ */
7
18
  export declare class Crypt {
8
19
  #private;
9
20
  readonly identity: Identity;
10
- ivStruct: Uint8Array;
11
- fakeSignature: Uint8Array;
12
- alphaTmp: Uint8Array;
13
- cryptoInitComplete: boolean;
14
21
  constructor(identity: Identity);
22
+ /** Whether the session keys are set up; until then packets use the dummy key. */
23
+ get cryptoInitComplete(): boolean;
24
+ /** The signature unencrypted packets carry: zeros until crypto init completes. */
25
+ get fakeSignature(): Uint8Array;
26
+ /** The client's random contribution to the IV, sent in clientinitiv; empty before. */
27
+ get alpha(): Uint8Array;
28
+ /** Choose the client's random contribution to the IV, and return it. */
29
+ newAlpha(): Uint8Array;
15
30
  solveRsaChallenge(data: Uint8Array, offset: number, level: number): Uint8Array;
16
31
  initCrypto(alpha: string, beta: string, omega: string): void;
17
32
  setSharedSecret(alpha: Uint8Array, beta: Uint8Array, sharedKey: Uint8Array): void;
@@ -1 +1 @@
1
- {"version":3,"file":"crypt.d.ts","sourceRoot":"","sources":["../../src/crypto/crypt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAW9C,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAMD,qBAAa,KAAK;;IAChB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,QAAQ,EAAE,UAAU,CAAqB;IACzC,aAAa,EAAE,UAAU,CAAuC;IAChE,QAAQ,EAAE,UAAU,CAAqB;IACzC,kBAAkB,UAAS;IAI3B,YAAY,QAAQ,EAAE,QAAQ,EAE7B;IAED,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAgB7E;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAS3D;IAED,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,IAAI,CAiBhF;IAED,WAAW,CACT,UAAU,EAAE,OAAO,EACnB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,GACb,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,CAAC,CA6BtC;IAED,OAAO,CACL,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,OAAO,GACnB,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,CAAC,CAW3C;IAED,OAAO,CACL,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,OAAO,GACnB,UAAU,CAeZ;CAyBF"}
1
+ {"version":3,"file":"crypt.d.ts","sourceRoot":"","sources":["../../src/crypto/crypt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAW9C;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAgB,CAAC;AAEvD,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAMD;;;;GAIG;AACH,qBAAa,KAAK;;IAChB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAS5B,YAAY,QAAQ,EAAE,QAAQ,EAE7B;IAED,iFAAiF;IACjF,IAAI,kBAAkB,IAAI,OAAO,CAEhC;IAED,kFAAkF;IAClF,IAAI,aAAa,IAAI,UAAU,CAE9B;IAED,sFAAsF;IACtF,IAAI,KAAK,IAAI,UAAU,CAEtB;IAED,wEAAwE;IACxE,QAAQ,IAAI,UAAU,CAGrB;IAED,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAiB7E;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAa3D;IAED,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,IAAI,CAqBhF;IAED,WAAW,CACT,UAAU,EAAE,OAAO,EACnB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,GACb,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,CAAC,CAkCtC;IAED,OAAO,CACL,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,OAAO,GACnB,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,CAAC,CAW3C;IAED,OAAO,CACL,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,OAAO,GACnB,UAAU,CAeZ;CAyBF"}
@@ -1,16 +1,95 @@
1
1
  import { type KeyObject } from "node:crypto";
2
+ import { type UID } from "@honeybbq/teamspeak-core";
3
+ /**
4
+ * A TeamSpeak client identity: a P-256 key pair plus a counter, the offset,
5
+ * whose hash with the public key sets the identity's security level.
6
+ *
7
+ * Immutable: raising the level returns a new identity. Opaque: the private
8
+ * key is never exposed, and `toString()`, `toJSON()` and `util.inspect()`
9
+ * describe the identity without it, so an identity that reaches a log or an
10
+ * error message does not leak; `export()` is the one way to get the secret
11
+ * form, for storing it. Make one with `generateIdentity()` or
12
+ * `parseIdentity()`.
13
+ */
2
14
  export declare class Identity {
3
- readonly privateKey: KeyObject;
4
- offset: bigint;
5
- constructor(privateKey: KeyObject, offset: bigint);
6
- publicKeyBase64(): string;
15
+ #private;
16
+ private constructor();
17
+ /** The counter whose hash with the public key sets the security level. */
18
+ get offset(): bigint;
19
+ /** The public key in TeamSpeak's encoding: base64 of its DER form. */
20
+ publicKey(): string;
21
+ /**
22
+ * The identity's unique identifier, hashed with `algorithm`: SHA-1 for a
23
+ * TeamSpeak 3 server, SHA-256 for TeamSpeak 6. A connected client's `uid`
24
+ * already uses the one its server does.
25
+ */
26
+ uid(algorithm: UIDAlgorithm): UID;
27
+ /**
28
+ * The identity in the form `parseIdentity()` reads, private key included.
29
+ * Store it as you would a password.
30
+ */
31
+ export(): string;
32
+ /** Describes the identity, by its SHA-1 unique identifier, without its private key. */
7
33
  toString(): string;
34
+ /** The same as `toString()`, so serialising an identity cannot leak its key. */
35
+ toJSON(): string;
36
+ /** The security level: trailing zero bits in SHA-1 of the public key and offset. */
8
37
  securityLevel(): number;
9
- upgradeToLevel(targetLevel: number, signal?: AbortSignal): Promise<void>;
38
+ /**
39
+ * The same key at an offset whose security level reaches `targetLevel`, as
40
+ * a new identity; this one is unchanged, and returned as it is when it
41
+ * already reaches the level.
42
+ *
43
+ * Each level doubles the expected work, so a high level can take a long
44
+ * time; `signal` bounds it, and the event loop keeps running meanwhile. A
45
+ * level above 160, the width of SHA-1, can never be reached and is refused
46
+ * with a RangeError.
47
+ */
48
+ upgradeToLevel(targetLevel: number, options?: {
49
+ readonly signal?: AbortSignal | undefined;
50
+ }): Promise<Identity>;
10
51
  }
11
- export declare function identityFromString(s: string): Identity;
12
- export declare function generateIdentity(targetLevel: number): Identity;
13
- export declare function getUidFromPublicKey(publicKey: string): string;
52
+ /**
53
+ * Read an identity from the form `Identity.export()` writes: the private key
54
+ * as canonical base64 of at most 32 bytes, a colon, and the offset as a
55
+ * decimal unsigned 64-bit integer. Anything else throws an
56
+ * InvalidIdentityError, with the underlying failure as its cause.
57
+ */
58
+ export declare function parseIdentity(s: string): Identity;
59
+ /**
60
+ * Create an identity at the given security level, at most 160. Each level
61
+ * doubles the expected work; `signal` bounds it, and the event loop keeps
62
+ * running meanwhile.
63
+ */
64
+ export declare function generateIdentity(targetLevel: number, options?: {
65
+ readonly signal?: AbortSignal | undefined;
66
+ }): Promise<Identity>;
67
+ /**
68
+ * Create an identity at the given security level, at most 160, blocking until
69
+ * done. Fine for a low level; a high one can block the event loop for minutes,
70
+ * so prefer `generateIdentity()` there.
71
+ */
72
+ export declare function generateIdentitySync(targetLevel: number): Identity;
73
+ /**
74
+ * The hash a server uses for `client_unique_identifier`, keyed by the server's
75
+ * major version.
76
+ *
77
+ * TeamSpeak 3 hashes the canonical public key with SHA-1 (20 bytes); TeamSpeak
78
+ * 6 switched to SHA-256 (32 bytes). The algorithm is not negotiable on the
79
+ * wire, so a client that guesses wrong computes a UID that matches nothing the
80
+ * server ever sends — self-identification by UID silently fails.
81
+ *
82
+ * May gain members in a minor release, should a server generation change the
83
+ * hash again: a `switch` over it needs a default case.
84
+ */
85
+ export type UIDAlgorithm = "sha1" | "sha256";
86
+ /** Pick the UID hash from a `virtualserver_version` string, e.g. "6.0.0-beta13.1". */
87
+ export declare function uidAlgorithmForVersion(version: string): UIDAlgorithm;
88
+ /**
89
+ * The unique identifier of a public key in TeamSpeak's encoding, hashed with
90
+ * `algorithm`: SHA-1 for a TeamSpeak 3 server, SHA-256 for TeamSpeak 6.
91
+ */
92
+ export declare function uidFromPublicKey(publicKey: string, algorithm: UIDAlgorithm): UID;
14
93
  export declare function hash512(data: Uint8Array): Uint8Array;
15
94
  /** Import a TS3 public key (canonical or legacy ASN.1 DER) and return the
16
95
  * uncompressed 65-byte point [0x04 || x || y]. */
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/crypto/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AAOrB,qBAAa,QAAQ;IACnB,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAGhD;IAED,eAAe,IAAI,MAAM,CAiBxB;IAED,QAAQ,IAAI,MAAM,CAQjB;IAED,aAAa,IAAI,MAAM,CAKtB;IAEK,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAc7E;CACF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CA+BtD;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CAc9D;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAEpD;AA+BD;mDACmD;AACnD,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,CAS3D"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/crypto/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAS,KAAK,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAY3D;;;;;;;;;;GAUG;AACH,qBAAa,QAAQ;;IAUnB,OAAO,eAGN;IAED,0EAA0E;IAC1E,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,sEAAsE;IACtE,SAAS,IAAI,MAAM,CAUlB;IAED;;;;OAIG;IACH,GAAG,CAAC,SAAS,EAAE,YAAY,GAAG,GAAG,CAEhC;IAED;;;OAGG;IACH,MAAM,IAAI,MAAM,CAKf;IAED,uFAAuF;IACvF,QAAQ,IAAI,MAAM,CAEjB;IAED,gFAAgF;IAChF,MAAM,IAAI,MAAM,CAEf;IAED,oFAAoF;IACpF,aAAa,IAAI,MAAM,CAEtB;IAED;;;;;;;;;OASG;IACG,cAAc,CAClB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;KAAO,GAC1D,OAAO,CAAC,QAAQ,CAAC,CAgBnB;CACF;AAqCD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CA0CjD;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAAO,GAC1D,OAAO,CAAC,QAAQ,CAAC,CAGnB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CAOlE;AAMD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7C,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAGpE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,GAAG,GAAG,CAEhF;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAEpD;AA+BD;mDACmD;AACnD,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,CAS3D"}
@@ -1,4 +1,5 @@
1
- export { Identity, identityFromString, generateIdentity, getUidFromPublicKey, hash512, importPublicKey, } from "./identity.js";
1
+ export { Identity, parseIdentity, generateIdentity, generateIdentitySync, uidFromPublicKey, uidAlgorithmForVersion, hash512, importPublicKey, } from "./identity.js";
2
+ export type { UIDAlgorithm } from "./identity.js";
2
3
  export { Crypt } from "./crypt.js";
3
4
  export type { KeyNonce } from "./crypt.js";
4
5
  export { EAX, aesCmac } from "./eax.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,OAAO,EACP,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,IAAI,EACJ,UAAU,EACV,gBAAgB,EAChB,WAAW,GACZ,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,OAAO,EACP,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,IAAI,EACJ,UAAU,EACV,gBAAgB,EAChB,WAAW,GACZ,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Diagnostics channels: the SDK's tracing points, for an application or an
3
+ * APM agent (OpenTelemetry instruments `diagnostics_channel` directly) to
4
+ * subscribe to.
5
+ *
6
+ * A library does not decide where its traces go, so the SDK publishes them
7
+ * here, and with no subscriber a channel costs a property read.
8
+ *
9
+ * Messages carry the command's name, never its parameters: those hold
10
+ * password hashes, which the server accepts as passwords, and privilege keys.
11
+ *
12
+ * ```ts
13
+ * import { tracingChannel } from "node:diagnostics_channel";
14
+ *
15
+ * tracingChannel("honeybbq:teamspeak:command").subscribe({
16
+ * start: ({ command }) => console.time(command),
17
+ * asyncEnd: ({ command }) => console.timeEnd(command),
18
+ * error: ({ command, error }) => console.error(command, error),
19
+ * });
20
+ * ```
21
+ */
22
+ import { type Channel, type TracingChannel } from "node:diagnostics_channel";
23
+ import type { Client } from "./client.js";
24
+ /** What `honeybbq:teamspeak:command` reports about a command. */
25
+ export interface CommandTrace {
26
+ /** The command's name, such as `clientlist`. */
27
+ readonly command: string;
28
+ /** The client sending it. Its fields are private, so it carries no secret. */
29
+ readonly client: Client;
30
+ }
31
+ /** What `honeybbq:teamspeak:packet` reports about a command arriving from the server. */
32
+ export interface PacketTrace {
33
+ /** The command's name, such as `notifytextmessage`, or `""` for a data row. */
34
+ readonly command: string;
35
+ /** The client receiving it. */
36
+ readonly client: Client;
37
+ }
38
+ /**
39
+ * Traces every command with an answer: `start` when it is sent, `asyncEnd`
40
+ * when its answer arrives, `error` when it fails.
41
+ */
42
+ export declare const commandChannel: TracingChannel<CommandTrace>;
43
+ /** Publishes every command the server sends. */
44
+ export declare const packetChannel: Channel<PacketTrace>;
45
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAGL,KAAK,OAAO,EACZ,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,yFAAyF;AACzF,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,YAAY,CAEvD,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,aAAa,EAAE,OAAO,CAAC,WAAW,CAE9C,CAAC"}
@@ -1,8 +1,26 @@
1
- import type { Logger } from "../types.js";
2
1
  import type { ResolvedAddr } from "../types.js";
2
+ /**
3
+ * The most a TSDNS server or the nickname service is read for. Either answers
4
+ * with one short line; without a bound, one that kept sending would be held in
5
+ * memory until the resolution budget ran out.
6
+ */
7
+ export declare const MAX_DISCOVERY_RESPONSE: number;
8
+ /** How the default resolver resolves. */
9
+ export interface ResolverOptions {
10
+ /** Look dot-less names up at named.myteamspeak.com; off by default. */
11
+ readonly resolveNicknames?: boolean | undefined;
12
+ }
3
13
  export declare class Resolver {
4
14
  #private;
5
- constructor(_log?: Logger);
6
- resolve(inputAddr: string, signal?: AbortSignal): Promise<ResolvedAddr[]>;
15
+ constructor(options?: ResolverOptions);
16
+ /**
17
+ * Resolve as the TeamSpeak application does: a server nickname (only with
18
+ * `resolveNicknames`), then SRV, then TSDNS, then the address as given. The
19
+ * whole resolution is bounded: past the budget, or when a step fails, the
20
+ * address as given is the answer.
21
+ */
22
+ resolve(inputAddr: string, options?: {
23
+ readonly signal?: AbortSignal | undefined;
24
+ }): Promise<ResolvedAddr[]>;
7
25
  }
8
26
  //# sourceMappingURL=resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/discovery/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQhD,qBAAa,QAAQ;;IAGnB,YAAY,IAAI,GAAE,MAAmB,EAAI;IAEnC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA+C9E;CAgEF"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/discovery/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiBhD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAiB,CAAC;AAEvD,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACjD;AAED,qBAAa,QAAQ;;IAInB,YAAY,OAAO,GAAE,eAAoB,EAExC;IAED;;;;;OAKG;IACG,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;KAAO,GAC1D,OAAO,CAAC,YAAY,EAAE,CAAC,CAYzB;CAgHF"}