yukimu 2.0.8 → 2.0.9

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 (97) hide show
  1. package/dist/BitrateOptimizer.d.ts +52 -0
  2. package/dist/BitrateOptimizer.d.ts.map +1 -0
  3. package/dist/BitrateOptimizer.js +115 -0
  4. package/dist/BitrateOptimizer.js.map +1 -0
  5. package/dist/ConnectionPool.d.ts +84 -0
  6. package/dist/ConnectionPool.d.ts.map +1 -0
  7. package/dist/ConnectionPool.js +203 -0
  8. package/dist/ConnectionPool.js.map +1 -0
  9. package/dist/Constants.d.ts +45 -0
  10. package/dist/Constants.d.ts.map +1 -0
  11. package/dist/Constants.js +168 -0
  12. package/dist/Constants.js.map +1 -0
  13. package/dist/Logger.d.ts +22 -0
  14. package/dist/Logger.d.ts.map +1 -0
  15. package/dist/Logger.js +74 -0
  16. package/dist/Logger.js.map +1 -0
  17. package/dist/Node.d.ts +61 -0
  18. package/dist/Node.d.ts.map +1 -0
  19. package/dist/Node.js +446 -0
  20. package/dist/Node.js.map +1 -0
  21. package/dist/Player.d.ts +135 -0
  22. package/dist/Player.d.ts.map +1 -0
  23. package/dist/Player.js +530 -0
  24. package/dist/Player.js.map +1 -0
  25. package/dist/Plugin.d.ts +25 -0
  26. package/dist/Plugin.d.ts.map +1 -0
  27. package/dist/Plugin.js +15 -0
  28. package/dist/Plugin.js.map +1 -0
  29. package/dist/Queue.d.ts +89 -0
  30. package/dist/Queue.d.ts.map +1 -0
  31. package/dist/Queue.js +188 -0
  32. package/dist/Queue.js.map +1 -0
  33. package/dist/Resolver.d.ts +68 -0
  34. package/dist/Resolver.d.ts.map +1 -0
  35. package/dist/Resolver.js +361 -0
  36. package/dist/Resolver.js.map +1 -0
  37. package/dist/Rest.d.ts +46 -0
  38. package/dist/Rest.d.ts.map +1 -0
  39. package/dist/Rest.js +246 -0
  40. package/dist/Rest.js.map +1 -0
  41. package/dist/TrackCache.d.ts +41 -0
  42. package/dist/TrackCache.d.ts.map +1 -0
  43. package/dist/TrackCache.js +144 -0
  44. package/dist/TrackCache.js.map +1 -0
  45. package/dist/WsQueue.d.ts +41 -0
  46. package/dist/WsQueue.d.ts.map +1 -0
  47. package/dist/WsQueue.js +116 -0
  48. package/dist/WsQueue.js.map +1 -0
  49. package/dist/Yukimu.d.ts +84 -0
  50. package/dist/Yukimu.d.ts.map +1 -0
  51. package/dist/Yukimu.js +290 -0
  52. package/dist/Yukimu.js.map +1 -0
  53. package/dist/connector/Connector.d.ts +34 -0
  54. package/dist/connector/Connector.d.ts.map +1 -0
  55. package/dist/connector/Connector.js +39 -0
  56. package/dist/connector/Connector.js.map +1 -0
  57. package/dist/connector/DiscordJS.d.ts +27 -0
  58. package/dist/connector/DiscordJS.d.ts.map +1 -0
  59. package/dist/connector/DiscordJS.js +69 -0
  60. package/dist/connector/DiscordJS.js.map +1 -0
  61. package/dist/connector/Eris.d.ts +19 -0
  62. package/dist/connector/Eris.d.ts.map +1 -0
  63. package/dist/connector/Eris.js +62 -0
  64. package/dist/connector/Eris.js.map +1 -0
  65. package/dist/connector/Oceanic.d.ts +19 -0
  66. package/dist/connector/Oceanic.d.ts.map +1 -0
  67. package/dist/connector/Oceanic.js +60 -0
  68. package/dist/connector/Oceanic.js.map +1 -0
  69. package/dist/errors/YukimuError.d.ts +51 -0
  70. package/dist/errors/YukimuError.d.ts.map +1 -0
  71. package/dist/errors/YukimuError.js +105 -0
  72. package/dist/errors/YukimuError.js.map +1 -0
  73. package/dist/index.d.ts +29 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +70 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/plugins/AutoResume.d.ts +25 -0
  78. package/dist/plugins/AutoResume.d.ts.map +1 -0
  79. package/dist/plugins/AutoResume.js +132 -0
  80. package/dist/plugins/AutoResume.js.map +1 -0
  81. package/dist/plugins/AutoplayPlugin.d.ts +35 -0
  82. package/dist/plugins/AutoplayPlugin.d.ts.map +1 -0
  83. package/dist/plugins/AutoplayPlugin.js +111 -0
  84. package/dist/plugins/AutoplayPlugin.js.map +1 -0
  85. package/dist/plugins/InactivityPlugin.d.ts +30 -0
  86. package/dist/plugins/InactivityPlugin.d.ts.map +1 -0
  87. package/dist/plugins/InactivityPlugin.js +86 -0
  88. package/dist/plugins/InactivityPlugin.js.map +1 -0
  89. package/dist/plugins/PlayerMoved.d.ts +29 -0
  90. package/dist/plugins/PlayerMoved.d.ts.map +1 -0
  91. package/dist/plugins/PlayerMoved.js +75 -0
  92. package/dist/plugins/PlayerMoved.js.map +1 -0
  93. package/dist/types.d.ts +309 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/types.js +4 -0
  96. package/dist/types.js.map +1 -0
  97. package/package.json +1 -1
package/dist/Rest.js ADDED
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ /** Yukimu v2.0.0 — REST Client with Retry, Rate Limiting & Circuit Breaker */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.Rest = void 0;
5
+ const YukimuError_1 = require("./errors/YukimuError");
6
+ const Constants_1 = require("./Constants");
7
+ /**
8
+ * REST client for communicating with a Lavalink node's HTTP API.
9
+ *
10
+ * Improvements from v1:
11
+ * - Automatic retry with exponential backoff for 429/5xx errors
12
+ * - Rate limit tracking (respects Retry-After headers)
13
+ * - Request timeout with proper AbortController cleanup
14
+ * - Uses VERSION constant for Client-Name header
15
+ * - Fixed v4.2+ data normalization bug (single track vs array)
16
+ * - Per-path rate limit awareness
17
+ * - Circuit breaker: after N consecutive failures, stop sending requests temporarily
18
+ */
19
+ class Rest {
20
+ node;
21
+ rateLimit = null;
22
+ consecutiveFailures = 0;
23
+ circuitOpenUntil = 0;
24
+ static MAX_CONSECUTIVE_FAILURES = 5;
25
+ static CIRCUIT_OPEN_DURATION = 30_000;
26
+ constructor(node) {
27
+ this.node = node;
28
+ }
29
+ get baseUrl() {
30
+ const protocol = this.node.options.secure ? "https" : "http";
31
+ return `${protocol}://${this.node.options.host}:${this.node.options.port}`;
32
+ }
33
+ get prefix() {
34
+ return this.node.version === 4 ? "/v4" : "";
35
+ }
36
+ get headers() {
37
+ return {
38
+ Authorization: this.node.options.password,
39
+ "User-Id": this.node.manager.options.clientId,
40
+ "Client-Name": Constants_1.CLIENT_NAME,
41
+ "Content-Type": "application/json",
42
+ ...(this.node.version === 3 ? { "Num-Shards": "1" } : {}),
43
+ };
44
+ }
45
+ // ─── Core Request ───────────────────────────────────────────────────────
46
+ async request(method, path, body, retries = Constants_1.DEFAULTS.REST_MAX_RETRIES) {
47
+ // Circuit breaker check
48
+ if (Date.now() < this.circuitOpenUntil) {
49
+ throw new YukimuError_1.RestError(`Circuit breaker open for node "${this.node.options.name}" — too many consecutive failures`, 503, path);
50
+ }
51
+ // Rate limit check
52
+ if (this.rateLimit && this.rateLimit.remaining <= 0 && Date.now() < this.rateLimit.resetAt) {
53
+ const waitMs = this.rateLimit.resetAt - Date.now();
54
+ this.node.manager.logger.debug(`Rate limited on ${path}, waiting ${waitMs}ms`);
55
+ await this.sleep(waitMs);
56
+ }
57
+ const url = `${this.baseUrl}${this.prefix}${path}`;
58
+ const controller = new AbortController();
59
+ const timeout = setTimeout(() => controller.abort(), Constants_1.DEFAULTS.REST_TIMEOUT);
60
+ try {
61
+ const res = await fetch(url, {
62
+ method,
63
+ headers: this.headers,
64
+ body: body !== undefined ? JSON.stringify(body) : undefined,
65
+ signal: controller.signal,
66
+ });
67
+ clearTimeout(timeout);
68
+ // Track rate limit headers
69
+ this.updateRateLimit(res);
70
+ // Handle rate limiting with retry
71
+ if (res.status === 429) {
72
+ const retryAfter = this.parseRetryAfter(res);
73
+ if (retries > 0) {
74
+ this.node.manager.logger.warn(`Rate limited on ${method} ${path}, retrying in ${retryAfter}ms`);
75
+ await this.sleep(retryAfter);
76
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
77
+ return this.request(method, path, body, (retries - 1));
78
+ }
79
+ throw new YukimuError_1.RestError(`Rate limited on ${method} ${path}`, 429, path, retryAfter);
80
+ }
81
+ // Handle server errors with retry
82
+ if (res.status >= 500 && retries > 0) {
83
+ const delay = this.getRetryDelay(Constants_1.DEFAULTS.REST_MAX_RETRIES - retries);
84
+ this.node.manager.logger.warn(`Server error ${res.status} on ${method} ${path}, retrying in ${delay}ms`);
85
+ await this.sleep(delay);
86
+ return this.request(method, path, body, (retries - 1));
87
+ }
88
+ if (!res.ok) {
89
+ const text = await res.text().catch(() => "Unknown error");
90
+ throw new YukimuError_1.RestError(`REST ${res.status} on ${method} ${path}: ${text}`, res.status, path);
91
+ }
92
+ // Reset circuit breaker on success
93
+ this.consecutiveFailures = 0;
94
+ if (res.status === 204)
95
+ return undefined;
96
+ const text = await res.text();
97
+ if (!text)
98
+ return null;
99
+ let parsed;
100
+ try {
101
+ parsed = JSON.parse(text);
102
+ }
103
+ catch {
104
+ return null;
105
+ }
106
+ // Normalize Lavalink v4.2+ response — uses "data" instead of "tracks"
107
+ if (parsed && "data" in parsed && !("tracks" in parsed)) {
108
+ const data = parsed["data"];
109
+ if (Array.isArray(data)) {
110
+ parsed["tracks"] = data;
111
+ }
112
+ else if (data && typeof data === "object") {
113
+ // Single track response — wrap in array
114
+ parsed["tracks"] = [data];
115
+ }
116
+ }
117
+ return parsed;
118
+ }
119
+ catch (err) {
120
+ clearTimeout(timeout);
121
+ // Increment circuit breaker
122
+ this.consecutiveFailures++;
123
+ if (this.consecutiveFailures >= Rest.MAX_CONSECUTIVE_FAILURES) {
124
+ this.circuitOpenUntil = Date.now() + Rest.CIRCUIT_OPEN_DURATION;
125
+ this.node.manager.logger.error(`Circuit breaker opened for node "${this.node.options.name}" after ${this.consecutiveFailures} failures`);
126
+ }
127
+ if (err instanceof YukimuError_1.RestError)
128
+ throw err;
129
+ if (err instanceof Error && err.name === "AbortError") {
130
+ throw new YukimuError_1.RestError(`REST timeout on ${method} ${path}`, 408, path);
131
+ }
132
+ throw err;
133
+ }
134
+ }
135
+ // ─── API Methods ────────────────────────────────────────────────────────
136
+ async loadTracks(identifier) {
137
+ const raw = await this.request("GET", `/loadtracks?identifier=${encodeURIComponent(identifier)}`);
138
+ if (!raw)
139
+ return { loadType: "empty", tracks: [] };
140
+ return this.node.version === 3 ? this.normalizeV3(raw) : this.normalizeV4(raw);
141
+ }
142
+ async decodeTrack(encoded) {
143
+ return this.request("GET", `/decodetrack?encodedTrack=${encodeURIComponent(encoded)}`);
144
+ }
145
+ async decodeTracks(encoded) {
146
+ return this.request("POST", `/decodetracks`, encoded);
147
+ }
148
+ async getInfo() {
149
+ return this.request("GET", "/info");
150
+ }
151
+ async getStats() {
152
+ return this.request("GET", "/stats");
153
+ }
154
+ async updatePlayer(guildId, body, noReplace = false) {
155
+ const path = this.node.version === 4
156
+ ? `/sessions/${this.node.sessionId}/players/${guildId}?noReplace=${noReplace}`
157
+ : `/players/${guildId}`;
158
+ return this.request("PATCH", path, body);
159
+ }
160
+ async destroyPlayer(guildId) {
161
+ const path = this.node.version === 4
162
+ ? `/sessions/${this.node.sessionId}/players/${guildId}`
163
+ : `/players/${guildId}`;
164
+ await this.request("DELETE", path).catch(() => { });
165
+ }
166
+ async updateSession(body) {
167
+ if (this.node.version !== 4 || !this.node.sessionId)
168
+ return;
169
+ return this.request("PATCH", `/sessions/${this.node.sessionId}`, body);
170
+ }
171
+ // ─── Normalization ──────────────────────────────────────────────────────
172
+ normalizeV4(raw) {
173
+ const result = raw;
174
+ if (!result.tracks)
175
+ result.tracks = [];
176
+ return result;
177
+ }
178
+ normalizeV3(raw) {
179
+ const loadType = String(raw["loadType"] ?? "").toLowerCase();
180
+ const rawTracks = raw["tracks"];
181
+ const tracks = Array.isArray(rawTracks) ? rawTracks : [];
182
+ const norm = (items) => items.map((t) => {
183
+ const track = t;
184
+ return {
185
+ encoded: (track["track"] ?? track["encoded"]),
186
+ info: track["info"],
187
+ pluginInfo: (track["pluginInfo"] ?? {}),
188
+ };
189
+ });
190
+ switch (loadType) {
191
+ case "track_loaded":
192
+ return { loadType: "track", tracks: norm(tracks) };
193
+ case "playlist_loaded":
194
+ return {
195
+ loadType: "playlist",
196
+ tracks: norm(tracks),
197
+ playlistInfo: raw["playlistInfo"],
198
+ };
199
+ case "search_result":
200
+ return { loadType: "search", tracks: norm(tracks) };
201
+ case "no_matches":
202
+ return { loadType: "empty", tracks: [] };
203
+ case "load_failed":
204
+ return {
205
+ loadType: "error",
206
+ tracks: [],
207
+ exception: raw["exception"],
208
+ };
209
+ default:
210
+ return { loadType: "empty", tracks: [] };
211
+ }
212
+ }
213
+ // ─── Helpers ────────────────────────────────────────────────────────────
214
+ updateRateLimit(res) {
215
+ const remaining = res.headers.get("x-ratelimit-remaining");
216
+ const reset = res.headers.get("x-ratelimit-reset");
217
+ if (remaining !== null && reset !== null) {
218
+ this.rateLimit = {
219
+ remaining: parseInt(remaining, 10),
220
+ resetAt: parseInt(reset, 10) * 1000,
221
+ };
222
+ }
223
+ }
224
+ parseRetryAfter(res) {
225
+ const retryAfter = res.headers.get("retry-after");
226
+ if (retryAfter) {
227
+ const seconds = parseFloat(retryAfter);
228
+ if (!isNaN(seconds))
229
+ return Math.ceil(seconds * 1000);
230
+ }
231
+ return 5000; // Default 5s
232
+ }
233
+ getRetryDelay(attempt) {
234
+ const base = Constants_1.DEFAULTS.REST_RETRY_BASE_DELAY;
235
+ const max = Constants_1.DEFAULTS.REST_RETRY_MAX_DELAY;
236
+ const delay = Math.min(base * Math.pow(2, attempt), max);
237
+ // Add jitter ±25%
238
+ const jitter = delay * 0.25 * (Math.random() * 2 - 1);
239
+ return Math.round(delay + jitter);
240
+ }
241
+ sleep(ms) {
242
+ return new Promise((resolve) => setTimeout(resolve, ms));
243
+ }
244
+ }
245
+ exports.Rest = Rest;
246
+ //# sourceMappingURL=Rest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Rest.js","sourceRoot":"","sources":["../src/Rest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /** Yukimu v2.0.0 — LRU Track Cache with Stats */
2
+ import type { Track, SearchResult } from "./types";
3
+ /**
4
+ * A TTL-based LRU cache for tracks and search results.
5
+ *
6
+ * Improvements from v1:
7
+ * - LRU eviction when cache exceeds maxSize
8
+ * - Lazy sweep (only sweeps if cache is non-empty)
9
+ * - Cache hit/miss stats for monitoring
10
+ * - Search result caching (not just individual tracks)
11
+ * - Configurable TTL and max size
12
+ */
13
+ export declare class TrackCache {
14
+ private tracks;
15
+ private searches;
16
+ private readonly ttl;
17
+ private readonly maxSize;
18
+ private sweepInterval;
19
+ private _hits;
20
+ private _misses;
21
+ constructor(ttlMs?: number, maxSize?: number);
22
+ set(encoded: string, track: Track): void;
23
+ get(encoded: string): Track | null;
24
+ has(encoded: string): boolean;
25
+ deleteTrack(encoded: string): void;
26
+ setSearch(identifier: string, result: SearchResult): void;
27
+ getSearch(identifier: string): SearchResult | null;
28
+ private startSweep;
29
+ private sweep;
30
+ get stats(): {
31
+ tracks: number;
32
+ searches: number;
33
+ hits: number;
34
+ misses: number;
35
+ hitRate: number;
36
+ };
37
+ clear(): void;
38
+ destroy(): void;
39
+ get size(): number;
40
+ }
41
+ //# sourceMappingURL=TrackCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TrackCache.d.ts","sourceRoot":"","sources":["../src/TrackCache.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAEjD,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQnD;;;;;;;;;GASG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,QAAQ,CAAoD;IACpE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,OAAO,CAAK;gBAER,KAAK,GAAE,MAA2B,EAAE,OAAO,GAAE,MAAgC;IAQlF,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAWxC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAkBlC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI7B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAMlC,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IAazD,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAezD,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,KAAK;IAYb,IAAW,KAAK;;;;;;MAUf;IAIM,KAAK,IAAI,IAAI;IAKb,OAAO,IAAI,IAAI;IAUtB,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ /** Yukimu v2.0.0 — LRU Track Cache with Stats */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.TrackCache = void 0;
5
+ const Constants_1 = require("./Constants");
6
+ /**
7
+ * A TTL-based LRU cache for tracks and search results.
8
+ *
9
+ * Improvements from v1:
10
+ * - LRU eviction when cache exceeds maxSize
11
+ * - Lazy sweep (only sweeps if cache is non-empty)
12
+ * - Cache hit/miss stats for monitoring
13
+ * - Search result caching (not just individual tracks)
14
+ * - Configurable TTL and max size
15
+ */
16
+ class TrackCache {
17
+ tracks = new Map();
18
+ searches = new Map();
19
+ ttl;
20
+ maxSize;
21
+ sweepInterval = null;
22
+ _hits = 0;
23
+ _misses = 0;
24
+ constructor(ttlMs = Constants_1.DEFAULTS.CACHE_TTL, maxSize = Constants_1.DEFAULTS.MAX_CACHE_SIZE) {
25
+ this.ttl = ttlMs;
26
+ this.maxSize = maxSize;
27
+ this.startSweep();
28
+ }
29
+ // ─── Track Cache ────────────────────────────────────────────────────────
30
+ set(encoded, track) {
31
+ // LRU eviction: delete oldest entries if at capacity
32
+ if (this.tracks.size >= this.maxSize && !this.tracks.has(encoded)) {
33
+ const firstKey = this.tracks.keys().next().value;
34
+ if (firstKey !== undefined) {
35
+ this.tracks.delete(firstKey);
36
+ }
37
+ }
38
+ this.tracks.set(encoded, { value: track, expiresAt: Date.now() + this.ttl });
39
+ }
40
+ get(encoded) {
41
+ const entry = this.tracks.get(encoded);
42
+ if (!entry) {
43
+ this._misses++;
44
+ return null;
45
+ }
46
+ if (Date.now() > entry.expiresAt) {
47
+ this.tracks.delete(encoded);
48
+ this._misses++;
49
+ return null;
50
+ }
51
+ this._hits++;
52
+ // Move to end for LRU (delete + re-set)
53
+ this.tracks.delete(encoded);
54
+ this.tracks.set(encoded, entry);
55
+ return entry.value;
56
+ }
57
+ has(encoded) {
58
+ return this.get(encoded) !== null;
59
+ }
60
+ deleteTrack(encoded) {
61
+ this.tracks.delete(encoded);
62
+ }
63
+ // ─── Search Result Cache ────────────────────────────────────────────────
64
+ setSearch(identifier, result) {
65
+ // Only cache successful results
66
+ if (result.loadType === "error")
67
+ return;
68
+ if (this.searches.size >= this.maxSize && !this.searches.has(identifier)) {
69
+ const firstKey = this.searches.keys().next().value;
70
+ if (firstKey !== undefined) {
71
+ this.searches.delete(firstKey);
72
+ }
73
+ }
74
+ this.searches.set(identifier, { value: result, expiresAt: Date.now() + this.ttl });
75
+ }
76
+ getSearch(identifier) {
77
+ const entry = this.searches.get(identifier);
78
+ if (!entry)
79
+ return null;
80
+ if (Date.now() > entry.expiresAt) {
81
+ this.searches.delete(identifier);
82
+ return null;
83
+ }
84
+ // LRU
85
+ this.searches.delete(identifier);
86
+ this.searches.set(identifier, entry);
87
+ return entry.value;
88
+ }
89
+ // ─── Sweep ──────────────────────────────────────────────────────────────
90
+ startSweep() {
91
+ this.sweepInterval = setInterval(() => {
92
+ // Only sweep if there are entries (lazy sweep)
93
+ if (this.tracks.size > 0 || this.searches.size > 0) {
94
+ this.sweep();
95
+ }
96
+ }, Constants_1.DEFAULTS.CACHE_SWEEP_INTERVAL);
97
+ // Prevent the interval from keeping the process alive
98
+ if (this.sweepInterval.unref) {
99
+ this.sweepInterval.unref();
100
+ }
101
+ }
102
+ sweep() {
103
+ const now = Date.now();
104
+ for (const [key, entry] of this.tracks) {
105
+ if (now > entry.expiresAt)
106
+ this.tracks.delete(key);
107
+ }
108
+ for (const [key, entry] of this.searches) {
109
+ if (now > entry.expiresAt)
110
+ this.searches.delete(key);
111
+ }
112
+ }
113
+ // ─── Stats ──────────────────────────────────────────────────────────────
114
+ get stats() {
115
+ return {
116
+ tracks: this.tracks.size,
117
+ searches: this.searches.size,
118
+ hits: this._hits,
119
+ misses: this._misses,
120
+ hitRate: this._hits + this._misses > 0
121
+ ? Math.round((this._hits / (this._hits + this._misses)) * 100)
122
+ : 0,
123
+ };
124
+ }
125
+ // ─── Lifecycle ──────────────────────────────────────────────────────────
126
+ clear() {
127
+ this.tracks.clear();
128
+ this.searches.clear();
129
+ }
130
+ destroy() {
131
+ if (this.sweepInterval) {
132
+ clearInterval(this.sweepInterval);
133
+ this.sweepInterval = null;
134
+ }
135
+ this.clear();
136
+ this._hits = 0;
137
+ this._misses = 0;
138
+ }
139
+ get size() {
140
+ return this.tracks.size;
141
+ }
142
+ }
143
+ exports.TrackCache = TrackCache;
144
+ //# sourceMappingURL=TrackCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TrackCache.js","sourceRoot":"","sources":["../src/TrackCache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /** Yukimu v2.0.0 — WebSocket Message Queue with Timeouts & Priority */
2
+ import WebSocket from "ws";
3
+ /**
4
+ * Manages a WebSocket message queue that buffers messages when the socket
5
+ * is not open and flushes them once it becomes available.
6
+ *
7
+ * Fixes from v1:
8
+ * - Messages are rejected after a configurable timeout (prevents hanging promises)
9
+ * - Max queue size prevents unbounded memory growth
10
+ * - Priority messages (voice updates) are sent first
11
+ * - No duplicate "open" listener — flush is called externally
12
+ */
13
+ export declare class WsQueue {
14
+ private queue;
15
+ private ws;
16
+ private readonly sendTimeout;
17
+ private readonly maxQueueSize;
18
+ constructor(sendTimeout?: 30000, maxQueueSize?: 100);
19
+ /**
20
+ * Set the underlying WebSocket.
21
+ * Unlike v1, does NOT add an "open" listener to avoid race conditions.
22
+ * The caller (Node) is responsible for calling flush() at the right time.
23
+ */
24
+ setSocket(ws: WebSocket | null): void;
25
+ /**
26
+ * Send data through the WebSocket, or queue it if not ready.
27
+ * @param data - The payload to send (will be JSON-serialized)
28
+ * @param priority - If true, message is prepended to the queue (used for voice updates)
29
+ */
30
+ send(data: Record<string, unknown>, priority?: boolean): Promise<void>;
31
+ /**
32
+ * Flush all queued messages. Called by Node after WebSocket is confirmed open.
33
+ */
34
+ flush(): void;
35
+ /**
36
+ * Clear the queue and reject all pending messages.
37
+ */
38
+ clear(): void;
39
+ get size(): number;
40
+ }
41
+ //# sourceMappingURL=WsQueue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WsQueue.d.ts","sourceRoot":"","sources":["../src/WsQueue.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,SAAS,MAAM,IAAI,CAAC;AAW3B;;;;;;;;;GASG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,WAAW,QAA2B,EAAE,YAAY,MAAwB;IAKxF;;;;OAIG;IACI,SAAS,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI;IAI5C;;;;OAIG;IACI,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAuC3E;;OAEG;IACI,KAAK,IAAI,IAAI;IAsBpB;;OAEG;IACI,KAAK,IAAI,IAAI;IAQpB,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ /** Yukimu v2.0.0 — WebSocket Message Queue with Timeouts & Priority */
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.WsQueue = void 0;
8
+ const ws_1 = __importDefault(require("ws"));
9
+ const Constants_1 = require("./Constants");
10
+ /**
11
+ * Manages a WebSocket message queue that buffers messages when the socket
12
+ * is not open and flushes them once it becomes available.
13
+ *
14
+ * Fixes from v1:
15
+ * - Messages are rejected after a configurable timeout (prevents hanging promises)
16
+ * - Max queue size prevents unbounded memory growth
17
+ * - Priority messages (voice updates) are sent first
18
+ * - No duplicate "open" listener — flush is called externally
19
+ */
20
+ class WsQueue {
21
+ queue = [];
22
+ ws = null;
23
+ sendTimeout;
24
+ maxQueueSize;
25
+ constructor(sendTimeout = Constants_1.DEFAULTS.WS_SEND_TIMEOUT, maxQueueSize = Constants_1.DEFAULTS.WS_MAX_QUEUE) {
26
+ this.sendTimeout = sendTimeout;
27
+ this.maxQueueSize = maxQueueSize;
28
+ }
29
+ /**
30
+ * Set the underlying WebSocket.
31
+ * Unlike v1, does NOT add an "open" listener to avoid race conditions.
32
+ * The caller (Node) is responsible for calling flush() at the right time.
33
+ */
34
+ setSocket(ws) {
35
+ this.ws = ws;
36
+ }
37
+ /**
38
+ * Send data through the WebSocket, or queue it if not ready.
39
+ * @param data - The payload to send (will be JSON-serialized)
40
+ * @param priority - If true, message is prepended to the queue (used for voice updates)
41
+ */
42
+ send(data, priority = false) {
43
+ return new Promise((resolve, reject) => {
44
+ const serialized = JSON.stringify(data);
45
+ // If socket is open, send immediately
46
+ if (this.ws?.readyState === ws_1.default.OPEN) {
47
+ this.ws.send(serialized, (err) => {
48
+ if (err)
49
+ reject(err);
50
+ else
51
+ resolve();
52
+ });
53
+ return;
54
+ }
55
+ // Enforce max queue size
56
+ if (this.queue.length >= this.maxQueueSize) {
57
+ reject(new Error(`WsQueue full (${this.maxQueueSize} messages)`));
58
+ return;
59
+ }
60
+ // Set a timeout so the promise doesn't hang forever
61
+ const timeout = setTimeout(() => {
62
+ const idx = this.queue.findIndex((m) => m.timeout === timeout);
63
+ if (idx !== -1) {
64
+ this.queue.splice(idx, 1);
65
+ }
66
+ reject(new Error(`WsQueue send timeout after ${this.sendTimeout}ms`));
67
+ }, this.sendTimeout);
68
+ const msg = { data: serialized, resolve, reject, timeout, priority };
69
+ // Priority messages go to the front of the queue
70
+ if (priority) {
71
+ this.queue.unshift(msg);
72
+ }
73
+ else {
74
+ this.queue.push(msg);
75
+ }
76
+ });
77
+ }
78
+ /**
79
+ * Flush all queued messages. Called by Node after WebSocket is confirmed open.
80
+ */
81
+ flush() {
82
+ if (!this.ws || this.ws.readyState !== ws_1.default.OPEN)
83
+ return;
84
+ const toSend = [...this.queue];
85
+ this.queue = [];
86
+ for (const msg of toSend) {
87
+ clearTimeout(msg.timeout);
88
+ if (this.ws.readyState !== ws_1.default.OPEN) {
89
+ // Socket closed mid-flush — re-queue remaining
90
+ this.queue.unshift(msg);
91
+ return;
92
+ }
93
+ this.ws.send(msg.data, (err) => {
94
+ if (err)
95
+ msg.reject(err);
96
+ else
97
+ msg.resolve();
98
+ });
99
+ }
100
+ }
101
+ /**
102
+ * Clear the queue and reject all pending messages.
103
+ */
104
+ clear() {
105
+ for (const msg of this.queue) {
106
+ clearTimeout(msg.timeout);
107
+ msg.reject(new Error("WsQueue cleared"));
108
+ }
109
+ this.queue = [];
110
+ }
111
+ get size() {
112
+ return this.queue.length;
113
+ }
114
+ }
115
+ exports.WsQueue = WsQueue;
116
+ //# sourceMappingURL=WsQueue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WsQueue.js","sourceRoot":"","sources":["../src/WsQueue.ts"],"names":[],"mappings":"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"}