@honeybbq/teamspeak 0.0.1 → 1.0.0-beta.7

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 (393) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +726 -2
  3. package/dist/client/abort.d.ts +5 -0
  4. package/dist/client/abort.d.ts.map +1 -0
  5. package/dist/client/abort.js +23 -0
  6. package/dist/client/abort.js.map +1 -0
  7. package/dist/client/address.d.ts +17 -0
  8. package/dist/client/address.d.ts.map +1 -0
  9. package/dist/client/address.js +66 -0
  10. package/dist/client/address.js.map +1 -0
  11. package/dist/client/api/voice.d.ts +24 -0
  12. package/dist/client/api/voice.d.ts.map +1 -0
  13. package/dist/client/api/voice.js +33 -0
  14. package/dist/client/api/voice.js.map +1 -0
  15. package/dist/client/capabilities.d.ts +68 -0
  16. package/dist/client/capabilities.d.ts.map +1 -0
  17. package/dist/client/capabilities.js +59 -0
  18. package/dist/client/capabilities.js.map +1 -0
  19. package/dist/client/client.d.ts +196 -0
  20. package/dist/client/client.d.ts.map +1 -0
  21. package/dist/client/client.js +1523 -0
  22. package/dist/client/client.js.map +1 -0
  23. package/dist/client/commands.d.ts +66 -0
  24. package/dist/client/commands.d.ts.map +1 -0
  25. package/dist/client/commands.js +162 -0
  26. package/dist/client/commands.js.map +1 -0
  27. package/dist/client/crypto/crypt-ops.d.ts +7 -0
  28. package/dist/client/crypto/crypt-ops.d.ts.map +1 -0
  29. package/dist/client/crypto/crypt-ops.js +2 -0
  30. package/dist/client/crypto/crypt-ops.js.map +1 -0
  31. package/dist/client/crypto/crypt.d.ts +37 -0
  32. package/dist/client/crypto/crypt.d.ts.map +1 -0
  33. package/dist/client/crypto/crypt.js +203 -0
  34. package/dist/client/crypto/crypt.js.map +1 -0
  35. package/dist/client/crypto/eax.d.ts +12 -0
  36. package/dist/client/crypto/eax.d.ts.map +1 -0
  37. package/dist/client/crypto/eax.js +118 -0
  38. package/dist/client/crypto/eax.js.map +1 -0
  39. package/dist/client/crypto/identity.d.ts +97 -0
  40. package/dist/client/crypto/identity.d.ts.map +1 -0
  41. package/dist/client/crypto/identity.js +412 -0
  42. package/dist/client/crypto/identity.js.map +1 -0
  43. package/dist/client/crypto/index.d.ts +7 -0
  44. package/dist/client/crypto/index.d.ts.map +1 -0
  45. package/dist/client/crypto/index.js +5 -0
  46. package/dist/client/crypto/index.js.map +1 -0
  47. package/dist/client/crypto/primitives.d.ts +43 -0
  48. package/dist/client/crypto/primitives.d.ts.map +1 -0
  49. package/dist/client/crypto/primitives.js +116 -0
  50. package/dist/client/crypto/primitives.js.map +1 -0
  51. package/dist/client/diagnostics.d.ts +45 -0
  52. package/dist/client/diagnostics.d.ts.map +1 -0
  53. package/dist/client/diagnostics.js +30 -0
  54. package/dist/client/diagnostics.js.map +1 -0
  55. package/dist/client/discovery/index.d.ts +2 -0
  56. package/dist/client/discovery/index.d.ts.map +1 -0
  57. package/dist/client/discovery/index.js +2 -0
  58. package/dist/client/discovery/index.js.map +1 -0
  59. package/dist/client/discovery/resolver.d.ts +26 -0
  60. package/dist/client/discovery/resolver.d.ts.map +1 -0
  61. package/dist/client/discovery/resolver.js +275 -0
  62. package/dist/client/discovery/resolver.js.map +1 -0
  63. package/dist/client/errors.d.ts +82 -0
  64. package/dist/client/errors.d.ts.map +1 -0
  65. package/dist/client/errors.js +201 -0
  66. package/dist/client/errors.js.map +1 -0
  67. package/dist/client/events.d.ts +3 -0
  68. package/dist/client/events.d.ts.map +1 -0
  69. package/dist/client/events.js +8 -0
  70. package/dist/client/events.js.map +1 -0
  71. package/dist/client/handshake/crypt-handshake.d.ts +17 -0
  72. package/dist/client/handshake/crypt-handshake.d.ts.map +1 -0
  73. package/dist/client/handshake/crypt-handshake.js +128 -0
  74. package/dist/client/handshake/crypt-handshake.js.map +1 -0
  75. package/dist/client/handshake/crypt-init2.d.ts +7 -0
  76. package/dist/client/handshake/crypt-init2.d.ts.map +1 -0
  77. package/dist/client/handshake/crypt-init2.js +37 -0
  78. package/dist/client/handshake/crypt-init2.js.map +1 -0
  79. package/dist/client/handshake/index.d.ts +4 -0
  80. package/dist/client/handshake/index.d.ts.map +1 -0
  81. package/dist/client/handshake/index.js +4 -0
  82. package/dist/client/handshake/index.js.map +1 -0
  83. package/dist/client/handshake/license.d.ts +35 -0
  84. package/dist/client/handshake/license.d.ts.map +1 -0
  85. package/dist/client/handshake/license.js +169 -0
  86. package/dist/client/handshake/license.js.map +1 -0
  87. package/dist/client/handshake.d.ts +27 -0
  88. package/dist/client/handshake.d.ts.map +1 -0
  89. package/dist/client/handshake.js +126 -0
  90. package/dist/client/handshake.js.map +1 -0
  91. package/dist/client/helpers.d.ts +6 -0
  92. package/dist/client/helpers.d.ts.map +1 -0
  93. package/dist/client/helpers.js +13 -0
  94. package/dist/client/helpers.js.map +1 -0
  95. package/dist/client/hooks.d.ts +7 -0
  96. package/dist/client/hooks.d.ts.map +1 -0
  97. package/dist/client/hooks.js +28 -0
  98. package/dist/client/hooks.js.map +1 -0
  99. package/dist/client/index.d.ts +26 -0
  100. package/dist/client/index.d.ts.map +1 -0
  101. package/dist/client/index.js +28 -0
  102. package/dist/client/index.js.map +1 -0
  103. package/dist/client/reconnect.d.ts +12 -0
  104. package/dist/client/reconnect.d.ts.map +1 -0
  105. package/dist/client/reconnect.js +44 -0
  106. package/dist/client/reconnect.js.map +1 -0
  107. package/dist/client/throttle.d.ts +20 -0
  108. package/dist/client/throttle.d.ts.map +1 -0
  109. package/dist/client/throttle.js +83 -0
  110. package/dist/client/throttle.js.map +1 -0
  111. package/dist/client/trace.d.ts +223 -0
  112. package/dist/client/trace.d.ts.map +1 -0
  113. package/dist/client/trace.js +13 -0
  114. package/dist/client/trace.js.map +1 -0
  115. package/dist/client/transfer.d.ts +111 -0
  116. package/dist/client/transfer.d.ts.map +1 -0
  117. package/dist/client/transfer.js +243 -0
  118. package/dist/client/transfer.js.map +1 -0
  119. package/dist/client/transport/generation-window.d.ts +30 -0
  120. package/dist/client/transport/generation-window.d.ts.map +1 -0
  121. package/dist/client/transport/generation-window.js +97 -0
  122. package/dist/client/transport/generation-window.js.map +1 -0
  123. package/dist/client/transport/handler.d.ts +61 -0
  124. package/dist/client/transport/handler.d.ts.map +1 -0
  125. package/dist/client/transport/handler.js +793 -0
  126. package/dist/client/transport/handler.js.map +1 -0
  127. package/dist/client/transport/index.d.ts +6 -0
  128. package/dist/client/transport/index.d.ts.map +1 -0
  129. package/dist/client/transport/index.js +5 -0
  130. package/dist/client/transport/index.js.map +1 -0
  131. package/dist/client/transport/packet.d.ts +36 -0
  132. package/dist/client/transport/packet.d.ts.map +1 -0
  133. package/dist/client/transport/packet.js +55 -0
  134. package/dist/client/transport/packet.js.map +1 -0
  135. package/dist/client/transport/quicklz.d.ts +24 -0
  136. package/dist/client/transport/quicklz.d.ts.map +1 -0
  137. package/dist/client/transport/quicklz.js +179 -0
  138. package/dist/client/transport/quicklz.js.map +1 -0
  139. package/dist/client/types.d.ts +226 -0
  140. package/dist/client/types.d.ts.map +1 -0
  141. package/dist/client/types.js +39 -0
  142. package/dist/client/types.js.map +1 -0
  143. package/dist/core/api/channels.d.ts +182 -0
  144. package/dist/core/api/channels.d.ts.map +1 -0
  145. package/dist/core/api/channels.js +155 -0
  146. package/dist/core/api/channels.js.map +1 -0
  147. package/dist/core/api/clients.d.ts +304 -0
  148. package/dist/core/api/clients.d.ts.map +1 -0
  149. package/dist/core/api/clients.js +260 -0
  150. package/dist/core/api/clients.js.map +1 -0
  151. package/dist/core/api/command.d.ts +305 -0
  152. package/dist/core/api/command.d.ts.map +1 -0
  153. package/dist/core/api/command.js +363 -0
  154. package/dist/core/api/command.js.map +1 -0
  155. package/dist/core/api/files.d.ts +153 -0
  156. package/dist/core/api/files.d.ts.map +1 -0
  157. package/dist/core/api/files.js +181 -0
  158. package/dist/core/api/files.js.map +1 -0
  159. package/dist/core/api/groups.d.ts +248 -0
  160. package/dist/core/api/groups.d.ts.map +1 -0
  161. package/dist/core/api/groups.js +271 -0
  162. package/dist/core/api/groups.js.map +1 -0
  163. package/dist/core/api/moderation.d.ts +205 -0
  164. package/dist/core/api/moderation.d.ts.map +1 -0
  165. package/dist/core/api/moderation.js +209 -0
  166. package/dist/core/api/moderation.js.map +1 -0
  167. package/dist/core/api/parse.d.ts +16 -0
  168. package/dist/core/api/parse.d.ts.map +1 -0
  169. package/dist/core/api/parse.js +40 -0
  170. package/dist/core/api/parse.js.map +1 -0
  171. package/dist/core/api/server.d.ts +310 -0
  172. package/dist/core/api/server.d.ts.map +1 -0
  173. package/dist/core/api/server.js +290 -0
  174. package/dist/core/api/server.js.map +1 -0
  175. package/dist/core/command/command.d.ts +20 -0
  176. package/dist/core/command/command.d.ts.map +1 -0
  177. package/dist/core/command/command.js +112 -0
  178. package/dist/core/command/command.js.map +1 -0
  179. package/dist/core/command/ids.d.ts +125 -0
  180. package/dist/core/command/ids.d.ts.map +1 -0
  181. package/dist/core/command/ids.js +85 -0
  182. package/dist/core/command/ids.js.map +1 -0
  183. package/dist/core/command/parser.d.ts +17 -0
  184. package/dist/core/command/parser.d.ts.map +1 -0
  185. package/dist/core/command/parser.js +65 -0
  186. package/dist/core/command/parser.js.map +1 -0
  187. package/dist/core/command/request.d.ts +61 -0
  188. package/dist/core/command/request.d.ts.map +1 -0
  189. package/dist/core/command/request.js +64 -0
  190. package/dist/core/command/request.js.map +1 -0
  191. package/dist/core/command/responses.d.ts +59 -0
  192. package/dist/core/command/responses.d.ts.map +1 -0
  193. package/dist/core/command/responses.js +74 -0
  194. package/dist/core/command/responses.js.map +1 -0
  195. package/dist/core/command-name.d.ts +333 -0
  196. package/dist/core/command-name.d.ts.map +1 -0
  197. package/dist/core/command-name.js +331 -0
  198. package/dist/core/command-name.js.map +1 -0
  199. package/dist/core/enums.d.ts +146 -0
  200. package/dist/core/enums.d.ts.map +1 -0
  201. package/dist/core/enums.js +132 -0
  202. package/dist/core/enums.js.map +1 -0
  203. package/dist/core/errorcode.d.ts +413 -0
  204. package/dist/core/errorcode.d.ts.map +1 -0
  205. package/dist/core/errorcode.js +410 -0
  206. package/dist/core/errorcode.js.map +1 -0
  207. package/dist/core/errors.d.ts +111 -0
  208. package/dist/core/errors.d.ts.map +1 -0
  209. package/dist/core/errors.js +233 -0
  210. package/dist/core/errors.js.map +1 -0
  211. package/dist/core/events.d.ts +207 -0
  212. package/dist/core/events.d.ts.map +1 -0
  213. package/dist/core/events.js +6 -0
  214. package/dist/core/events.js.map +1 -0
  215. package/dist/core/helpers.d.ts +42 -0
  216. package/dist/core/helpers.d.ts.map +1 -0
  217. package/dist/core/helpers.js +156 -0
  218. package/dist/core/helpers.js.map +1 -0
  219. package/dist/core/index.d.ts +35 -0
  220. package/dist/core/index.d.ts.map +1 -0
  221. package/dist/core/index.js +34 -0
  222. package/dist/core/index.js.map +1 -0
  223. package/dist/core/internal.d.ts +23 -0
  224. package/dist/core/internal.d.ts.map +1 -0
  225. package/dist/core/internal.js +20 -0
  226. package/dist/core/internal.js.map +1 -0
  227. package/dist/core/middleware.d.ts +14 -0
  228. package/dist/core/middleware.d.ts.map +1 -0
  229. package/dist/core/middleware.js +18 -0
  230. package/dist/core/middleware.js.map +1 -0
  231. package/dist/core/notification-name.d.ts +154 -0
  232. package/dist/core/notification-name.d.ts.map +1 -0
  233. package/dist/core/notification-name.js +152 -0
  234. package/dist/core/notification-name.js.map +1 -0
  235. package/dist/core/notifications.d.ts +43 -0
  236. package/dist/core/notifications.d.ts.map +1 -0
  237. package/dist/core/notifications.js +116 -0
  238. package/dist/core/notifications.js.map +1 -0
  239. package/dist/core/permission-name.d.ts +533 -0
  240. package/dist/core/permission-name.d.ts.map +1 -0
  241. package/dist/core/permission-name.js +531 -0
  242. package/dist/core/permission-name.js.map +1 -0
  243. package/dist/core/permission.d.ts +31 -0
  244. package/dist/core/permission.d.ts.map +1 -0
  245. package/dist/core/permission.js +20 -0
  246. package/dist/core/permission.js.map +1 -0
  247. package/dist/core/property-name.d.ts +403 -0
  248. package/dist/core/property-name.d.ts.map +1 -0
  249. package/dist/core/property-name.js +405 -0
  250. package/dist/core/property-name.js.map +1 -0
  251. package/dist/core/reason.d.ts +39 -0
  252. package/dist/core/reason.d.ts.map +1 -0
  253. package/dist/core/reason.js +36 -0
  254. package/dist/core/reason.js.map +1 -0
  255. package/dist/core/reconnect.d.ts +59 -0
  256. package/dist/core/reconnect.d.ts.map +1 -0
  257. package/dist/core/reconnect.js +55 -0
  258. package/dist/core/reconnect.js.map +1 -0
  259. package/dist/core/types.d.ts +110 -0
  260. package/dist/core/types.d.ts.map +1 -0
  261. package/dist/core/types.js +27 -0
  262. package/dist/core/types.js.map +1 -0
  263. package/dist/query/client.d.ts +194 -0
  264. package/dist/query/client.d.ts.map +1 -0
  265. package/dist/query/client.js +967 -0
  266. package/dist/query/client.js.map +1 -0
  267. package/dist/query/errors.d.ts +61 -0
  268. package/dist/query/errors.d.ts.map +1 -0
  269. package/dist/query/errors.js +100 -0
  270. package/dist/query/errors.js.map +1 -0
  271. package/dist/query/index.d.ts +36 -0
  272. package/dist/query/index.d.ts.map +1 -0
  273. package/dist/query/index.js +34 -0
  274. package/dist/query/index.js.map +1 -0
  275. package/dist/query/knownhosts.d.ts +61 -0
  276. package/dist/query/knownhosts.d.ts.map +1 -0
  277. package/dist/query/knownhosts.js +223 -0
  278. package/dist/query/knownhosts.js.map +1 -0
  279. package/dist/query/lines.d.ts +14 -0
  280. package/dist/query/lines.d.ts.map +1 -0
  281. package/dist/query/lines.js +29 -0
  282. package/dist/query/lines.js.map +1 -0
  283. package/dist/query/options.d.ts +187 -0
  284. package/dist/query/options.d.ts.map +1 -0
  285. package/dist/query/options.js +213 -0
  286. package/dist/query/options.js.map +1 -0
  287. package/dist/query/reconnect.d.ts +29 -0
  288. package/dist/query/reconnect.d.ts.map +1 -0
  289. package/dist/query/reconnect.js +87 -0
  290. package/dist/query/reconnect.js.map +1 -0
  291. package/dist/query/ssh.d.ts +345 -0
  292. package/dist/query/ssh.d.ts.map +1 -0
  293. package/dist/query/ssh.js +241 -0
  294. package/dist/query/ssh.js.map +1 -0
  295. package/dist/query/stream.d.ts +38 -0
  296. package/dist/query/stream.d.ts.map +1 -0
  297. package/dist/query/stream.js +70 -0
  298. package/dist/query/stream.js.map +1 -0
  299. package/dist/query/tcp.d.ts +38 -0
  300. package/dist/query/tcp.d.ts.map +1 -0
  301. package/dist/query/tcp.js +53 -0
  302. package/dist/query/tcp.js.map +1 -0
  303. package/dist/query/transport.d.ts +36 -0
  304. package/dist/query/transport.d.ts.map +1 -0
  305. package/dist/query/transport.js +2 -0
  306. package/dist/query/transport.js.map +1 -0
  307. package/dist/query/web.d.ts +54 -0
  308. package/dist/query/web.d.ts.map +1 -0
  309. package/dist/query/web.js +146 -0
  310. package/dist/query/web.js.map +1 -0
  311. package/dist/versions/index.d.ts +3094 -0
  312. package/dist/versions/index.d.ts.map +1 -0
  313. package/dist/versions/index.js +9237 -0
  314. package/dist/versions/index.js.map +1 -0
  315. package/package.json +118 -4
  316. package/src/client/abort.ts +24 -0
  317. package/src/client/address.ts +74 -0
  318. package/src/client/api/voice.ts +61 -0
  319. package/src/client/capabilities.ts +106 -0
  320. package/src/client/client.ts +1809 -0
  321. package/src/client/commands.ts +193 -0
  322. package/src/client/crypto/crypt-ops.ts +6 -0
  323. package/src/client/crypto/crypt.ts +275 -0
  324. package/src/client/crypto/eax.ts +152 -0
  325. package/src/client/crypto/identity.ts +484 -0
  326. package/src/client/crypto/index.ts +21 -0
  327. package/src/client/crypto/primitives.ts +141 -0
  328. package/src/client/diagnostics.ts +58 -0
  329. package/src/client/discovery/index.ts +1 -0
  330. package/src/client/discovery/resolver.ts +325 -0
  331. package/src/client/errors.ts +217 -0
  332. package/src/client/events.ts +12 -0
  333. package/src/client/handshake/crypt-handshake.ts +157 -0
  334. package/src/client/handshake/crypt-init2.ts +48 -0
  335. package/src/client/handshake/index.ts +3 -0
  336. package/src/client/handshake/license.ts +208 -0
  337. package/src/client/handshake.ts +150 -0
  338. package/src/client/helpers.ts +10 -0
  339. package/src/client/hooks.ts +30 -0
  340. package/src/client/index.ts +93 -0
  341. package/src/client/reconnect.ts +43 -0
  342. package/src/client/throttle.ts +92 -0
  343. package/src/client/trace.ts +237 -0
  344. package/src/client/transfer.ts +436 -0
  345. package/src/client/transport/generation-window.ts +108 -0
  346. package/src/client/transport/handler.ts +926 -0
  347. package/src/client/transport/index.ts +14 -0
  348. package/src/client/transport/packet.ts +69 -0
  349. package/src/client/transport/quicklz.ts +209 -0
  350. package/src/client/types.ts +268 -0
  351. package/src/core/api/channels.ts +396 -0
  352. package/src/core/api/clients.ts +654 -0
  353. package/src/core/api/command.ts +563 -0
  354. package/src/core/api/files.ts +405 -0
  355. package/src/core/api/groups.ts +622 -0
  356. package/src/core/api/moderation.ts +525 -0
  357. package/src/core/api/parse.ts +52 -0
  358. package/src/core/api/server.ts +709 -0
  359. package/src/core/command/command.ts +125 -0
  360. package/src/core/command/ids.ts +149 -0
  361. package/src/core/command/parser.ts +68 -0
  362. package/src/core/command/request.ts +119 -0
  363. package/src/core/command/responses.ts +114 -0
  364. package/src/core/command-name.ts +334 -0
  365. package/src/core/enums.ts +167 -0
  366. package/src/core/errorcode.ts +412 -0
  367. package/src/core/errors.ts +258 -0
  368. package/src/core/events.ts +219 -0
  369. package/src/core/helpers.ts +161 -0
  370. package/src/core/index.ts +126 -0
  371. package/src/core/internal.ts +23 -0
  372. package/src/core/middleware.ts +24 -0
  373. package/src/core/notification-name.ts +155 -0
  374. package/src/core/notifications.ts +157 -0
  375. package/src/core/permission-name.ts +538 -0
  376. package/src/core/permission.ts +34 -0
  377. package/src/core/property-name.ts +411 -0
  378. package/src/core/reason.ts +38 -0
  379. package/src/core/reconnect.ts +98 -0
  380. package/src/core/types.ts +138 -0
  381. package/src/query/client.ts +1132 -0
  382. package/src/query/errors.ts +108 -0
  383. package/src/query/index.ts +65 -0
  384. package/src/query/knownhosts.ts +257 -0
  385. package/src/query/lines.ts +28 -0
  386. package/src/query/options.ts +388 -0
  387. package/src/query/reconnect.ts +84 -0
  388. package/src/query/ssh.ts +614 -0
  389. package/src/query/stream.ts +91 -0
  390. package/src/query/tcp.ts +85 -0
  391. package/src/query/transport.ts +35 -0
  392. package/src/query/web.ts +195 -0
  393. package/src/versions/index.ts +10774 -0
@@ -0,0 +1,388 @@
1
+ import type { CommandMiddleware, Logger, ReconnectOptions, ServerID } from "@honeybbq/teamspeak";
2
+ import type { QueryTransport } from "./transport.js";
3
+ import type { TcpConnector } from "./tcp.js";
4
+ import { hostKeyDecision } from "./ssh.js";
5
+ import type { SshConnectOptions, SshConnection } from "./ssh.js";
6
+
7
+ /**
8
+ * The connection a `QueryClient` talks over, as a description rather than an
9
+ * open transport: the client builds one from it on every connection, so a
10
+ * reconnect is the same connection as the first one was.
11
+ *
12
+ * `custom` is the escape hatch for anything the three built-in kinds do not
13
+ * cover — a proxy, a tunnel, a test double — and is the only one that cannot
14
+ * be rebuilt from data alone: it is called again for each attempt.
15
+ */
16
+ export type QueryTransportOptions =
17
+ | {
18
+ /** A raw ServerQuery connection. TeamSpeak 3 only: TS6 dropped it. */
19
+ readonly kind: "tcp";
20
+ /** The server's host. */
21
+ readonly host: string;
22
+ /** The raw query port; 10011 by default. */
23
+ readonly port?: number | undefined;
24
+ /**
25
+ * Opens the connection; `net.createConnection` by default. See
26
+ * {@link TcpOptions.connect}.
27
+ */
28
+ readonly connect?: TcpConnector | undefined;
29
+ }
30
+ | ({
31
+ /** A ServerQuery over SSH. */
32
+ readonly kind: "ssh";
33
+ } & SshConnectOptions)
34
+ | {
35
+ /** TeamSpeak 6's HTTP WebQuery. */
36
+ readonly kind: "web";
37
+ /** The server's base URL, such as `http://127.0.0.1:10080`. */
38
+ readonly url: string;
39
+ /** The api key, sent as `x-api-key`; it replaces `login`. */
40
+ readonly apiKey: string;
41
+ /** The `fetch` to use; a small one over `node:http` by default. */
42
+ readonly fetch?: typeof fetch | undefined;
43
+ }
44
+ | {
45
+ /** An application-supplied transport. */
46
+ readonly kind: "custom";
47
+ /**
48
+ * Open the transport. Called once per connection attempt, with the
49
+ * signal that abandons that attempt; a transport that ignores it is
50
+ * abandoned all the same.
51
+ */
52
+ readonly open: (signal?: AbortSignal) => Promise<QueryTransport>;
53
+ };
54
+
55
+ /**
56
+ * One category of notifications to register for: the `event` parameter of
57
+ * `servernotifyregister`.
58
+ *
59
+ * `bans` is not registered by default: TeamSpeak 3 refuses it with 1539,
60
+ * which would fail the registration of every category after it.
61
+ */
62
+ export type QueryEventCategory =
63
+ | "server"
64
+ | "channel"
65
+ | "textserver"
66
+ | "textchannel"
67
+ | "textprivate"
68
+ | "bans";
69
+
70
+ /**
71
+ * Which notification categories to register for.
72
+ *
73
+ * `true` (the default) registers the ones raw TCP and SSH both accept;
74
+ * `{ categories }` names them; `false` registers nothing. This says nothing
75
+ * about whether the transport can deliver notifications — a transport
76
+ * declares that itself, see {@link QueryTransport.supportsNotifications} — so
77
+ * asking for categories on one that cannot is an error rather than a silent
78
+ * no-op.
79
+ */
80
+ export type QueryEventsOption = boolean | { readonly categories?: readonly QueryEventCategory[] };
81
+
82
+ /** How a `QueryClient` connects, logs and behaves. */
83
+ export interface QueryClientOptions {
84
+ /** How to reach the server. Required: a client cannot connect without one. */
85
+ readonly transport: QueryTransportOptions;
86
+ /**
87
+ * The query account to log in as, sent as a `login` command after the
88
+ * transport is opened. Raw TCP only: SSH logs in as the session's account,
89
+ * and WebQuery's api key is the login, so giving this with either throws.
90
+ */
91
+ readonly login?: { readonly name: string; readonly password: string } | undefined;
92
+ /**
93
+ * The virtual server to select with `use`, by id or by voice port. Over
94
+ * WebQuery the transport answers `use` itself, by addressing that server in
95
+ * its requests. Unset leaves the instance selected on raw TCP, where the
96
+ * server's own default applies.
97
+ */
98
+ readonly server?: { readonly id: ServerID | number } | { readonly port: number } | undefined;
99
+ /** The query client's nickname on the server (`client_nickname` of `use`). */
100
+ readonly nickname?: string | undefined;
101
+ /**
102
+ * Register for server notifications: `true` (the default) for the five
103
+ * categories raw TCP and SSH both accept, `{ categories }` to name them, or
104
+ * `false` for none. See {@link QueryEventsOption}.
105
+ *
106
+ * Registering for anything on a transport that cannot deliver notifications
107
+ * throws a `TypeError`, at construction when the `transport` kind says so
108
+ * and when connecting for a `custom` transport.
109
+ */
110
+ readonly events?: QueryEventsOption | undefined;
111
+ /**
112
+ * Reconnect after a dropped connection. Off by default, as for the voice
113
+ * client, and a deliberate `disconnect()` never reconnects. The retry
114
+ * decision is {@link defaultShouldRetry}.
115
+ */
116
+ readonly reconnect?: ReconnectOptions | undefined;
117
+ /**
118
+ * Wraps every typed command, outermost first; see `CommandMiddleware`. Raw
119
+ * lines sent with `exec` do not pass through it. Fixed for the client's life.
120
+ */
121
+ readonly commandMiddleware?: readonly CommandMiddleware[] | undefined;
122
+ /** Where the client logs. Silent unless set. */
123
+ readonly logger?: Logger | undefined;
124
+ }
125
+
126
+ /** The five categories both raw TCP on TS3 and SSH on TS6 accept. */
127
+ export const DEFAULT_EVENT_CATEGORIES: readonly QueryEventCategory[] = Object.freeze([
128
+ "server",
129
+ "channel",
130
+ "textserver",
131
+ "textchannel",
132
+ "textprivate",
133
+ ]);
134
+
135
+ /** The kinds whose own handshake is the login, so there is no `login` to send. */
136
+ const LOGIN_HANDLED_BY_TRANSPORT: ReadonlySet<ResolvedTransport["kind"]> = new Set(["ssh", "web"]);
137
+
138
+ const CATEGORIES: ReadonlySet<string> = new Set([
139
+ "server",
140
+ "channel",
141
+ "textserver",
142
+ "textchannel",
143
+ "textprivate",
144
+ "bans",
145
+ ]);
146
+
147
+ /** A transport with its defaults filled, and its fields copied. */
148
+ export type ResolvedTransport =
149
+ | {
150
+ readonly kind: "tcp";
151
+ readonly host: string;
152
+ readonly port: number;
153
+ readonly connect: TcpConnector | undefined;
154
+ }
155
+ | {
156
+ readonly kind: "ssh";
157
+ /** What `connectSsh` is given, with the port filled in; one arm of {@link SshConnectOptions}. */
158
+ readonly connect: SshConnectOptions;
159
+ }
160
+ | {
161
+ readonly kind: "web";
162
+ readonly url: string;
163
+ readonly apiKey: string;
164
+ readonly fetch: typeof fetch | undefined;
165
+ }
166
+ | { readonly kind: "custom"; readonly open: (signal?: AbortSignal) => Promise<QueryTransport> };
167
+
168
+ /** Options with their defaults filled and their fields copied. */
169
+ export interface ResolvedQueryOptions {
170
+ readonly transport: ResolvedTransport;
171
+ readonly login: { readonly name: string; readonly password: string } | undefined;
172
+ readonly server: { readonly id: ServerID | number } | { readonly port: number } | undefined;
173
+ readonly nickname: string | undefined;
174
+ /** The categories the caller asked to register for; `events: false` leaves this empty. */
175
+ readonly eventCategories: readonly QueryEventCategory[];
176
+ /**
177
+ * Whether the transport the options name can deliver notifications. False
178
+ * for `web`; `undefined` for `custom`, whose transport object does not exist
179
+ * until it is opened, and which is checked then.
180
+ */
181
+ readonly notificationsSupported: boolean | undefined;
182
+ /**
183
+ * Whether the caller named `events` at all, whatever its value. On a
184
+ * transport that cannot deliver notifications, naming it is the mistake to
185
+ * report; leaving it unset is not.
186
+ */
187
+ readonly eventsRequested: boolean;
188
+ readonly reconnect: ReconnectOptions | undefined;
189
+ readonly commandMiddleware: readonly CommandMiddleware[] | undefined;
190
+ readonly logger: Logger | undefined;
191
+ }
192
+
193
+ function nonEmpty(value: unknown, name: string): string {
194
+ if (typeof value !== "string" || value === "") {
195
+ throw new TypeError(`${name} is a non-empty string`);
196
+ }
197
+ return value;
198
+ }
199
+
200
+ function port(value: unknown, name: string): number {
201
+ if (value === undefined) return -1; // replaced by the caller's default
202
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 65535) {
203
+ throw new RangeError(`${name} is an integer port from 1 to 65535`);
204
+ }
205
+ return value;
206
+ }
207
+
208
+ function transportOptions(transport: unknown): ResolvedTransport {
209
+ if (typeof transport !== "object" || transport === null) {
210
+ throw new TypeError("transport is an object");
211
+ }
212
+ const options = transport as Record<string, unknown>;
213
+ switch (options["kind"]) {
214
+ case "tcp": {
215
+ const host = nonEmpty(options["host"], "transport.host");
216
+ const raw = port(options["port"], "transport.port");
217
+ const connect = options["connect"];
218
+ if (connect !== undefined && typeof connect !== "function") {
219
+ throw new TypeError("transport.connect is a function");
220
+ }
221
+ return {
222
+ kind: "tcp",
223
+ host,
224
+ port: raw < 0 ? 10011 : raw,
225
+ connect: connect as TcpConnector | undefined,
226
+ };
227
+ }
228
+ case "ssh": {
229
+ const host = nonEmpty(options["host"], "transport.host");
230
+ const raw = port(options["port"], "transport.port");
231
+ const connection = options["connection"];
232
+ if (
233
+ connection !== undefined &&
234
+ typeof connection !== "function" &&
235
+ (typeof connection !== "object" || connection === null)
236
+ ) {
237
+ throw new TypeError("transport.connection is an SSH client or a factory for one");
238
+ }
239
+ // Asked here as well as when connecting: a client that could never
240
+ // connect, for want of a decision about the host key, is the caller's
241
+ // mistake and is reported when the client is made.
242
+ const decision = hostKeyDecision(options);
243
+ const address = { host, port: raw < 0 ? 10022 : raw };
244
+ // Built from the decision rather than copied, so each arm is made of
245
+ // exactly what it takes and the union needs no cast.
246
+ if (decision.kind === "connection") {
247
+ return {
248
+ kind: "ssh",
249
+ connect: { ...address, connection: connection as SshConnection },
250
+ };
251
+ }
252
+ const login = {
253
+ ...address,
254
+ username: nonEmpty(options["username"], "transport.username"),
255
+ password: nonEmpty(options["password"], "transport.password"),
256
+ };
257
+ switch (decision.kind) {
258
+ case "verifier":
259
+ return { kind: "ssh", connect: { ...login, hostKey: decision.verify } };
260
+ case "knownHosts":
261
+ return { kind: "ssh", connect: { ...login, knownHosts: decision.path } };
262
+ case "insecure":
263
+ return { kind: "ssh", connect: { ...login, insecureIgnoreHostKey: true } };
264
+ }
265
+ }
266
+ case "web": {
267
+ const fetchFn: unknown = options["fetch"];
268
+ if (fetchFn !== undefined && typeof fetchFn !== "function") {
269
+ throw new TypeError("transport.fetch is a function");
270
+ }
271
+ return {
272
+ kind: "web",
273
+ url: nonEmpty(options["url"], "transport.url"),
274
+ apiKey: nonEmpty(options["apiKey"], "transport.apiKey"),
275
+ fetch: fetchFn as typeof globalThis.fetch | undefined,
276
+ };
277
+ }
278
+ case "custom": {
279
+ const open = options["open"];
280
+ if (typeof open !== "function") throw new TypeError("transport.open is a function");
281
+ return {
282
+ kind: "custom",
283
+ open: open as (signal?: AbortSignal) => Promise<QueryTransport>,
284
+ };
285
+ }
286
+ default:
287
+ throw new TypeError('transport.kind is one of "tcp", "ssh", "web" or "custom"');
288
+ }
289
+ }
290
+
291
+ /**
292
+ * The categories to register for. These are a wish, not a capability: whether
293
+ * the transport can deliver a notification is its own to report, and a request
294
+ * on one that cannot is the caller's mistake, reported by
295
+ * {@link assertEventsSupported}.
296
+ */
297
+ function eventCategories(events: QueryEventsOption | undefined): readonly QueryEventCategory[] {
298
+ if (events === false) return [];
299
+ if (events === undefined || events === true) return DEFAULT_EVENT_CATEGORIES;
300
+ if (typeof events !== "object" || events === null) {
301
+ throw new TypeError("events is a boolean or { categories }");
302
+ }
303
+ const categories = events.categories ?? DEFAULT_EVENT_CATEGORIES;
304
+ if (!Array.isArray(categories)) throw new TypeError("events.categories is an array");
305
+ for (const category of categories) {
306
+ if (typeof category !== "string" || !CATEGORIES.has(category)) {
307
+ throw new TypeError(`events.categories has an unknown category ${JSON.stringify(category)}`);
308
+ }
309
+ }
310
+ return [...categories] as readonly QueryEventCategory[];
311
+ }
312
+
313
+ /**
314
+ * Refuse a request to register on a transport that cannot deliver what it
315
+ * would register for.
316
+ *
317
+ * WebQuery refuses `servernotifyregister` with 5120, and a listener that can
318
+ * never run is worse than an error: the caller would be left believing the
319
+ * server had nothing to say. Naming `events` at all is the mistake, even as
320
+ * `false`, since it says the caller read the option as a capability.
321
+ */
322
+ export function assertEventsSupported(resolved: ResolvedQueryOptions): void {
323
+ if (resolved.notificationsSupported !== false || !resolved.eventsRequested) return;
324
+ throw new TypeError("this transport delivers no notifications, so `events` cannot be set");
325
+ }
326
+
327
+ /**
328
+ * Check the options and fill in the defaults, once, when the client is made:
329
+ * an option that cannot be one is a bug in the caller's code, and is worth
330
+ * hearing about before a connection is attempted.
331
+ */
332
+ export function normalizeQueryOptions(options: QueryClientOptions): ResolvedQueryOptions {
333
+ const transport = transportOptions(options.transport);
334
+ const login = options.login;
335
+ if (login !== undefined && typeof login !== "object") {
336
+ throw new TypeError("login is { name, password }");
337
+ }
338
+ const server = options.server;
339
+ if (server !== undefined) {
340
+ if (typeof server !== "object" || server === null) {
341
+ throw new TypeError("server is { id } or { port }");
342
+ }
343
+ const byID = "id" in server;
344
+ const raw = byID ? server.id : server.port;
345
+ const value = typeof raw === "bigint" ? Number(raw) : raw;
346
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
347
+ throw new RangeError(`server.${byID ? "id" : "port"} is a non-negative integer`);
348
+ }
349
+ // A port names a virtual server's voice port, which is 1 to 65535; an id
350
+ // is a server's own number, which starts at 1 and has no ceiling here.
351
+ if (!byID && (value < 1 || value > 65535)) {
352
+ throw new RangeError("server.port is an integer from 1 to 65535");
353
+ }
354
+ }
355
+ const nickname = options.nickname;
356
+ if (nickname !== undefined) nonEmpty(nickname, "nickname");
357
+ const commandMiddleware = options.commandMiddleware;
358
+ if (commandMiddleware !== undefined && !Array.isArray(commandMiddleware)) {
359
+ throw new TypeError("commandMiddleware is an array");
360
+ }
361
+ if (login !== undefined && LOGIN_HANDLED_BY_TRANSPORT.has(transport.kind)) {
362
+ // The SSH session is the query account already, and WebQuery's api key is
363
+ // the login; neither leaves a `login` to send, and the server refuses the
364
+ // command on both. A silently dropped option would leave the caller
365
+ // believing they had logged in.
366
+ throw new TypeError(
367
+ `login is not sent over the ${transport.kind} transport, which logs in by itself`,
368
+ );
369
+ }
370
+
371
+ const resolved: ResolvedQueryOptions = {
372
+ transport,
373
+ // Kept for `custom` too: its opener is the caller's, and most of them wrap
374
+ // one of the built-in transports, `tcp` among them.
375
+ login: login === undefined ? undefined : { ...login },
376
+ server: server === undefined ? undefined : { ...server },
377
+ nickname,
378
+ eventCategories: eventCategories(options.events),
379
+ // `web` is the only kind whose capability is known before it is opened.
380
+ notificationsSupported: transport.kind === "custom" ? undefined : transport.kind !== "web",
381
+ eventsRequested: options.events !== undefined,
382
+ reconnect: options.reconnect,
383
+ commandMiddleware,
384
+ logger: options.logger,
385
+ };
386
+ assertEventsSupported(resolved);
387
+ return resolved;
388
+ }
@@ -0,0 +1,84 @@
1
+ import { AlreadyConnectedError, ErrorCode, ServerError } from "@honeybbq/teamspeak";
2
+ import { MissingDependencyError, QueryAuthError, SshHostKeyError } from "./errors.js";
3
+
4
+ /**
5
+ * Server errors where retrying cannot help.
6
+ *
7
+ * A refused `login` or `use` is the case this exists for. `login` is the only
8
+ * command that can succeed with the wrong credentials and fail with the right
9
+ * ones, and a wrong password stays wrong, so retrying it costs a bounded few
10
+ * attempts and floods the server for nothing. The server's own flood refusal
11
+ * (524) is deliberately *not* here: it clears by itself, and it is what a
12
+ * reconnect loop is most likely to meet right after a drop.
13
+ */
14
+ const PERMANENT_SERVER_ERRORS: ReadonlySet<number> = new Set([
15
+ ErrorCode.ClientInvalidPassword,
16
+ ErrorCode.ClientNotLoggedIn,
17
+ ErrorCode.ClientLoginNotPermitted,
18
+ ErrorCode.ServerInvalidID,
19
+ ErrorCode.ConnectFailedBanned,
20
+ ErrorCode.BanFlooding,
21
+ ErrorCode.PermissionsClientInsufficient,
22
+ ]);
23
+
24
+ /** Commands whose refusal is the credentials or the target, not the network. */
25
+ const CONNECT_COMMANDS: ReadonlySet<string> = new Set(["login", "use"]);
26
+
27
+ /**
28
+ * Refusals of those commands that do clear by themselves: the query flood
29
+ * limit, which the client meets because reconnecting is exactly what the limit
30
+ * is about.
31
+ */
32
+ const TRANSIENT_SERVER_ERRORS: ReadonlySet<number> = new Set([ErrorCode.ClientIsFlooding]);
33
+
34
+ /**
35
+ * Whether a failed connection attempt is worth another one.
36
+ *
37
+ * The voice client's `defaultShouldRetry` answers the same question for a
38
+ * dropped voice session, where the failure to retry is a kick or a bad
39
+ * password. ServerQuery has neither a session to be kicked from nor a
40
+ * handshake to fail, so the rule is about the commands `connect()` runs: a
41
+ * refused `login` or `use` is not retried, nor is an ssh account the server
42
+ * will not authenticate, nor a host key that was not accepted, nor a missing
43
+ * `ssh2`. Everything else — a refused
44
+ * TCP connection, a dropped one, an HTTP failure, a server that stopped
45
+ * answering — is.
46
+ *
47
+ * A custom `shouldRetry` replaces this; see `ReconnectOptions`.
48
+ */
49
+ export function defaultShouldRetry(error: Error | undefined): boolean {
50
+ // The client is connected, or connecting, already: nothing is left to retry.
51
+ if (error instanceof AlreadyConnectedError) return false;
52
+ // An account the server refused, or a transport that is on the wrong side
53
+ // of a firewall: both stay refused.
54
+ if (error instanceof QueryAuthError) return false;
55
+ // A key that was refused stays refused: the file or the caller's check says
56
+ // the same thing next time, and a changed key is not one to keep knocking on.
57
+ if (error instanceof SshHostKeyError) return false;
58
+ if (error instanceof MissingDependencyError) return false;
59
+ if (error instanceof ServerError) {
60
+ return !PERMANENT_SERVER_ERRORS.has(error.code);
61
+ }
62
+ // A transport's own failure, such as a refused socket or a reset. The
63
+ // offending command is not known from here, so the loop's own history is
64
+ // what decides; see `QueryClient`.
65
+ return true;
66
+ }
67
+
68
+ /**
69
+ * Whether a command that failed during `connect()` is one to give up on: the
70
+ * commands whose failure is a fact about the account or the target rather than
71
+ * about the connection.
72
+ *
73
+ * `defaultShouldRetry` cannot see which command failed, and does not need to:
74
+ * it is called with the error alone, as `ReconnectOptions` documents, and
75
+ * covers the errors a wrong password arrives as. This covers the rest — a
76
+ * `use` refused with a code that is not in the list above, such as 1542 for a
77
+ * port no virtual server is on.
78
+ */
79
+ export function isPermanentCommandError(command: string, error: unknown): boolean {
80
+ if (!CONNECT_COMMANDS.has(command)) return false;
81
+ if (error instanceof QueryAuthError) return true;
82
+ if (error instanceof ServerError) return !TRANSIENT_SERVER_ERRORS.has(error.code);
83
+ return false;
84
+ }