@honeybbq/teamspeak-client 0.2.1 → 0.2.2

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.
@@ -3,14 +3,11 @@ import { resolveSrv as t } from "node:dns/promises";
3
3
  import { createConnection as n } from "node:net";
4
4
  //#region src/discovery/resolver.ts
5
5
  var r = 41144, i = "https://named.myteamspeak.com/lookup", a = 600 * 1e3, o = class {
6
- #e;
7
- #t = /* @__PURE__ */ new Map();
8
- constructor(t = e) {
9
- this.#e = t;
10
- }
6
+ #e = /* @__PURE__ */ new Map();
7
+ constructor(t = e) {}
11
8
  async resolve(e, t) {
12
9
  if (!e) throw Error("empty address");
13
- let n = this.#n(e);
10
+ let n = this.#t(e);
14
11
  if (n) return n;
15
12
  let { host: r, port: i } = u(e);
16
13
  if (l(r)) return [{
@@ -22,16 +19,16 @@ var r = 41144, i = "https://named.myteamspeak.com/lookup", a = 600 * 1e3, o = cl
22
19
  let e = await s(r, t);
23
20
  if (e) return this.resolve(e, t);
24
21
  }
25
- let a = await this.#i(r, t);
26
- if (a) return this.#r(e, a);
27
- let o = p(r), c = await this.#a(o, r, t);
28
- if (c) return this.#r(e, [{
22
+ let a = await this.#r(r, t);
23
+ if (a) return this.#n(e, a);
24
+ let o = p(r), c = await this.#i(o, r, t);
25
+ if (c) return this.#n(e, [{
29
26
  addr: c,
30
27
  source: "TSDNS-SRV",
31
28
  expiry: /* @__PURE__ */ new Date(0)
32
29
  }]);
33
- let d = await this.#o(o, r, t);
34
- if (d) return this.#r(e, [{
30
+ let d = await this.#a(o, r, t);
31
+ if (d) return this.#n(e, [{
35
32
  addr: d,
36
33
  source: "TSDNS-Direct",
37
34
  expiry: /* @__PURE__ */ new Date(0)
@@ -41,22 +38,22 @@ var r = 41144, i = "https://named.myteamspeak.com/lookup", a = 600 * 1e3, o = cl
41
38
  source: "Direct",
42
39
  expiry: /* @__PURE__ */ new Date(0)
43
40
  }];
44
- return this.#r(e, m);
41
+ return this.#n(e, m);
45
42
  }
46
- #n(e) {
47
- let t = this.#t.get(e);
43
+ #t(e) {
44
+ let t = this.#e.get(e);
48
45
  if (!t || t.length === 0) return null;
49
46
  let n = t[0];
50
47
  return n.expiry.getTime() > 0 && Date.now() > n.expiry.getTime() ? null : t;
51
48
  }
52
- #r(e, t) {
49
+ #n(e, t) {
53
50
  let n = new Date(Date.now() + a), r = t.map((e) => ({
54
51
  ...e,
55
52
  expiry: n
56
53
  }));
57
- return this.#t.set(e, r), r;
54
+ return this.#e.set(e, r), r;
58
55
  }
59
- async #i(e, n) {
56
+ async #r(e, n) {
60
57
  try {
61
58
  let r = await m(t(`_ts3._udp.${e}`), n);
62
59
  return !r || r.length === 0 ? null : r.map((e) => ({
@@ -68,7 +65,7 @@ var r = 41144, i = "https://named.myteamspeak.com/lookup", a = 600 * 1e3, o = cl
68
65
  return null;
69
66
  }
70
67
  }
71
- async #a(e, n, r) {
68
+ async #i(e, n, r) {
72
69
  for (let i of e) try {
73
70
  let e = await m(t(`_tsdns._tcp.${i}`), r);
74
71
  if (!e || e.length === 0) continue;
@@ -81,7 +78,7 @@ var r = 41144, i = "https://named.myteamspeak.com/lookup", a = 600 * 1e3, o = cl
81
78
  }
82
79
  return null;
83
80
  }
84
- async #o(e, t, n) {
81
+ async #a(e, t, n) {
85
82
  for (let i of e) {
86
83
  let e = await c(f(i, String(r)), t, n);
87
84
  if (e) return e;
@@ -162,4 +159,4 @@ function m(e, t) {
162
159
  //#endregion
163
160
  export { o as t };
164
161
 
165
- //# sourceMappingURL=resolver-DDZWomrF.js.map
162
+ //# sourceMappingURL=resolver-Br0QefWx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver-Br0QefWx.js","names":["#cache","#getValidCache","#resolveSRV","#setCache","#resolveTSDNSSRV","#resolveTSDNSDirect"],"sources":["../src/discovery/resolver.ts"],"sourcesContent":["import { resolveSrv } from \"node:dns/promises\";\nimport { createConnection } from \"node:net\";\nimport type { Logger } from \"../types.js\";\nimport type { ResolvedAddr } from \"../types.js\";\nimport { noopLogger } from \"../types.js\";\n\nconst TS_DNS_DEFAULT_PORT = 41144;\nconst NICKNAME_LOOKUP_URL = \"https://named.myteamspeak.com/lookup\";\nconst CACHE_TTL_MS = 10 * 60 * 1000;\n\nexport class Resolver {\n readonly #cache = new Map<string, ResolvedAddr[]>();\n\n constructor(_log: Logger = noopLogger) {}\n\n async resolve(inputAddr: string, signal?: AbortSignal): Promise<ResolvedAddr[]> {\n if (!inputAddr) throw new Error(\"empty address\");\n\n const cached = this.#getValidCache(inputAddr);\n if (cached) return cached;\n\n const { host, port } = splitHostPort(inputAddr);\n\n if (isIpAddress(host)) {\n return [{ addr: joinHostPort(host, port), source: \"Direct\", expiry: new Date(0) }];\n }\n\n // Nickname resolution (no dots → not a domain)\n if (!host.includes(\".\") && host !== \"localhost\") {\n const nickAddr = await resolveNickname(host, signal);\n if (nickAddr) {\n return this.resolve(nickAddr, signal);\n }\n }\n\n // SRV _ts3._udp.<host>\n const srvResults = await this.#resolveSRV(host, signal);\n if (srvResults) return this.#setCache(inputAddr, srvResults);\n\n const domainList = getDomainList(host);\n\n // TSDNS via SRV _tsdns._tcp.<domain>\n const tsdnsSrv = await this.#resolveTSDNSSRV(domainList, host, signal);\n if (tsdnsSrv) {\n return this.#setCache(inputAddr, [\n { addr: tsdnsSrv, source: \"TSDNS-SRV\", expiry: new Date(0) },\n ]);\n }\n\n // TSDNS direct :41144\n const tsdnsDirect = await this.#resolveTSDNSDirect(domainList, host, signal);\n if (tsdnsDirect) {\n return this.#setCache(inputAddr, [\n { addr: tsdnsDirect, source: \"TSDNS-Direct\", expiry: new Date(0) },\n ]);\n }\n\n // Plain DNS fallback\n const fallback: ResolvedAddr[] = [\n { addr: joinHostPort(host, port), source: \"Direct\", expiry: new Date(0) },\n ];\n return this.#setCache(inputAddr, fallback);\n }\n\n #getValidCache(inputAddr: string): ResolvedAddr[] | null {\n const cached = this.#cache.get(inputAddr);\n if (!cached || cached.length === 0) return null;\n const first = cached[0]!;\n if (first.expiry.getTime() > 0 && Date.now() > first.expiry.getTime()) return null;\n return cached;\n }\n\n #setCache(key: string, results: ResolvedAddr[]): ResolvedAddr[] {\n const expiry = new Date(Date.now() + CACHE_TTL_MS);\n const withExpiry = results.map((r) => ({ ...r, expiry }));\n this.#cache.set(key, withExpiry);\n return withExpiry;\n }\n\n async #resolveSRV(host: string, signal?: AbortSignal): Promise<ResolvedAddr[] | null> {\n try {\n const srvs = await withSignal(resolveSrv(`_ts3._udp.${host}`), signal);\n if (!srvs || srvs.length === 0) return null;\n return srvs.map((srv) => ({\n addr: joinHostPort(srv.name.replace(/\\.$/, \"\"), String(srv.port)),\n source: \"SRV\",\n expiry: new Date(0),\n }));\n } catch {\n return null;\n }\n }\n\n async #resolveTSDNSSRV(\n domains: string[],\n queryHost: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n for (const domain of domains) {\n try {\n const srvs = await withSignal(resolveSrv(`_tsdns._tcp.${domain}`), signal);\n if (!srvs || srvs.length === 0) continue;\n for (const srv of srvs) {\n const tsdnsAddr = joinHostPort(srv.name.replace(/\\.$/, \"\"), String(srv.port));\n const result = await queryTSDNS(tsdnsAddr, queryHost, signal);\n if (result) return result;\n }\n } catch {\n continue;\n }\n }\n return null;\n }\n\n async #resolveTSDNSDirect(\n domains: string[],\n queryHost: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n for (const domain of domains) {\n const tsdnsAddr = joinHostPort(domain, String(TS_DNS_DEFAULT_PORT));\n const result = await queryTSDNS(tsdnsAddr, queryHost, signal);\n if (result) return result;\n }\n return null;\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nasync function resolveNickname(nickname: string, signal?: AbortSignal): Promise<string | null> {\n try {\n const url = new URL(NICKNAME_LOOKUP_URL);\n url.searchParams.set(\"name\", nickname);\n const init: RequestInit = signal ? { signal } : {};\n const resp = await fetch(url.toString(), init);\n if (!resp.ok) return null;\n const text = await resp.text();\n const line = text.split(\"\\n\")[0]?.trim();\n return line || null;\n } catch {\n return null;\n }\n}\n\nfunction queryTSDNS(\n tsdnsAddr: string,\n queryHost: string,\n signal?: AbortSignal,\n): Promise<string | null> {\n const [host, portStr] = splitHostPortParts(tsdnsAddr);\n const port = parseInt(portStr, 10);\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n socket.destroy();\n resolve(null);\n }, 3_000);\n\n const socket = createConnection({ host, port, timeout: 2_000 }, () => {\n socket.write(`${queryHost}\\n`);\n });\n\n let buf = \"\";\n socket.on(\"data\", (chunk: Buffer) => {\n buf += chunk.toString();\n const idx = buf.indexOf(\"\\n\");\n if (idx >= 0) {\n clearTimeout(timeout);\n socket.destroy();\n const line = buf.slice(0, idx).trim();\n if (!line || line === \"404\" || line === \"errors\") resolve(null);\n else resolve(line);\n }\n });\n\n socket.on(\"error\", () => {\n clearTimeout(timeout);\n resolve(null);\n });\n\n if (signal) {\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timeout);\n socket.destroy();\n resolve(null);\n },\n { once: true },\n );\n }\n });\n}\n\nfunction isIpAddress(host: string): boolean {\n // Simple IPv4 check; IPv6 would be inside brackets\n return /^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host) || host.startsWith(\"[\");\n}\n\nfunction splitHostPort(addr: string): { host: string; port: string } {\n const lastColon = addr.lastIndexOf(\":\");\n if (lastColon < 0) return { host: addr, port: \"9987\" };\n const afterColon = addr.slice(lastColon + 1);\n if (/^\\d+$/.test(afterColon)) {\n return { host: addr.slice(0, lastColon), port: afterColon };\n }\n return { host: addr, port: \"9987\" };\n}\n\nfunction splitHostPortParts(addr: string): [string, string] {\n const { host, port } = splitHostPort(addr);\n return [host, port];\n}\n\nfunction joinHostPort(host: string, port: string): string {\n if (host.includes(\":\")) return `[${host}]:${port}`;\n return `${host}:${port}`;\n}\n\nfunction getDomainList(host: string): string[] {\n const parts = host.split(\".\");\n const list: string[] = [];\n for (let i = 0; i < parts.length - 1; i++) {\n list.push(parts.slice(i).join(\".\"));\n }\n return list.slice(0, 3);\n}\n\nfunction withSignal<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return promise;\n return Promise.race([\n promise,\n new Promise<T>((_, reject) =>\n signal.addEventListener(\"abort\", () => reject(signal.reason), { once: true }),\n ),\n ]);\n}\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ const e=require(`./types-DrnoCdSW.cjs`);let t=require(`node:dns/promises`),n=require(`node:net`);var r=41144,i=`https://named.myteamspeak.com/lookup`,a=600*1e3,o=class{#e=new Map;constructor(t=e.r){}async resolve(e,t){if(!e)throw Error(`empty address`);let n=this.#t(e);if(n)return n;let{host:r,port:i}=u(e);if(l(r))return[{addr:f(r,i),source:`Direct`,expiry:new Date(0)}];if(!r.includes(`.`)&&r!==`localhost`){let e=await s(r,t);if(e)return this.resolve(e,t)}let a=await this.#r(r,t);if(a)return this.#n(e,a);let o=p(r),c=await this.#i(o,r,t);if(c)return this.#n(e,[{addr:c,source:`TSDNS-SRV`,expiry:new Date(0)}]);let d=await this.#a(o,r,t);if(d)return this.#n(e,[{addr:d,source:`TSDNS-Direct`,expiry:new Date(0)}]);let m=[{addr:f(r,i),source:`Direct`,expiry:new Date(0)}];return this.#n(e,m)}#t(e){let t=this.#e.get(e);if(!t||t.length===0)return null;let n=t[0];return n.expiry.getTime()>0&&Date.now()>n.expiry.getTime()?null:t}#n(e,t){let n=new Date(Date.now()+a),r=t.map(e=>({...e,expiry:n}));return this.#e.set(e,r),r}async#r(e,n){try{let r=await m((0,t.resolveSrv)(`_ts3._udp.${e}`),n);return!r||r.length===0?null:r.map(e=>({addr:f(e.name.replace(/\.$/,``),String(e.port)),source:`SRV`,expiry:new Date(0)}))}catch{return null}}async#i(e,n,r){for(let i of e)try{let e=await m((0,t.resolveSrv)(`_tsdns._tcp.${i}`),r);if(!e||e.length===0)continue;for(let t of e){let e=await c(f(t.name.replace(/\.$/,``),String(t.port)),n,r);if(e)return e}}catch{continue}return null}async#a(e,t,n){for(let i of e){let e=await c(f(i,String(r)),t,n);if(e)return e}return null}};async function s(e,t){try{let n=new URL(i);n.searchParams.set(`name`,e);let r=t?{signal:t}:{},a=await fetch(n.toString(),r);return a.ok&&(await a.text()).split(`
2
+ `)[0]?.trim()||null}catch{return null}}function c(e,t,r){let[i,a]=d(e),o=parseInt(a,10);return new Promise(e=>{let a=setTimeout(()=>{s.destroy(),e(null)},3e3),s=(0,n.createConnection)({host:i,port:o,timeout:2e3},()=>{s.write(`${t}\n`)}),c=``;s.on(`data`,t=>{c+=t.toString();let n=c.indexOf(`
3
+ `);if(n>=0){clearTimeout(a),s.destroy();let t=c.slice(0,n).trim();e(!t||t===`404`||t===`errors`?null:t)}}),s.on(`error`,()=>{clearTimeout(a),e(null)}),r&&r.addEventListener(`abort`,()=>{clearTimeout(a),s.destroy(),e(null)},{once:!0})})}function l(e){return/^\d{1,3}(\.\d{1,3}){3}$/.test(e)||e.startsWith(`[`)}function u(e){let t=e.lastIndexOf(`:`);if(t<0)return{host:e,port:`9987`};let n=e.slice(t+1);return/^\d+$/.test(n)?{host:e.slice(0,t),port:n}:{host:e,port:`9987`}}function d(e){let{host:t,port:n}=u(e);return[t,n]}function f(e,t){return e.includes(`:`)?`[${e}]:${t}`:`${e}:${t}`}function p(e){let t=e.split(`.`),n=[];for(let e=0;e<t.length-1;e++)n.push(t.slice(e).join(`.`));return n.slice(0,3)}function m(e,t){return t?Promise.race([e,new Promise((e,n)=>t.addEventListener(`abort`,()=>n(t.reason),{once:!0}))]):e}Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return o}});
4
+ //# sourceMappingURL=resolver-DDD2FFhD.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver-DDD2FFhD.cjs","names":["#cache","#getValidCache","#resolveSRV","#setCache","#resolveTSDNSSRV","#resolveTSDNSDirect"],"sources":["../src/discovery/resolver.ts"],"sourcesContent":["import { resolveSrv } from \"node:dns/promises\";\nimport { createConnection } from \"node:net\";\nimport type { Logger } from \"../types.js\";\nimport type { ResolvedAddr } from \"../types.js\";\nimport { noopLogger } from \"../types.js\";\n\nconst TS_DNS_DEFAULT_PORT = 41144;\nconst NICKNAME_LOOKUP_URL = \"https://named.myteamspeak.com/lookup\";\nconst CACHE_TTL_MS = 10 * 60 * 1000;\n\nexport class Resolver {\n readonly #cache = new Map<string, ResolvedAddr[]>();\n\n constructor(_log: Logger = noopLogger) {}\n\n async resolve(inputAddr: string, signal?: AbortSignal): Promise<ResolvedAddr[]> {\n if (!inputAddr) throw new Error(\"empty address\");\n\n const cached = this.#getValidCache(inputAddr);\n if (cached) return cached;\n\n const { host, port } = splitHostPort(inputAddr);\n\n if (isIpAddress(host)) {\n return [{ addr: joinHostPort(host, port), source: \"Direct\", expiry: new Date(0) }];\n }\n\n // Nickname resolution (no dots → not a domain)\n if (!host.includes(\".\") && host !== \"localhost\") {\n const nickAddr = await resolveNickname(host, signal);\n if (nickAddr) {\n return this.resolve(nickAddr, signal);\n }\n }\n\n // SRV _ts3._udp.<host>\n const srvResults = await this.#resolveSRV(host, signal);\n if (srvResults) return this.#setCache(inputAddr, srvResults);\n\n const domainList = getDomainList(host);\n\n // TSDNS via SRV _tsdns._tcp.<domain>\n const tsdnsSrv = await this.#resolveTSDNSSRV(domainList, host, signal);\n if (tsdnsSrv) {\n return this.#setCache(inputAddr, [\n { addr: tsdnsSrv, source: \"TSDNS-SRV\", expiry: new Date(0) },\n ]);\n }\n\n // TSDNS direct :41144\n const tsdnsDirect = await this.#resolveTSDNSDirect(domainList, host, signal);\n if (tsdnsDirect) {\n return this.#setCache(inputAddr, [\n { addr: tsdnsDirect, source: \"TSDNS-Direct\", expiry: new Date(0) },\n ]);\n }\n\n // Plain DNS fallback\n const fallback: ResolvedAddr[] = [\n { addr: joinHostPort(host, port), source: \"Direct\", expiry: new Date(0) },\n ];\n return this.#setCache(inputAddr, fallback);\n }\n\n #getValidCache(inputAddr: string): ResolvedAddr[] | null {\n const cached = this.#cache.get(inputAddr);\n if (!cached || cached.length === 0) return null;\n const first = cached[0]!;\n if (first.expiry.getTime() > 0 && Date.now() > first.expiry.getTime()) return null;\n return cached;\n }\n\n #setCache(key: string, results: ResolvedAddr[]): ResolvedAddr[] {\n const expiry = new Date(Date.now() + CACHE_TTL_MS);\n const withExpiry = results.map((r) => ({ ...r, expiry }));\n this.#cache.set(key, withExpiry);\n return withExpiry;\n }\n\n async #resolveSRV(host: string, signal?: AbortSignal): Promise<ResolvedAddr[] | null> {\n try {\n const srvs = await withSignal(resolveSrv(`_ts3._udp.${host}`), signal);\n if (!srvs || srvs.length === 0) return null;\n return srvs.map((srv) => ({\n addr: joinHostPort(srv.name.replace(/\\.$/, \"\"), String(srv.port)),\n source: \"SRV\",\n expiry: new Date(0),\n }));\n } catch {\n return null;\n }\n }\n\n async #resolveTSDNSSRV(\n domains: string[],\n queryHost: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n for (const domain of domains) {\n try {\n const srvs = await withSignal(resolveSrv(`_tsdns._tcp.${domain}`), signal);\n if (!srvs || srvs.length === 0) continue;\n for (const srv of srvs) {\n const tsdnsAddr = joinHostPort(srv.name.replace(/\\.$/, \"\"), String(srv.port));\n const result = await queryTSDNS(tsdnsAddr, queryHost, signal);\n if (result) return result;\n }\n } catch {\n continue;\n }\n }\n return null;\n }\n\n async #resolveTSDNSDirect(\n domains: string[],\n queryHost: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n for (const domain of domains) {\n const tsdnsAddr = joinHostPort(domain, String(TS_DNS_DEFAULT_PORT));\n const result = await queryTSDNS(tsdnsAddr, queryHost, signal);\n if (result) return result;\n }\n return null;\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nasync function resolveNickname(nickname: string, signal?: AbortSignal): Promise<string | null> {\n try {\n const url = new URL(NICKNAME_LOOKUP_URL);\n url.searchParams.set(\"name\", nickname);\n const init: RequestInit = signal ? { signal } : {};\n const resp = await fetch(url.toString(), init);\n if (!resp.ok) return null;\n const text = await resp.text();\n const line = text.split(\"\\n\")[0]?.trim();\n return line || null;\n } catch {\n return null;\n }\n}\n\nfunction queryTSDNS(\n tsdnsAddr: string,\n queryHost: string,\n signal?: AbortSignal,\n): Promise<string | null> {\n const [host, portStr] = splitHostPortParts(tsdnsAddr);\n const port = parseInt(portStr, 10);\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n socket.destroy();\n resolve(null);\n }, 3_000);\n\n const socket = createConnection({ host, port, timeout: 2_000 }, () => {\n socket.write(`${queryHost}\\n`);\n });\n\n let buf = \"\";\n socket.on(\"data\", (chunk: Buffer) => {\n buf += chunk.toString();\n const idx = buf.indexOf(\"\\n\");\n if (idx >= 0) {\n clearTimeout(timeout);\n socket.destroy();\n const line = buf.slice(0, idx).trim();\n if (!line || line === \"404\" || line === \"errors\") resolve(null);\n else resolve(line);\n }\n });\n\n socket.on(\"error\", () => {\n clearTimeout(timeout);\n resolve(null);\n });\n\n if (signal) {\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timeout);\n socket.destroy();\n resolve(null);\n },\n { once: true },\n );\n }\n });\n}\n\nfunction isIpAddress(host: string): boolean {\n // Simple IPv4 check; IPv6 would be inside brackets\n return /^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host) || host.startsWith(\"[\");\n}\n\nfunction splitHostPort(addr: string): { host: string; port: string } {\n const lastColon = addr.lastIndexOf(\":\");\n if (lastColon < 0) return { host: addr, port: \"9987\" };\n const afterColon = addr.slice(lastColon + 1);\n if (/^\\d+$/.test(afterColon)) {\n return { host: addr.slice(0, lastColon), port: afterColon };\n }\n return { host: addr, port: \"9987\" };\n}\n\nfunction splitHostPortParts(addr: string): [string, string] {\n const { host, port } = splitHostPort(addr);\n return [host, port];\n}\n\nfunction joinHostPort(host: string, port: string): string {\n if (host.includes(\":\")) return `[${host}]:${port}`;\n return `${host}:${port}`;\n}\n\nfunction getDomainList(host: string): string[] {\n const parts = host.split(\".\");\n const list: string[] = [];\n for (let i = 0; i < parts.length - 1; i++) {\n list.push(parts.slice(i).join(\".\"));\n }\n return list.slice(0, 3);\n}\n\nfunction withSignal<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return promise;\n return Promise.race([\n promise,\n new Promise<T>((_, reject) =>\n signal.addEventListener(\"abort\", () => reject(signal.reason), { once: true }),\n ),\n ]);\n}\n"],"mappings":"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"}
@@ -1,17 +1,13 @@
1
1
  import type { Readable, Writable } from "node:stream";
2
2
  import type { FileUploadInfo, FileDownloadInfo } from "./types.js";
3
3
  import { FileTransferError, FileTransferTimeoutError } from "./errors.js";
4
- export interface FileTransferTracker {
5
- register(): [
6
- cftid: number,
7
- promise: Promise<FileUploadInfo | FileDownloadInfo | import("./types.js").FileTransferStatusInfo>
8
- ];
9
- unregister(cftid: number): void;
10
- notify(cftid: number, value: FileUploadInfo | FileDownloadInfo | import("./types.js").FileTransferStatusInfo): void;
11
- reset(): void;
12
- }
4
+ type FtNotification = FileUploadInfo | FileDownloadInfo | import("./types.js").FileTransferStatusInfo;
13
5
  export declare class FileTransferTracker {
14
6
  #private;
7
+ register(): [cftid: number, promise: Promise<FtNotification>];
8
+ unregister(cftid: number): void;
9
+ notify(cftid: number, value: FtNotification): void;
10
+ reset(): void;
15
11
  }
16
12
  /**
17
13
  * Open a TCP connection to the TS3 file transfer port and perform the
@@ -1 +1 @@
1
- {"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../src/transfer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAG1E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,IAAI;QACV,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,OAAO,CACd,cAAc,GAAG,gBAAgB,GAAG,OAAO,YAAY,EAAE,sBAAsB,CAChF;KACF,CAAC;IACF,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GAAG,gBAAgB,GAAG,OAAO,YAAY,EAAE,sBAAsB,GACrF,IAAI,CAAC;IACR,KAAK,IAAI,IAAI,CAAC;CACf;AAOD,qBAAa,mBAAmB;;CA2B/B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,UAAU,EAAE,MAAM,CAAC,CAmBpC;AAED,sDAAsD;AACtD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wDAAwD;AACxD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,GACjB,MAAM,CAWR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,MAAM,CASR;AAED,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,CAAC"}
1
+ {"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../src/transfer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAG1E,KAAK,cAAc,GACf,cAAc,GACd,gBAAgB,GAChB,OAAO,YAAY,EAAE,sBAAsB,CAAC;AAEhD,qBAAa,mBAAmB;;IAI9B,QAAQ,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAU7D,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI;IAKlD,KAAK,IAAI,IAAI;CAId;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,UAAU,EAAE,MAAM,CAAC,CAmBpC;AAED,sDAAsD;AACtD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wDAAwD;AACxD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,GACjB,MAAM,CAWR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,MAAM,CASR;AAED,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/transport/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,IAAI,SAAS,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EACL,KAAK,MAAM,EACX,UAAU,EAMX,MAAM,aAAa,CAAC;AAyBrB,qBAAa,aAAa;;IACxB,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC9C,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;gBAyB1C,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAmB;IAKrD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI7B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBpC,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAkC5B,oBAAoB,IAAI,IAAI;IAI5B,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IASpE,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IA+BtD,KAAK,IAAI,IAAI;CAocd"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/transport/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,IAAI,SAAS,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EACL,KAAK,MAAM,EACX,UAAU,EAMX,MAAM,aAAa,CAAC;AAwBrB,qBAAa,aAAa;;IACxB,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC9C,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;gBAyB1C,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAmB;IAKrD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI7B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBpC,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAkC5B,oBAAoB,IAAI,IAAI;IAI5B,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IASpE,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IA+BtD,KAAK,IAAI,IAAI;CAocd"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honeybbq/teamspeak-client",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "TeamSpeak 3 client SDK for Node.js — a clean-room TypeScript port of the Go reference implementation",
5
5
  "keywords": [
6
6
  "client",
@@ -71,14 +71,14 @@
71
71
  "@noble/curves": "^2.0.1"
72
72
  },
73
73
  "devDependencies": {
74
- "@types/node": "^25.5.0",
74
+ "@types/node": "^25.6.0",
75
75
  "oxfmt": "^0.44.0",
76
- "oxlint": "^1.57.0",
76
+ "oxlint": "^1.59.0",
77
77
  "tsx": "^4.21.0",
78
78
  "typescript": "^6.0.2",
79
- "vite": "^8.0.2",
79
+ "vite": "^8.0.8",
80
80
  "vite-plugin-dts": "^4.5.4",
81
- "vitest": "^4.1.1"
81
+ "vitest": "^4.1.4"
82
82
  },
83
83
  "engines": {
84
84
  "node": ">=20.19"
@@ -1 +0,0 @@
1
- {"version":3,"file":"resolver-DDZWomrF.js","names":["#log","#cache","#getValidCache","#resolveSRV","#setCache","#resolveTSDNSSRV","#resolveTSDNSDirect"],"sources":["../src/discovery/resolver.ts"],"sourcesContent":["import { resolveSrv } from \"node:dns/promises\";\nimport { createConnection } from \"node:net\";\nimport type { Logger } from \"../types.js\";\nimport type { ResolvedAddr } from \"../types.js\";\nimport { noopLogger } from \"../types.js\";\n\nconst TS_DNS_DEFAULT_PORT = 41144;\nconst NICKNAME_LOOKUP_URL = \"https://named.myteamspeak.com/lookup\";\nconst CACHE_TTL_MS = 10 * 60 * 1000;\n\nexport class Resolver {\n readonly #log: Logger;\n readonly #cache = new Map<string, ResolvedAddr[]>();\n\n constructor(log: Logger = noopLogger) {\n this.#log = log;\n }\n\n async resolve(inputAddr: string, signal?: AbortSignal): Promise<ResolvedAddr[]> {\n if (!inputAddr) throw new Error(\"empty address\");\n\n const cached = this.#getValidCache(inputAddr);\n if (cached) return cached;\n\n const { host, port } = splitHostPort(inputAddr);\n\n if (isIpAddress(host)) {\n return [{ addr: joinHostPort(host, port), source: \"Direct\", expiry: new Date(0) }];\n }\n\n // Nickname resolution (no dots → not a domain)\n if (!host.includes(\".\") && host !== \"localhost\") {\n const nickAddr = await resolveNickname(host, signal);\n if (nickAddr) {\n return this.resolve(nickAddr, signal);\n }\n }\n\n // SRV _ts3._udp.<host>\n const srvResults = await this.#resolveSRV(host, signal);\n if (srvResults) return this.#setCache(inputAddr, srvResults);\n\n const domainList = getDomainList(host);\n\n // TSDNS via SRV _tsdns._tcp.<domain>\n const tsdnsSrv = await this.#resolveTSDNSSRV(domainList, host, signal);\n if (tsdnsSrv) {\n return this.#setCache(inputAddr, [\n { addr: tsdnsSrv, source: \"TSDNS-SRV\", expiry: new Date(0) },\n ]);\n }\n\n // TSDNS direct :41144\n const tsdnsDirect = await this.#resolveTSDNSDirect(domainList, host, signal);\n if (tsdnsDirect) {\n return this.#setCache(inputAddr, [\n { addr: tsdnsDirect, source: \"TSDNS-Direct\", expiry: new Date(0) },\n ]);\n }\n\n // Plain DNS fallback\n const fallback: ResolvedAddr[] = [\n { addr: joinHostPort(host, port), source: \"Direct\", expiry: new Date(0) },\n ];\n return this.#setCache(inputAddr, fallback);\n }\n\n #getValidCache(inputAddr: string): ResolvedAddr[] | null {\n const cached = this.#cache.get(inputAddr);\n if (!cached || cached.length === 0) return null;\n const first = cached[0]!;\n if (first.expiry.getTime() > 0 && Date.now() > first.expiry.getTime()) return null;\n return cached;\n }\n\n #setCache(key: string, results: ResolvedAddr[]): ResolvedAddr[] {\n const expiry = new Date(Date.now() + CACHE_TTL_MS);\n const withExpiry = results.map((r) => ({ ...r, expiry }));\n this.#cache.set(key, withExpiry);\n return withExpiry;\n }\n\n async #resolveSRV(host: string, signal?: AbortSignal): Promise<ResolvedAddr[] | null> {\n try {\n const srvs = await withSignal(resolveSrv(`_ts3._udp.${host}`), signal);\n if (!srvs || srvs.length === 0) return null;\n return srvs.map((srv) => ({\n addr: joinHostPort(srv.name.replace(/\\.$/, \"\"), String(srv.port)),\n source: \"SRV\",\n expiry: new Date(0),\n }));\n } catch {\n return null;\n }\n }\n\n async #resolveTSDNSSRV(\n domains: string[],\n queryHost: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n for (const domain of domains) {\n try {\n const srvs = await withSignal(resolveSrv(`_tsdns._tcp.${domain}`), signal);\n if (!srvs || srvs.length === 0) continue;\n for (const srv of srvs) {\n const tsdnsAddr = joinHostPort(srv.name.replace(/\\.$/, \"\"), String(srv.port));\n const result = await queryTSDNS(tsdnsAddr, queryHost, signal);\n if (result) return result;\n }\n } catch {\n continue;\n }\n }\n return null;\n }\n\n async #resolveTSDNSDirect(\n domains: string[],\n queryHost: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n for (const domain of domains) {\n const tsdnsAddr = joinHostPort(domain, String(TS_DNS_DEFAULT_PORT));\n const result = await queryTSDNS(tsdnsAddr, queryHost, signal);\n if (result) return result;\n }\n return null;\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nasync function resolveNickname(nickname: string, signal?: AbortSignal): Promise<string | null> {\n try {\n const url = new URL(NICKNAME_LOOKUP_URL);\n url.searchParams.set(\"name\", nickname);\n const init: RequestInit = signal ? { signal } : {};\n const resp = await fetch(url.toString(), init);\n if (!resp.ok) return null;\n const text = await resp.text();\n const line = text.split(\"\\n\")[0]?.trim();\n return line || null;\n } catch {\n return null;\n }\n}\n\nfunction queryTSDNS(\n tsdnsAddr: string,\n queryHost: string,\n signal?: AbortSignal,\n): Promise<string | null> {\n const [host, portStr] = splitHostPortParts(tsdnsAddr);\n const port = parseInt(portStr, 10);\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n socket.destroy();\n resolve(null);\n }, 3_000);\n\n const socket = createConnection({ host, port, timeout: 2_000 }, () => {\n socket.write(`${queryHost}\\n`);\n });\n\n let buf = \"\";\n socket.on(\"data\", (chunk: Buffer) => {\n buf += chunk.toString();\n const idx = buf.indexOf(\"\\n\");\n if (idx >= 0) {\n clearTimeout(timeout);\n socket.destroy();\n const line = buf.slice(0, idx).trim();\n if (!line || line === \"404\" || line === \"errors\") resolve(null);\n else resolve(line);\n }\n });\n\n socket.on(\"error\", () => {\n clearTimeout(timeout);\n resolve(null);\n });\n\n if (signal) {\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timeout);\n socket.destroy();\n resolve(null);\n },\n { once: true },\n );\n }\n });\n}\n\nfunction isIpAddress(host: string): boolean {\n // Simple IPv4 check; IPv6 would be inside brackets\n return /^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host) || host.startsWith(\"[\");\n}\n\nfunction splitHostPort(addr: string): { host: string; port: string } {\n const lastColon = addr.lastIndexOf(\":\");\n if (lastColon < 0) return { host: addr, port: \"9987\" };\n const afterColon = addr.slice(lastColon + 1);\n if (/^\\d+$/.test(afterColon)) {\n return { host: addr.slice(0, lastColon), port: afterColon };\n }\n return { host: addr, port: \"9987\" };\n}\n\nfunction splitHostPortParts(addr: string): [string, string] {\n const { host, port } = splitHostPort(addr);\n return [host, port];\n}\n\nfunction joinHostPort(host: string, port: string): string {\n if (host.includes(\":\")) return `[${host}]:${port}`;\n return `${host}:${port}`;\n}\n\nfunction getDomainList(host: string): string[] {\n const parts = host.split(\".\");\n const list: string[] = [];\n for (let i = 0; i < parts.length - 1; i++) {\n list.push(parts.slice(i).join(\".\"));\n }\n return list.slice(0, 3);\n}\n\nfunction withSignal<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return promise;\n return Promise.race([\n promise,\n new Promise<T>((_, reject) =>\n signal.addEventListener(\"abort\", () => reject(signal.reason), { once: true }),\n ),\n ]);\n}\n"],"mappings":"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"}
@@ -1,4 +0,0 @@
1
- const e=require(`./types-DrnoCdSW.cjs`);let t=require(`node:dns/promises`),n=require(`node:net`);var r=41144,i=`https://named.myteamspeak.com/lookup`,a=600*1e3,o=class{#e;#t=new Map;constructor(t=e.r){this.#e=t}async resolve(e,t){if(!e)throw Error(`empty address`);let n=this.#n(e);if(n)return n;let{host:r,port:i}=u(e);if(l(r))return[{addr:f(r,i),source:`Direct`,expiry:new Date(0)}];if(!r.includes(`.`)&&r!==`localhost`){let e=await s(r,t);if(e)return this.resolve(e,t)}let a=await this.#i(r,t);if(a)return this.#r(e,a);let o=p(r),c=await this.#a(o,r,t);if(c)return this.#r(e,[{addr:c,source:`TSDNS-SRV`,expiry:new Date(0)}]);let d=await this.#o(o,r,t);if(d)return this.#r(e,[{addr:d,source:`TSDNS-Direct`,expiry:new Date(0)}]);let m=[{addr:f(r,i),source:`Direct`,expiry:new Date(0)}];return this.#r(e,m)}#n(e){let t=this.#t.get(e);if(!t||t.length===0)return null;let n=t[0];return n.expiry.getTime()>0&&Date.now()>n.expiry.getTime()?null:t}#r(e,t){let n=new Date(Date.now()+a),r=t.map(e=>({...e,expiry:n}));return this.#t.set(e,r),r}async#i(e,n){try{let r=await m((0,t.resolveSrv)(`_ts3._udp.${e}`),n);return!r||r.length===0?null:r.map(e=>({addr:f(e.name.replace(/\.$/,``),String(e.port)),source:`SRV`,expiry:new Date(0)}))}catch{return null}}async#a(e,n,r){for(let i of e)try{let e=await m((0,t.resolveSrv)(`_tsdns._tcp.${i}`),r);if(!e||e.length===0)continue;for(let t of e){let e=await c(f(t.name.replace(/\.$/,``),String(t.port)),n,r);if(e)return e}}catch{continue}return null}async#o(e,t,n){for(let i of e){let e=await c(f(i,String(r)),t,n);if(e)return e}return null}};async function s(e,t){try{let n=new URL(i);n.searchParams.set(`name`,e);let r=t?{signal:t}:{},a=await fetch(n.toString(),r);return a.ok&&(await a.text()).split(`
2
- `)[0]?.trim()||null}catch{return null}}function c(e,t,r){let[i,a]=d(e),o=parseInt(a,10);return new Promise(e=>{let a=setTimeout(()=>{s.destroy(),e(null)},3e3),s=(0,n.createConnection)({host:i,port:o,timeout:2e3},()=>{s.write(`${t}\n`)}),c=``;s.on(`data`,t=>{c+=t.toString();let n=c.indexOf(`
3
- `);if(n>=0){clearTimeout(a),s.destroy();let t=c.slice(0,n).trim();e(!t||t===`404`||t===`errors`?null:t)}}),s.on(`error`,()=>{clearTimeout(a),e(null)}),r&&r.addEventListener(`abort`,()=>{clearTimeout(a),s.destroy(),e(null)},{once:!0})})}function l(e){return/^\d{1,3}(\.\d{1,3}){3}$/.test(e)||e.startsWith(`[`)}function u(e){let t=e.lastIndexOf(`:`);if(t<0)return{host:e,port:`9987`};let n=e.slice(t+1);return/^\d+$/.test(n)?{host:e.slice(0,t),port:n}:{host:e,port:`9987`}}function d(e){let{host:t,port:n}=u(e);return[t,n]}function f(e,t){return e.includes(`:`)?`[${e}]:${t}`:`${e}:${t}`}function p(e){let t=e.split(`.`),n=[];for(let e=0;e<t.length-1;e++)n.push(t.slice(e).join(`.`));return n.slice(0,3)}function m(e,t){return t?Promise.race([e,new Promise((e,n)=>t.addEventListener(`abort`,()=>n(t.reason),{once:!0}))]):e}Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return o}});
4
- //# sourceMappingURL=resolver-Dey6omBe.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resolver-Dey6omBe.cjs","names":["#log","#cache","#getValidCache","#resolveSRV","#setCache","#resolveTSDNSSRV","#resolveTSDNSDirect"],"sources":["../src/discovery/resolver.ts"],"sourcesContent":["import { resolveSrv } from \"node:dns/promises\";\nimport { createConnection } from \"node:net\";\nimport type { Logger } from \"../types.js\";\nimport type { ResolvedAddr } from \"../types.js\";\nimport { noopLogger } from \"../types.js\";\n\nconst TS_DNS_DEFAULT_PORT = 41144;\nconst NICKNAME_LOOKUP_URL = \"https://named.myteamspeak.com/lookup\";\nconst CACHE_TTL_MS = 10 * 60 * 1000;\n\nexport class Resolver {\n readonly #log: Logger;\n readonly #cache = new Map<string, ResolvedAddr[]>();\n\n constructor(log: Logger = noopLogger) {\n this.#log = log;\n }\n\n async resolve(inputAddr: string, signal?: AbortSignal): Promise<ResolvedAddr[]> {\n if (!inputAddr) throw new Error(\"empty address\");\n\n const cached = this.#getValidCache(inputAddr);\n if (cached) return cached;\n\n const { host, port } = splitHostPort(inputAddr);\n\n if (isIpAddress(host)) {\n return [{ addr: joinHostPort(host, port), source: \"Direct\", expiry: new Date(0) }];\n }\n\n // Nickname resolution (no dots → not a domain)\n if (!host.includes(\".\") && host !== \"localhost\") {\n const nickAddr = await resolveNickname(host, signal);\n if (nickAddr) {\n return this.resolve(nickAddr, signal);\n }\n }\n\n // SRV _ts3._udp.<host>\n const srvResults = await this.#resolveSRV(host, signal);\n if (srvResults) return this.#setCache(inputAddr, srvResults);\n\n const domainList = getDomainList(host);\n\n // TSDNS via SRV _tsdns._tcp.<domain>\n const tsdnsSrv = await this.#resolveTSDNSSRV(domainList, host, signal);\n if (tsdnsSrv) {\n return this.#setCache(inputAddr, [\n { addr: tsdnsSrv, source: \"TSDNS-SRV\", expiry: new Date(0) },\n ]);\n }\n\n // TSDNS direct :41144\n const tsdnsDirect = await this.#resolveTSDNSDirect(domainList, host, signal);\n if (tsdnsDirect) {\n return this.#setCache(inputAddr, [\n { addr: tsdnsDirect, source: \"TSDNS-Direct\", expiry: new Date(0) },\n ]);\n }\n\n // Plain DNS fallback\n const fallback: ResolvedAddr[] = [\n { addr: joinHostPort(host, port), source: \"Direct\", expiry: new Date(0) },\n ];\n return this.#setCache(inputAddr, fallback);\n }\n\n #getValidCache(inputAddr: string): ResolvedAddr[] | null {\n const cached = this.#cache.get(inputAddr);\n if (!cached || cached.length === 0) return null;\n const first = cached[0]!;\n if (first.expiry.getTime() > 0 && Date.now() > first.expiry.getTime()) return null;\n return cached;\n }\n\n #setCache(key: string, results: ResolvedAddr[]): ResolvedAddr[] {\n const expiry = new Date(Date.now() + CACHE_TTL_MS);\n const withExpiry = results.map((r) => ({ ...r, expiry }));\n this.#cache.set(key, withExpiry);\n return withExpiry;\n }\n\n async #resolveSRV(host: string, signal?: AbortSignal): Promise<ResolvedAddr[] | null> {\n try {\n const srvs = await withSignal(resolveSrv(`_ts3._udp.${host}`), signal);\n if (!srvs || srvs.length === 0) return null;\n return srvs.map((srv) => ({\n addr: joinHostPort(srv.name.replace(/\\.$/, \"\"), String(srv.port)),\n source: \"SRV\",\n expiry: new Date(0),\n }));\n } catch {\n return null;\n }\n }\n\n async #resolveTSDNSSRV(\n domains: string[],\n queryHost: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n for (const domain of domains) {\n try {\n const srvs = await withSignal(resolveSrv(`_tsdns._tcp.${domain}`), signal);\n if (!srvs || srvs.length === 0) continue;\n for (const srv of srvs) {\n const tsdnsAddr = joinHostPort(srv.name.replace(/\\.$/, \"\"), String(srv.port));\n const result = await queryTSDNS(tsdnsAddr, queryHost, signal);\n if (result) return result;\n }\n } catch {\n continue;\n }\n }\n return null;\n }\n\n async #resolveTSDNSDirect(\n domains: string[],\n queryHost: string,\n signal?: AbortSignal,\n ): Promise<string | null> {\n for (const domain of domains) {\n const tsdnsAddr = joinHostPort(domain, String(TS_DNS_DEFAULT_PORT));\n const result = await queryTSDNS(tsdnsAddr, queryHost, signal);\n if (result) return result;\n }\n return null;\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nasync function resolveNickname(nickname: string, signal?: AbortSignal): Promise<string | null> {\n try {\n const url = new URL(NICKNAME_LOOKUP_URL);\n url.searchParams.set(\"name\", nickname);\n const init: RequestInit = signal ? { signal } : {};\n const resp = await fetch(url.toString(), init);\n if (!resp.ok) return null;\n const text = await resp.text();\n const line = text.split(\"\\n\")[0]?.trim();\n return line || null;\n } catch {\n return null;\n }\n}\n\nfunction queryTSDNS(\n tsdnsAddr: string,\n queryHost: string,\n signal?: AbortSignal,\n): Promise<string | null> {\n const [host, portStr] = splitHostPortParts(tsdnsAddr);\n const port = parseInt(portStr, 10);\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n socket.destroy();\n resolve(null);\n }, 3_000);\n\n const socket = createConnection({ host, port, timeout: 2_000 }, () => {\n socket.write(`${queryHost}\\n`);\n });\n\n let buf = \"\";\n socket.on(\"data\", (chunk: Buffer) => {\n buf += chunk.toString();\n const idx = buf.indexOf(\"\\n\");\n if (idx >= 0) {\n clearTimeout(timeout);\n socket.destroy();\n const line = buf.slice(0, idx).trim();\n if (!line || line === \"404\" || line === \"errors\") resolve(null);\n else resolve(line);\n }\n });\n\n socket.on(\"error\", () => {\n clearTimeout(timeout);\n resolve(null);\n });\n\n if (signal) {\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timeout);\n socket.destroy();\n resolve(null);\n },\n { once: true },\n );\n }\n });\n}\n\nfunction isIpAddress(host: string): boolean {\n // Simple IPv4 check; IPv6 would be inside brackets\n return /^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host) || host.startsWith(\"[\");\n}\n\nfunction splitHostPort(addr: string): { host: string; port: string } {\n const lastColon = addr.lastIndexOf(\":\");\n if (lastColon < 0) return { host: addr, port: \"9987\" };\n const afterColon = addr.slice(lastColon + 1);\n if (/^\\d+$/.test(afterColon)) {\n return { host: addr.slice(0, lastColon), port: afterColon };\n }\n return { host: addr, port: \"9987\" };\n}\n\nfunction splitHostPortParts(addr: string): [string, string] {\n const { host, port } = splitHostPort(addr);\n return [host, port];\n}\n\nfunction joinHostPort(host: string, port: string): string {\n if (host.includes(\":\")) return `[${host}]:${port}`;\n return `${host}:${port}`;\n}\n\nfunction getDomainList(host: string): string[] {\n const parts = host.split(\".\");\n const list: string[] = [];\n for (let i = 0; i < parts.length - 1; i++) {\n list.push(parts.slice(i).join(\".\"));\n }\n return list.slice(0, 3);\n}\n\nfunction withSignal<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return promise;\n return Promise.race([\n promise,\n new Promise<T>((_, reject) =>\n signal.addEventListener(\"abort\", () => reject(signal.reason), { once: true }),\n ),\n ]);\n}\n"],"mappings":"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"}