@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,157 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { buildCommandOrdered } from "../../core/internal.js";
3
+ import type { Crypt } from "../crypto/crypt.js";
4
+ import { HandshakeError } from "../errors.js";
5
+
6
+ export const INIT_VERSION = 1566914096; // 3.5.0 [Stable]
7
+
8
+ const INIT_VERSION_LEN = 4;
9
+ const INIT_TYPE_LEN = 1;
10
+ const INIT_STEP_LEN = 21;
11
+
12
+ /** The RSA challenge each connection attempt, by its Crypt, has answered. */
13
+ const answeredChallenges = new WeakMap<
14
+ Crypt,
15
+ {
16
+ readonly challenge: Uint8Array;
17
+ readonly answer: Uint8Array;
18
+ readonly level: number;
19
+ readonly durationMs: number;
20
+ }
21
+ >();
22
+
23
+ /**
24
+ * The RSA puzzle `crypt`'s attempt solved: the level the server demanded and
25
+ * the milliseconds spent on it. Level 0 and no time when the server sent none.
26
+ */
27
+ export function puzzleOf(crypt: Crypt): { readonly level: number; readonly durationMs: number } {
28
+ const answered = answeredChallenges.get(crypt);
29
+ return { level: answered?.level ?? 0, durationMs: answered?.durationMs ?? 0 };
30
+ }
31
+
32
+ /**
33
+ * Handle the TS3INIT1 handshake steps.
34
+ * Returns the response bytes to send, or null if nothing should be sent.
35
+ * Throws a HandshakeError for a step that cannot be answered.
36
+ */
37
+ export function processInit1(crypt: Crypt, data: Uint8Array | null): Uint8Array | null {
38
+ if (data === null || (data.length >= 1 && data[0] === 0x7f)) {
39
+ return buildInit1StartPacket();
40
+ }
41
+ if (data.length === 0) throw new HandshakeError("empty Init1 packet");
42
+
43
+ switch (data[0]) {
44
+ case 0:
45
+ return buildInit1Step1Packet(data);
46
+ case 1:
47
+ return buildInit1Step2Packet(data);
48
+ case 2:
49
+ return buildInit1Step3Packet(data);
50
+ case 3:
51
+ return buildInit1Step4Packet(crypt, data);
52
+ case undefined:
53
+ default:
54
+ return null;
55
+ }
56
+ }
57
+
58
+ function buildInit1StartPacket(): Uint8Array {
59
+ const sendData = new Uint8Array(INIT_VERSION_LEN + INIT_TYPE_LEN + 4 + 4 + 8);
60
+ const view = new DataView(sendData.buffer);
61
+
62
+ view.setUint32(0, INIT_VERSION, false);
63
+ sendData[4] = 0x00;
64
+
65
+ const nowSec = Math.floor(Date.now() / 1000);
66
+ const clampedNow = Math.max(0, Math.min(nowSec, 0xffff_ffff));
67
+ view.setUint32(5, clampedNow, false);
68
+
69
+ const rng = randomBytes(4);
70
+ sendData.set(rng, 9);
71
+
72
+ return sendData;
73
+ }
74
+
75
+ function buildInit1Step1Packet(data: Uint8Array): Uint8Array | null {
76
+ if (data.length !== INIT_STEP_LEN) return null;
77
+
78
+ const sendData = new Uint8Array(INIT_TYPE_LEN + 16 + 4);
79
+ sendData[0] = 0x01;
80
+
81
+ // TS rand: little-endian uint32 at offset [INIT_VERSION_LEN + INIT_TYPE_LEN + 4]
82
+ const tsRandOffset = INIT_VERSION_LEN + INIT_TYPE_LEN + 4;
83
+ const tsRand =
84
+ data[tsRandOffset]! |
85
+ (data[tsRandOffset + 1]! << 8) |
86
+ (data[tsRandOffset + 2]! << 16) |
87
+ (data[tsRandOffset + 3]! << 24);
88
+
89
+ new DataView(sendData.buffer).setUint32(INIT_TYPE_LEN + 16, tsRand >>> 0, false);
90
+ return sendData;
91
+ }
92
+
93
+ function buildInit1Step2Packet(data: Uint8Array): Uint8Array | null {
94
+ if (data.length !== INIT_STEP_LEN) return null;
95
+
96
+ const sendData = new Uint8Array(INIT_VERSION_LEN + INIT_TYPE_LEN + 16 + 4);
97
+ new DataView(sendData.buffer).setUint32(0, INIT_VERSION, false);
98
+ sendData[4] = 0x02;
99
+ sendData.set(data.slice(1, 21), 5);
100
+ return sendData;
101
+ }
102
+
103
+ function buildInit1Step3Packet(data: Uint8Array): Uint8Array | null {
104
+ if (data.length !== INIT_VERSION_LEN + INIT_TYPE_LEN + 16 + 4) return null;
105
+
106
+ const sendData = new Uint8Array(INIT_TYPE_LEN + 64 + 64 + 4 + 100);
107
+ sendData[0] = 0x03;
108
+ sendData[INIT_TYPE_LEN + 64 - 1] = 1;
109
+ sendData[INIT_TYPE_LEN + 64 + 64 - 1] = 1;
110
+ new DataView(sendData.buffer).setUint32(INIT_TYPE_LEN + 64 + 64, 1, false);
111
+ return sendData;
112
+ }
113
+
114
+ function buildInit1Step4Packet(crypt: Crypt, data: Uint8Array): Uint8Array | null {
115
+ if (data.length !== INIT_TYPE_LEN + 64 + 64 + 4 + 100) return null;
116
+ // One challenge per connection attempt, whose Crypt is its own. Each answer
117
+ // costs up to a second of synchronous squarings, and a second would choose a
118
+ // new alpha behind the clientinitiv already sent. The same challenge again
119
+ // means the server did not get our answer, so it gets the same answer,
120
+ // without solving anything; a different one gets none.
121
+ const answered = answeredChallenges.get(crypt);
122
+ if (answered !== undefined) {
123
+ return Buffer.from(answered.challenge).equals(Buffer.from(data)) ? answered.answer : null;
124
+ }
125
+
126
+ const level = new DataView(data.buffer, data.byteOffset).getUint32(1 + 128, false);
127
+ const solveStarted = performance.now();
128
+ const y = crypt.solveRsaChallenge(data, 1, level);
129
+ const durationMs = performance.now() - solveStarted;
130
+
131
+ const alphaB64 = Buffer.from(crypt.newAlpha()).toString("base64");
132
+ const omegaB64 = crypt.identity.publicKey();
133
+
134
+ const cmd = buildCommandOrdered("clientinitiv", [
135
+ ["alpha", alphaB64],
136
+ ["omega", omegaB64],
137
+ ["ot", "1"],
138
+ ["ip", ""],
139
+ ]);
140
+ const cmdBytes = Buffer.from(cmd);
141
+
142
+ const sendData = new Uint8Array(INIT_VERSION_LEN + INIT_TYPE_LEN + 232 + 64 + cmdBytes.length);
143
+ const view = new DataView(sendData.buffer);
144
+ view.setUint32(0, INIT_VERSION, false);
145
+ sendData[4] = 0x04;
146
+ sendData.set(data.slice(1, 233), 5);
147
+ sendData.set(y.slice(0, 64), 5 + 232);
148
+ sendData.set(cmdBytes, 5 + 232 + 64);
149
+
150
+ answeredChallenges.set(crypt, {
151
+ challenge: Uint8Array.from(data),
152
+ answer: sendData,
153
+ level,
154
+ durationMs,
155
+ });
156
+ return sendData;
157
+ }
@@ -0,0 +1,48 @@
1
+ import type { KeyObject } from "node:crypto";
2
+ import type { Crypt } from "../crypto/crypt.js";
3
+ import { importPublicKey } from "../crypto/identity.js";
4
+ import { verifySign, getSharedSecret2 } from "../crypto/primitives.js";
5
+ import { parseLicenses } from "./license.js";
6
+ import { HandshakeError } from "../errors.js";
7
+
8
+ /**
9
+ * CryptoInit2 performs the second stage of crypto initialization (Ed25519 ECDH).
10
+ * Mirrors Go's handshake.CryptoInit2.
11
+ */
12
+ export function cryptoInit2(
13
+ crypt: Crypt,
14
+ license: string,
15
+ omega: string,
16
+ proof: string,
17
+ beta: string,
18
+ privateKey: Uint8Array,
19
+ ): void {
20
+ if (crypt.alpha.length === 0) {
21
+ throw new HandshakeError("initivexpand2 arrived before clientinitiv was sent");
22
+ }
23
+
24
+ const licenseBytes = Buffer.from(license, "base64");
25
+ const omegaBytes = Buffer.from(omega, "base64");
26
+ const proofBytes = Buffer.from(proof, "base64");
27
+ const betaBytes = Buffer.from(beta, "base64");
28
+
29
+ let serverPubKey: KeyObject;
30
+ try {
31
+ serverPubKey = importPublicKey(omegaBytes);
32
+ } catch (error) {
33
+ throw new HandshakeError("the server's public key is invalid", { cause: error });
34
+ }
35
+ if (!verifySign(serverPubKey, licenseBytes, proofBytes)) {
36
+ throw new HandshakeError("the server's license proof is not valid");
37
+ }
38
+
39
+ let sharedSecret: Uint8Array;
40
+ try {
41
+ const key = parseLicenses(licenseBytes).deriveKey();
42
+ sharedSecret = getSharedSecret2(key, privateKey);
43
+ } catch (error) {
44
+ throw new HandshakeError("the server's license is invalid", { cause: error });
45
+ }
46
+
47
+ crypt.setSharedSecret(crypt.alpha, betaBytes, sharedSecret);
48
+ }
@@ -0,0 +1,3 @@
1
+ export { processInit1, INIT_VERSION } from "./crypt-handshake.js";
2
+ export { cryptoInit2 } from "./crypt-init2.js";
3
+ export { LicenseChain, parseLicenses } from "./license.js";
@@ -0,0 +1,208 @@
1
+ import { hash512 } from "../crypto/identity.js";
2
+ import { clampScalar, scalarMultFull, bytesToBigIntLE } from "../crypto/primitives.js";
3
+ import { ed25519 } from "@noble/curves/ed25519.js";
4
+
5
+ // The root key is a 33-byte Ed25519 point
6
+ const LICENSE_ROOT_KEY = new Uint8Array([
7
+ 0xcd, 0x0d, 0xe2, 0xae, 0xd4, 0x63, 0x45, 0x50, 0x9a, 0x7e, 0x3c, 0xfd, 0x8f, 0x68, 0xb3, 0xdc,
8
+ 0x75, 0x55, 0xb2, 0x9d, 0xcc, 0xec, 0x73, 0xcd, 0x18, 0x75, 0x0f, 0x99, 0x38, 0x12, 0x40, 0x8a,
9
+ ]);
10
+
11
+ const enum LicenseBlockType {
12
+ Intermediate = 0,
13
+ Server = 2,
14
+ Ts5Server = 8,
15
+ Ephemeral = 32,
16
+ }
17
+
18
+ interface LicenseBlock {
19
+ readonly key: Uint8Array; // 32-byte Ed25519 point
20
+ readonly hash: Uint8Array; // SHA-512 truncated to 32 bytes
21
+ properties: Uint8Array[];
22
+ issuer: string;
23
+ notValidBefore: Date;
24
+ notValidAfter: Date;
25
+ blockType: LicenseBlockType;
26
+ serverType: number;
27
+ }
28
+
29
+ export class LicenseChain {
30
+ readonly blocks: LicenseBlock[];
31
+
32
+ constructor(blocks: LicenseBlock[]) {
33
+ this.blocks = blocks;
34
+ }
35
+
36
+ /**
37
+ * Derive the session key by chaining Ed25519 point arithmetic starting from
38
+ * the root key through each license block.
39
+ */
40
+ deriveKey(): Uint8Array<ArrayBuffer> {
41
+ let round: Uint8Array<ArrayBuffer> = Uint8Array.from(LICENSE_ROOT_KEY);
42
+ for (const block of this.blocks) {
43
+ round = deriveKeyFromBlock(block, round);
44
+ }
45
+ return round;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * The most blocks a license chain may have. Real chains have two or three —
51
+ * root-issued intermediate, server, ephemeral — and every block costs a
52
+ * point multiplication to derive, so a server's chain of thousands would hold
53
+ * the event loop for seconds.
54
+ */
55
+ export const MAX_LICENSE_BLOCKS = 8;
56
+
57
+ export function parseLicenses(data: Uint8Array): LicenseChain {
58
+ if (data.length < 1) throw new Error("license too short");
59
+ if (data[0] !== 1) throw new Error("unsupported license version");
60
+
61
+ let remaining = data.slice(1);
62
+ const blocks: LicenseBlock[] = [];
63
+
64
+ while (remaining.length > 0) {
65
+ if (blocks.length === MAX_LICENSE_BLOCKS) {
66
+ throw new Error(`license chain longer than ${MAX_LICENSE_BLOCKS} blocks`);
67
+ }
68
+ const { block, consumed } = parseLicenseBlock(remaining);
69
+ blocks.push(block);
70
+ remaining = remaining.slice(consumed);
71
+ }
72
+
73
+ return new LicenseChain(blocks);
74
+ }
75
+
76
+ function parseLicenseBlock(data: Uint8Array): { block: LicenseBlock; consumed: number } {
77
+ const MIN_BLOCK_LEN = 42;
78
+ if (data.length < MIN_BLOCK_LEN) throw new Error("license too short");
79
+ if (data[0] !== 0) throw new Error(`wrong key kind in license: ${data[0]}`);
80
+
81
+ const blockType = data[33] as LicenseBlockType;
82
+
83
+ // Timestamps are seconds since Unix epoch offset by 0x50e22700
84
+ const UNIX_OFFSET = 0x50e22700;
85
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
86
+ const notValidBefore = new Date((view.getUint32(34, false) + UNIX_OFFSET) * 1000);
87
+ const notValidAfter = new Date((view.getUint32(38, false) + UNIX_OFFSET) * 1000);
88
+
89
+ if (notValidAfter < notValidBefore) {
90
+ throw new Error("license times are invalid");
91
+ }
92
+
93
+ const key = Uint8Array.from(data.slice(1, 33));
94
+
95
+ const { payload, payloadRead } = parseBlockPayload(blockType, data, MIN_BLOCK_LEN);
96
+
97
+ const allLen = MIN_BLOCK_LEN + payloadRead;
98
+ if (allLen > data.length) throw new Error("license block longer than the license");
99
+ const hashInput = data.slice(1, allLen);
100
+ const hashFull = hash512(Uint8Array.from(hashInput));
101
+ const hash = Uint8Array.from(hashFull.slice(0, 32));
102
+
103
+ return {
104
+ block: {
105
+ key,
106
+ hash,
107
+ properties: payload.properties,
108
+ issuer: payload.issuer,
109
+ notValidBefore,
110
+ notValidAfter,
111
+ blockType,
112
+ serverType: payload.serverType,
113
+ },
114
+ consumed: allLen,
115
+ };
116
+ }
117
+
118
+ interface BlockPayload {
119
+ issuer: string;
120
+ serverType: number;
121
+ properties: Uint8Array[];
122
+ read: number;
123
+ }
124
+
125
+ function parseBlockPayload(
126
+ blockType: LicenseBlockType,
127
+ data: Uint8Array,
128
+ minBlockLen: number,
129
+ ): { payload: BlockPayload; payloadRead: number } {
130
+ switch (blockType) {
131
+ case LicenseBlockType.Intermediate: {
132
+ const { str, read } = readNullString(data.slice(46));
133
+ return {
134
+ payload: { issuer: str, serverType: 0, properties: [], read: 5 + read },
135
+ payloadRead: 5 + read,
136
+ };
137
+ }
138
+ case LicenseBlockType.Server: {
139
+ const { str, read } = readNullString(data.slice(47));
140
+ return {
141
+ payload: { issuer: str, serverType: data[42] ?? 0, properties: [], read: 6 + read },
142
+ payloadRead: 6 + read,
143
+ };
144
+ }
145
+ case LicenseBlockType.Ts5Server: {
146
+ const propCount = data[43] !== undefined ? data[43] : 0;
147
+ let pos = 44;
148
+ const properties: Uint8Array[] = [];
149
+ for (let i = 0; i < propCount; i++) {
150
+ if (pos >= data.length) throw new Error("license too short");
151
+ const propLen = data[pos++]!;
152
+ if (pos + propLen > data.length) throw new Error("license too short");
153
+ properties.push(Uint8Array.from(data.slice(pos, pos + propLen)));
154
+ pos += propLen;
155
+ }
156
+ return {
157
+ payload: { issuer: "", serverType: data[42] ?? 0, properties, read: pos - minBlockLen },
158
+ payloadRead: pos - minBlockLen,
159
+ };
160
+ }
161
+ case LicenseBlockType.Ephemeral:
162
+ return { payload: { issuer: "", serverType: 0, properties: [], read: 0 }, payloadRead: 0 };
163
+ default:
164
+ throw new Error(`invalid license block type: ${String(blockType)}`);
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Scan for a null-terminated string and return its content and the index
170
+ * of the null byte (NOT including it in the count, matching Go behaviour).
171
+ * The parent formulas (`5 + read`, `6 + read`) already add 1 for the null.
172
+ */
173
+ function readNullString(data: Uint8Array): { str: string; read: number } {
174
+ for (let i = 0; i < data.length; i++) {
175
+ if (data[i] === 0) {
176
+ return { str: new TextDecoder().decode(data.slice(0, i)), read: i };
177
+ }
178
+ }
179
+ throw new Error("non-null-terminated issuer string");
180
+ }
181
+
182
+ /**
183
+ * Derive the next key given a parent key and a license block.
184
+ * Mirrors Go's licenseBlock.deriveKey.
185
+ */
186
+ function deriveKeyFromBlock(block: LicenseBlock, parent: Uint8Array): Uint8Array<ArrayBuffer> {
187
+ const scalarBytes = Uint8Array.from(block.hash);
188
+ clampScalar(scalarBytes);
189
+
190
+ // Use raw scalar WITHOUT reducing mod n — NaCl's ge_scalarmult_vartime
191
+ // uses the raw 256-bit scalar. Reducing mod n changes the result when
192
+ // the point has a small-order component (Ed25519 cofactor = 8).
193
+ const scalar = bytesToBigIntLE(scalarBytes);
194
+
195
+ const pub = ed25519.Point.fromBytes(block.key);
196
+ const negPub = pub.negate();
197
+
198
+ const par = ed25519.Point.fromBytes(parent);
199
+ const negPar = par.negate();
200
+
201
+ const res = scalarMultFull(negPub, scalar).add(negPar);
202
+ const raw = res.toBytes();
203
+ const final = new Uint8Array(raw.length);
204
+ final.set(raw);
205
+ final[31] = (final[31] !== undefined ? final[31] : 0) ^ 0x80;
206
+
207
+ return final;
208
+ }
@@ -0,0 +1,150 @@
1
+ import { createHash } from "node:crypto";
2
+ import { buildCommandOrdered, buildCommand } from "../core/internal.js";
3
+ import { sign, generateTemporaryKey } from "./crypto/primitives.js";
4
+ import { identityPrivateKey } from "./crypto/identity.js";
5
+ import { parseServerCapabilities } from "./capabilities.js";
6
+ import { cryptoInit2 } from "./handshake/crypt-init2.js";
7
+ import { PacketType } from "./transport/packet.js";
8
+ import type { ClientVersion } from "@honeybbq/teamspeak";
9
+ import type { ClientInternals } from "./client.js";
10
+
11
+ /** Handle the `clientinitiv` message (P-256 based crypto path). */
12
+ export function handleHandshakeInitIV(
13
+ client: ClientInternals,
14
+ params: Record<string, string>,
15
+ ): void {
16
+ const alpha = params["alpha"] ?? "";
17
+ const beta = params["beta"] ?? "";
18
+ const omega = params["omega"] ?? "";
19
+
20
+ client.handler.receivedFinalInitAck();
21
+ client.crypt.initCrypto(alpha, beta, omega);
22
+ client.markHandshakeDone(omega);
23
+ client.logger.info("crypto initialized (P-256 path), sending clientinit");
24
+ sendClientInit(client);
25
+ }
26
+
27
+ /** Handle the `initivexpand2` message (Ed25519 / TS3 crypto path). */
28
+ export function handleHandshakeExpand2(
29
+ client: ClientInternals,
30
+ params: Record<string, string>,
31
+ ): void {
32
+ client.logger.info("received initivexpand2");
33
+ client.handler.receivedFinalInitAck();
34
+
35
+ const license = params["l"] ?? "";
36
+ const omega = params["omega"] ?? "";
37
+ const proof = params["proof"] ?? "";
38
+ const beta = params["beta"] ?? "";
39
+
40
+ const privateKey = sendClientEkPacket(client, beta);
41
+ cryptoInit2(client.crypt, license, omega, proof, beta, privateKey);
42
+ client.markHandshakeDone(omega);
43
+ sendClientInit(client);
44
+ }
45
+
46
+ /** Handle `initserver` — marks the client as connected. */
47
+ export function handleInitServer(client: ClientInternals, params: Record<string, string>): void {
48
+ const idStr = params["aclid"] ?? params["clid"] ?? "";
49
+ const clid = idStr ? parseInt(idStr, 10) : 0;
50
+
51
+ if (clid > 0) {
52
+ client.clid = clid;
53
+ client.handler.setClientID(clid);
54
+ }
55
+
56
+ // initserver is the only place the server names itself, and it determines how
57
+ // the server hashes our public key into client_unique_identifier: SHA-1 on
58
+ // TS3, SHA-256 on TS6. Recording the whole capability set here keeps callers
59
+ // from re-deriving it, or assuming one server generation.
60
+ client.capabilities = parseServerCapabilities(params);
61
+
62
+ client.logger.info("connected to server", { selfId: client.clid });
63
+ client.markConnected(params);
64
+
65
+ // Inform the server about mute state
66
+ setImmediate(() => {
67
+ const updateCmd = buildCommand("clientupdate", {
68
+ client_input_muted: "0",
69
+ client_output_muted: "0",
70
+ });
71
+ client.execNoWait(updateCmd).catch(() => {});
72
+ });
73
+ }
74
+
75
+ function sendClientEkPacket(client: ClientInternals, beta: string): Uint8Array {
76
+ const [publicKey, privateKey] = generateTemporaryKey();
77
+ const ekBase64 = Buffer.from(publicKey).toString("base64");
78
+ const clientProof = buildClientEkProof(client, publicKey, beta);
79
+
80
+ const clientEk = buildCommandOrdered("clientek", [
81
+ ["ek", ekBase64],
82
+ ["proof", clientProof],
83
+ ]);
84
+ client.handler.sendPacket(PacketType.Command, Buffer.from(clientEk), 0);
85
+ return privateKey;
86
+ }
87
+
88
+ function buildClientEkProof(client: ClientInternals, publicKey: Uint8Array, beta: string): string {
89
+ const betaBytes = Buffer.from(beta, "base64");
90
+ const toSign = new Uint8Array(86);
91
+ toSign.set(publicKey.subarray(0, 32));
92
+ toSign.set(betaBytes.subarray(0, Math.min(54, betaBytes.length)), 32);
93
+ const sig = sign(identityPrivateKey(client.crypt.identity), toSign);
94
+ return Buffer.from(sig).toString("base64");
95
+ }
96
+
97
+ /**
98
+ * Encode a password field for the command protocol.
99
+ *
100
+ * TeamSpeak expects password fields as base64(sha1(password)) — server and
101
+ * channel passwords during the handshake, the `clientmove` channel password,
102
+ * and the `cpw` field of the file transfer commands. Sending the plain text
103
+ * instead makes the server reject correct passwords with error 781.
104
+ */
105
+ export function prepareClientPassword(password: string): string {
106
+ if (password === "") return "";
107
+ return createHash("sha1").update(password).digest().toString("base64");
108
+ }
109
+
110
+ /**
111
+ * The version `clientinit` sends unless the `version` option says otherwise.
112
+ *
113
+ * A literal on purpose: `@honeybbq/teamspeak/versions` holds over a thousand
114
+ * constants, and the client must not load them. A test holds this equal to the
115
+ * table's `Windows_3_X_X_5680278000`.
116
+ */
117
+ export const defaultClientVersion: ClientVersion = {
118
+ platform: "Windows",
119
+ version: "3.?.? [Build: 5680278000]",
120
+ sign: "DX5NIYLvfJEUjuIbCidnoeozxIDRRkpq3I9vVMBmE9L2qnekOoBzSenkzsg2lC9CMv8K5hkEzhr2TYUYSwUXCg==",
121
+ };
122
+
123
+ export function sendClientInit(client: ClientInternals): void {
124
+ const cmd = buildClientInitCommand(client);
125
+ client.handler.sendPacket(PacketType.Command, Buffer.from(cmd), 0);
126
+ }
127
+
128
+ function buildClientInitCommand(client: ClientInternals): string {
129
+ const pubKeyBase64 = client.crypt.identity.publicKey();
130
+ const initOptions = client.initOptions;
131
+ // HWID should match TS3 client UID format: base64(SHA1(publicKey()))
132
+ const hwid = createHash("sha1").update(pubKeyBase64).digest().toString("base64");
133
+
134
+ return buildCommandOrdered("clientinit", [
135
+ ["client_nickname", client.nickname],
136
+ ["client_version", initOptions.version.version],
137
+ ["client_platform", initOptions.version.platform],
138
+ ["client_input_hardware", "1"],
139
+ ["client_output_hardware", "1"],
140
+ ["client_default_channel", initOptions.defaultChannel],
141
+ ["client_default_channel_password", initOptions.defaultChannelPasswordHash],
142
+ ["client_server_password", initOptions.serverPasswordHash],
143
+ ["client_meta_data", ""],
144
+ ["client_version_sign", initOptions.version.sign],
145
+ ["client_key_offset", String(client.crypt.identity.offset)],
146
+ ["client_nickname_phonetic", ""],
147
+ ["client_default_token", ""],
148
+ ["hwid", hwid],
149
+ ]);
150
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Reports whether `actual` equals `expected` or equals `expected` followed by
3
+ * only digits — the pattern TeamSpeak uses when a nickname is already taken.
4
+ */
5
+ export function isAutoNicknameMatch(expected: string, actual: string): boolean {
6
+ if (actual === expected) return true;
7
+ if (!actual.startsWith(expected)) return false;
8
+ const suffix = actual.slice(expected.length);
9
+ return /^\d+$/.test(suffix);
10
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Report an error from a trace hook the way a throwing event listener's is
3
+ * reported: rethrown on its own, as an uncaught exception.
4
+ */
5
+ function reportHookError(error: unknown): void {
6
+ queueMicrotask(() => {
7
+ throw error;
8
+ });
9
+ }
10
+
11
+ /**
12
+ * Call a trace hook, if there is one, and never let it touch what called it:
13
+ * a throw, or the rejection of a promise it returns, is reported as a
14
+ * throwing listener's is. The info is built only when there is a hook.
15
+ */
16
+ export function callHook<I>(
17
+ hook: ((info: I) => unknown) | undefined,
18
+ owner: object | undefined,
19
+ info: () => I,
20
+ ): void {
21
+ if (hook === undefined) return;
22
+ try {
23
+ const result = hook.call(owner, info());
24
+ if (result !== null && typeof result === "object" && "then" in result) {
25
+ Promise.resolve(result).then(undefined, reportHookError);
26
+ }
27
+ } catch (error) {
28
+ reportHookError(error);
29
+ }
30
+ }