@wumx-labs/noxaeapi-sdk 0.1.0

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.
package/dist/index.js ADDED
@@ -0,0 +1,740 @@
1
+ // src/errors.ts
2
+ var NoxAeApiError = class extends Error {
3
+ status;
4
+ method;
5
+ path;
6
+ body;
7
+ constructor(message, info) {
8
+ super(message);
9
+ this.name = "NoxAeApiError";
10
+ this.status = info.status;
11
+ this.method = info.method;
12
+ this.path = info.path;
13
+ this.body = info.body;
14
+ }
15
+ };
16
+ var NoxAeApiUnauthorizedError = class extends NoxAeApiError {
17
+ constructor(info) {
18
+ super(
19
+ `Unauthorized: the API key was missing or invalid for ${info.method} ${info.path}`,
20
+ info
21
+ );
22
+ this.name = "NoxAeApiUnauthorizedError";
23
+ }
24
+ };
25
+ var NoxAeApiForbiddenError = class extends NoxAeApiError {
26
+ constructor(info) {
27
+ super(
28
+ `Forbidden: this API key does not have permission to call ${info.method} ${info.path}`,
29
+ info
30
+ );
31
+ this.name = "NoxAeApiForbiddenError";
32
+ }
33
+ };
34
+ var NoxAeApiNotFoundError = class extends NoxAeApiError {
35
+ constructor(info) {
36
+ super(`Not found: ${info.method} ${info.path}`, info);
37
+ this.name = "NoxAeApiNotFoundError";
38
+ }
39
+ };
40
+ var NoxAeApiRateLimitError = class extends NoxAeApiError {
41
+ retryAfterMs;
42
+ constructor(info, retryAfterMs) {
43
+ super(
44
+ `Rate limited on ${info.method} ${info.path}${retryAfterMs ? ` \u2014 retry after ${retryAfterMs}ms` : ""}`,
45
+ info
46
+ );
47
+ this.name = "NoxAeApiRateLimitError";
48
+ this.retryAfterMs = retryAfterMs;
49
+ }
50
+ };
51
+ var NoxAeApiServerError = class extends NoxAeApiError {
52
+ constructor(info) {
53
+ super(`Server error (${info.status}) on ${info.method} ${info.path}`, info);
54
+ this.name = "NoxAeApiServerError";
55
+ }
56
+ };
57
+ var NoxAeApiNetworkError = class extends Error {
58
+ method;
59
+ path;
60
+ cause;
61
+ constructor(message, method, path, cause) {
62
+ super(message);
63
+ this.name = "NoxAeApiNetworkError";
64
+ this.method = method;
65
+ this.path = path;
66
+ this.cause = cause;
67
+ }
68
+ };
69
+
70
+ // src/http-engine.ts
71
+ var DEFAULT_RETRY = {
72
+ attempts: 3,
73
+ baseDelayMs: 300,
74
+ maxDelayMs: 5e3
75
+ };
76
+ function sleep(ms) {
77
+ return new Promise((resolve) => setTimeout(resolve, ms));
78
+ }
79
+ function backoffDelay(attempt, opts) {
80
+ const exp = Math.min(opts.maxDelayMs, opts.baseDelayMs * 2 ** attempt);
81
+ return Math.floor(Math.random() * exp);
82
+ }
83
+ var HttpEngine = class {
84
+ baseUrl;
85
+ apiKey;
86
+ timeoutMs;
87
+ retry;
88
+ extraHeaders;
89
+ fetchImpl;
90
+ constructor(options) {
91
+ if (!options.baseUrl) {
92
+ throw new Error("NoxAeApiClient requires a non-empty baseUrl");
93
+ }
94
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
95
+ this.apiKey = options.apiKey;
96
+ this.timeoutMs = options.timeoutMs ?? 1e4;
97
+ this.retry = options.retry === false ? false : { ...DEFAULT_RETRY, ...options.retry ?? {} };
98
+ this.extraHeaders = options.headers ?? {};
99
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
100
+ if (!fetchImpl) {
101
+ throw new Error(
102
+ "No fetch implementation available. Pass `fetchImpl` in options for this runtime."
103
+ );
104
+ }
105
+ this.fetchImpl = fetchImpl;
106
+ }
107
+ buildUrl(path, query) {
108
+ const url = new URL(`${this.baseUrl}/${path.replace(/^\/+/, "")}`);
109
+ if (query) {
110
+ for (const [key, value] of Object.entries(query)) {
111
+ if (value !== void 0 && value !== null) {
112
+ url.searchParams.set(key, String(value));
113
+ }
114
+ }
115
+ }
116
+ return url.toString();
117
+ }
118
+ async request(method, path, opts = {}) {
119
+ const url = this.buildUrl(path, opts.query);
120
+ const maxAttempts = this.retry ? this.retry.attempts : 1;
121
+ let lastError;
122
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
123
+ const controller = new AbortController();
124
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
125
+ try {
126
+ const headers = {
127
+ Accept: "application/json",
128
+ ...this.extraHeaders
129
+ };
130
+ if (this.apiKey) headers["key"] = this.apiKey;
131
+ if (opts.body !== void 0) headers["Content-Type"] = "application/json";
132
+ const response = await this.fetchImpl(url, {
133
+ method,
134
+ headers,
135
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0,
136
+ signal: controller.signal
137
+ });
138
+ clearTimeout(timeout);
139
+ if (response.ok) {
140
+ if (response.status === 204) return void 0;
141
+ const text = await response.text();
142
+ return text ? JSON.parse(text) : void 0;
143
+ }
144
+ const errorBody = await response.text().catch(() => void 0);
145
+ const parsedBody = safeJsonParse(errorBody);
146
+ const info = { status: response.status, method, path, body: parsedBody };
147
+ if (response.status === 401) throw new NoxAeApiUnauthorizedError(info);
148
+ if (response.status === 403) throw new NoxAeApiForbiddenError(info);
149
+ if (response.status === 404) throw new NoxAeApiNotFoundError(info);
150
+ if (response.status === 429) {
151
+ const retryAfterHeader = response.headers.get("Retry-After");
152
+ const retryAfterMs = retryAfterHeader ? parseRetryAfter(retryAfterHeader) : void 0;
153
+ const err = new NoxAeApiRateLimitError(info, retryAfterMs);
154
+ if (this.retry && attempt < maxAttempts - 1) {
155
+ lastError = err;
156
+ await sleep(retryAfterMs ?? backoffDelay(attempt, this.retry));
157
+ continue;
158
+ }
159
+ throw err;
160
+ }
161
+ if (response.status >= 500) {
162
+ const err = new NoxAeApiServerError(info);
163
+ if (this.retry && attempt < maxAttempts - 1) {
164
+ lastError = err;
165
+ await sleep(backoffDelay(attempt, this.retry));
166
+ continue;
167
+ }
168
+ throw err;
169
+ }
170
+ throw new NoxAeApiError(
171
+ `Unexpected status ${response.status} on ${method} ${path}`,
172
+ info
173
+ );
174
+ } catch (err) {
175
+ clearTimeout(timeout);
176
+ if (err instanceof NoxAeApiError) throw err;
177
+ const isAbort = err instanceof Error && err.name === "AbortError";
178
+ const networkErr = new NoxAeApiNetworkError(
179
+ isAbort ? `Request timed out after ${this.timeoutMs}ms: ${method} ${path}` : `Network error on ${method} ${path}: ${err.message}`,
180
+ method,
181
+ path,
182
+ err
183
+ );
184
+ if (this.retry && attempt < maxAttempts - 1) {
185
+ lastError = networkErr;
186
+ await sleep(backoffDelay(attempt, this.retry));
187
+ continue;
188
+ }
189
+ throw networkErr;
190
+ }
191
+ }
192
+ throw lastError instanceof Error ? lastError : new Error("Request failed after retries");
193
+ }
194
+ };
195
+ function safeJsonParse(text) {
196
+ if (!text) return void 0;
197
+ try {
198
+ return JSON.parse(text);
199
+ } catch {
200
+ return text;
201
+ }
202
+ }
203
+ function parseRetryAfter(headerValue) {
204
+ const seconds = Number(headerValue);
205
+ if (!Number.isNaN(seconds)) return seconds * 1e3;
206
+ const date = Date.parse(headerValue);
207
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
208
+ return void 0;
209
+ }
210
+
211
+ // src/modules/players.ts
212
+ var PlayersModule = class {
213
+ constructor(http) {
214
+ this.http = http;
215
+ }
216
+ http;
217
+ /** List currently online players. */
218
+ list() {
219
+ return this.http.request("GET", "players");
220
+ }
221
+ /** List all players the server has ever seen (online + offline). */
222
+ listAll() {
223
+ return this.http.request("GET", "players/all");
224
+ }
225
+ /** Get a single player by UUID (works for online or offline players). */
226
+ get(uuid) {
227
+ return this.http.request("GET", `players/${encodeURIComponent(uuid)}`);
228
+ }
229
+ /** Get a player's inventory in a specific world. */
230
+ getInventory(playerUuid, worldUuid) {
231
+ return this.http.request(
232
+ "GET",
233
+ `players/${encodeURIComponent(playerUuid)}/${encodeURIComponent(worldUuid)}/inventory`
234
+ );
235
+ }
236
+ /** Kick an online player, optionally with a reason. */
237
+ kick(uuid, reason) {
238
+ return this.http.request("POST", `players/${encodeURIComponent(uuid)}/kick`, {
239
+ body: reason ? { reason } : void 0
240
+ });
241
+ }
242
+ /** Ban a player, optionally with a reason and expiration. */
243
+ ban(uuid, reason) {
244
+ return this.http.request("POST", `players/${encodeURIComponent(uuid)}/ban`, {
245
+ body: reason ? { reason } : void 0
246
+ });
247
+ }
248
+ /** Remove a player's ban. */
249
+ unban(uuid) {
250
+ return this.http.request("DELETE", `players/${encodeURIComponent(uuid)}/ban`);
251
+ }
252
+ /** Teleport a player to a location. */
253
+ teleport(uuid, location) {
254
+ return this.http.request("POST", `players/${encodeURIComponent(uuid)}/teleport`, {
255
+ body: location
256
+ });
257
+ }
258
+ /** Change a player's gamemode. */
259
+ setGamemode(uuid, gamemode) {
260
+ return this.http.request("PUT", `players/${encodeURIComponent(uuid)}/gamemode`, {
261
+ body: { gamemode }
262
+ });
263
+ }
264
+ /** Get kill/death/playtime/block stats for a player. */
265
+ getStats(uuid) {
266
+ return this.http.request("GET", `players/${encodeURIComponent(uuid)}/stats`);
267
+ }
268
+ };
269
+
270
+ // src/modules/economy.ts
271
+ var EconomyModule = class {
272
+ constructor(http) {
273
+ this.http = http;
274
+ }
275
+ http;
276
+ /** Get info about the connected economy provider (Impactor on Fabric, Vault on Bukkit/Spigot/Paper). */
277
+ info() {
278
+ return this.http.request("GET", "economy");
279
+ }
280
+ /** Get a player's balance. */
281
+ getBalance(uuid) {
282
+ return this.http.request("GET", `economy/balance/${encodeURIComponent(uuid)}`);
283
+ }
284
+ /** Get the top balances leaderboard. */
285
+ getTopBalance(limit) {
286
+ return this.http.request("GET", "economy/top", {
287
+ query: { limit }
288
+ });
289
+ }
290
+ /** Pay an amount to a player (adds to their balance). */
291
+ pay(uuid, amount) {
292
+ return this.http.request("POST", "economy/pay", { body: { uuid, amount } });
293
+ }
294
+ /** Debit an amount from a player (subtracts from their balance). */
295
+ debit(uuid, amount) {
296
+ return this.http.request("POST", "economy/debit", { body: { uuid, amount } });
297
+ }
298
+ };
299
+
300
+ // src/modules/server.ts
301
+ var ServerModule = class {
302
+ constructor(http) {
303
+ this.http = http;
304
+ }
305
+ http;
306
+ /** Basic liveness check. */
307
+ ping() {
308
+ return this.http.request("GET", "ping");
309
+ }
310
+ /** Get server info: version, MOTD, TPS, health, player counts, etc. */
311
+ info() {
312
+ return this.http.request("GET", "server");
313
+ }
314
+ /**
315
+ * Run a console command on the server.
316
+ * This is a privileged endpoint — requires a write-enabled API key.
317
+ */
318
+ exec(command) {
319
+ return this.http.request("POST", "server/exec", { body: { command } });
320
+ }
321
+ /** List server operators. */
322
+ getOps() {
323
+ return this.http.request("GET", "server/ops");
324
+ }
325
+ /** Grant operator status to a player. */
326
+ opPlayer(uuid) {
327
+ return this.http.request("POST", "server/ops", { body: { uuid } });
328
+ }
329
+ /** Revoke operator status from a player. */
330
+ deopPlayer(uuid) {
331
+ return this.http.request("DELETE", "server/ops", { body: { uuid } });
332
+ }
333
+ /** Get the current whitelist. */
334
+ getWhitelist() {
335
+ return this.http.request("GET", "server/whitelist");
336
+ }
337
+ /** Add a player to the whitelist. */
338
+ addToWhitelist(uuid, name) {
339
+ return this.http.request("POST", "server/whitelist", { body: { uuid, name } });
340
+ }
341
+ /** Remove a player from the whitelist. */
342
+ removeFromWhitelist(uuid) {
343
+ return this.http.request("DELETE", "server/whitelist", { body: { uuid } });
344
+ }
345
+ /**
346
+ * Restart the server.
347
+ * This is a privileged endpoint — requires a write-enabled API key.
348
+ */
349
+ restart() {
350
+ return this.http.request("POST", "server/restart");
351
+ }
352
+ /** Tail the server console log. */
353
+ getLogs(lines) {
354
+ return this.http.request("GET", "server/logs", { query: { lines } });
355
+ }
356
+ /** Get entity counts on the server, optionally scoped to a world. */
357
+ getEntities(world) {
358
+ return this.http.request("GET", "server/entities", { query: { world } });
359
+ }
360
+ /** Get loaded chunk counts, optionally scoped to a world. */
361
+ getChunks(world) {
362
+ return this.http.request("GET", "server/chunks", { query: { world } });
363
+ }
364
+ /** Ban an IP address. */
365
+ banIp(ip, reason) {
366
+ return this.http.request("POST", "server/ban-ip", { body: { ip, reason } });
367
+ }
368
+ /** Get a scoreboard objective's scores by objective name. */
369
+ getObjective(name) {
370
+ return this.http.request("GET", `scoreboard/${encodeURIComponent(name)}`);
371
+ }
372
+ /** List all scoreboard objectives and tracked entries. */
373
+ getScoreboard() {
374
+ return this.http.request("GET", "scoreboard");
375
+ }
376
+ /** Set a score for an entry on an objective. */
377
+ setScore(objective, entry, value) {
378
+ return this.http.request("POST", `scoreboard/${encodeURIComponent(objective)}/score`, {
379
+ body: { entry, value }
380
+ });
381
+ }
382
+ /** Reset (remove) a score for an entry on an objective. */
383
+ resetScore(objective, entry) {
384
+ return this.http.request("DELETE", `scoreboard/${encodeURIComponent(objective)}/score`, {
385
+ body: { entry }
386
+ });
387
+ }
388
+ /** Broadcast a message to every player on the server. */
389
+ broadcast(message) {
390
+ return this.http.request("POST", "chat/broadcast", { body: { message } });
391
+ }
392
+ /** Send a private message to a specific player. */
393
+ tell(uuid, message) {
394
+ return this.http.request("POST", "chat/tell", { body: { uuid, message } });
395
+ }
396
+ };
397
+
398
+ // src/modules/worlds.ts
399
+ var WorldsModule = class {
400
+ constructor(http) {
401
+ this.http = http;
402
+ }
403
+ http;
404
+ /** List all worlds. */
405
+ list() {
406
+ return this.http.request("GET", "worlds");
407
+ }
408
+ /** Save all worlds to disk. */
409
+ saveAll() {
410
+ return this.http.request("POST", "worlds/save");
411
+ }
412
+ /** Get a download link/stream reference for all worlds. */
413
+ downloadAll() {
414
+ return this.http.request("GET", "worlds/download");
415
+ }
416
+ /** Get a single world by UUID. */
417
+ get(uuid) {
418
+ return this.http.request("GET", `worlds/${encodeURIComponent(uuid)}`);
419
+ }
420
+ /** Save a specific world to disk. */
421
+ save(uuid) {
422
+ return this.http.request("POST", `worlds/${encodeURIComponent(uuid)}/save`);
423
+ }
424
+ /** Get a download link/stream reference for a specific world. */
425
+ download(uuid) {
426
+ return this.http.request("GET", `worlds/${encodeURIComponent(uuid)}/download`);
427
+ }
428
+ /** Set the in-game time for a world. */
429
+ setTime(uuid, time) {
430
+ return this.http.request("POST", `worlds/${encodeURIComponent(uuid)}/time`, {
431
+ body: { time }
432
+ });
433
+ }
434
+ /** Set weather (storm/thundering) for a world. */
435
+ setWeather(uuid, weather) {
436
+ return this.http.request("POST", `worlds/${encodeURIComponent(uuid)}/weather`, {
437
+ body: weather
438
+ });
439
+ }
440
+ /** Get entity counts within a specific world. */
441
+ getEntities(uuid) {
442
+ return this.http.request("GET", `worlds/${encodeURIComponent(uuid)}/entities`);
443
+ }
444
+ };
445
+
446
+ // src/modules/plugins.ts
447
+ var PluginsModule = class {
448
+ constructor(http) {
449
+ this.http = http;
450
+ }
451
+ http;
452
+ /** List all installed plugins/mods. */
453
+ list() {
454
+ return this.http.request("GET", "plugins");
455
+ }
456
+ /**
457
+ * Install a plugin from a URL or identifier.
458
+ * This is a privileged endpoint — requires a write-enabled API key.
459
+ */
460
+ install(source) {
461
+ return this.http.request("POST", "plugins", { body: { source } });
462
+ }
463
+ /** Enable a plugin by name. */
464
+ enable(name) {
465
+ return this.http.request("POST", `plugins/${encodeURIComponent(name)}/enable`);
466
+ }
467
+ /** Disable a plugin by name. */
468
+ disable(name) {
469
+ return this.http.request("POST", `plugins/${encodeURIComponent(name)}/disable`);
470
+ }
471
+ };
472
+
473
+ // src/modules/misc.ts
474
+ var AdvancementsModule = class {
475
+ constructor(http) {
476
+ this.http = http;
477
+ }
478
+ http;
479
+ /** List all advancements known to the server. */
480
+ list() {
481
+ return this.http.request("GET", "advancements");
482
+ }
483
+ };
484
+ var PlaceholdersModule = class {
485
+ constructor(http) {
486
+ this.http = http;
487
+ }
488
+ http;
489
+ /**
490
+ * Replace PlaceholderAPI-style placeholders (e.g. "%player_name%") for a
491
+ * player, returning the resolved string.
492
+ */
493
+ replace(uuid, text) {
494
+ return this.http.request("POST", "placeholders/replace", {
495
+ body: { uuid, text }
496
+ });
497
+ }
498
+ };
499
+
500
+ // src/modules/luckperms.ts
501
+ var LuckPermsModule = class {
502
+ constructor(http) {
503
+ this.http = http;
504
+ }
505
+ http;
506
+ /** Get the groups a player belongs to. */
507
+ getPlayerGroups(uuid) {
508
+ return this.http.request("GET", `luckperms/player/${encodeURIComponent(uuid)}/groups`);
509
+ }
510
+ /** Get a player's effective permission nodes. */
511
+ getPlayerPermissions(uuid) {
512
+ return this.http.request(
513
+ "GET",
514
+ `luckperms/player/${encodeURIComponent(uuid)}/permissions`
515
+ );
516
+ }
517
+ /** Add a permission node to a player. */
518
+ addPlayerPermission(uuid, permission, value = true) {
519
+ return this.http.request(
520
+ "POST",
521
+ `luckperms/player/${encodeURIComponent(uuid)}/permission`,
522
+ { body: { permission, value } }
523
+ );
524
+ }
525
+ /** Check whether a player has a given permission. */
526
+ checkPlayerPermission(uuid, permission) {
527
+ return this.http.request(
528
+ "POST",
529
+ `luckperms/player/${encodeURIComponent(uuid)}/check-permission`,
530
+ { body: { permission } }
531
+ );
532
+ }
533
+ /** Remove a permission node from a player. */
534
+ removePlayerPermission(uuid, permission) {
535
+ return this.http.request(
536
+ "DELETE",
537
+ `luckperms/player/${encodeURIComponent(uuid)}/permission`,
538
+ { body: { permission } }
539
+ );
540
+ }
541
+ /** Set a player's primary group. */
542
+ setPlayerGroup(uuid, group) {
543
+ return this.http.request(
544
+ "POST",
545
+ `luckperms/player/${encodeURIComponent(uuid)}/group`,
546
+ { body: { group } }
547
+ );
548
+ }
549
+ /** Remove a group from a player. */
550
+ removePlayerGroup(uuid, groupName) {
551
+ return this.http.request(
552
+ "DELETE",
553
+ `luckperms/player/${encodeURIComponent(uuid)}/group/${encodeURIComponent(groupName)}`
554
+ );
555
+ }
556
+ /** List all known groups. */
557
+ getGroups() {
558
+ return this.http.request("GET", "luckperms/groups");
559
+ }
560
+ /** Get the permissions attached to a specific group. */
561
+ getGroupPermissions(name) {
562
+ return this.http.request("GET", `luckperms/group/${encodeURIComponent(name)}/permissions`);
563
+ }
564
+ };
565
+
566
+ // src/modules/noxauth.ts
567
+ var NoxAuthModule = class {
568
+ constructor(http) {
569
+ this.http = http;
570
+ }
571
+ http;
572
+ /** Get NoxAuth registration/auth info for a player by name. */
573
+ getPlayerAuth(name) {
574
+ return this.http.request("GET", `noxauth/player/${encodeURIComponent(name)}`);
575
+ }
576
+ /** Check whether a password matches a player's stored NoxAuth password. */
577
+ checkPassword(name, password) {
578
+ return this.http.request(
579
+ "POST",
580
+ `noxauth/player/${encodeURIComponent(name)}/check-password`,
581
+ { body: { password } }
582
+ );
583
+ }
584
+ };
585
+
586
+ // src/socket.ts
587
+ function toWsUrl(baseUrl, route, apiKey) {
588
+ const url = new URL(`${baseUrl.replace(/\/+$/, "")}/${route.replace(/^\/+/, "")}`);
589
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
590
+ if (apiKey) url.searchParams.set("key", apiKey);
591
+ return url.toString();
592
+ }
593
+ var NoxAeApiSocket = class {
594
+ constructor(options) {
595
+ this.options = options;
596
+ const impl = options.webSocketImpl ?? globalThis.WebSocket;
597
+ if (!impl) {
598
+ throw new Error(
599
+ "No WebSocket implementation available in this runtime. Pass `webSocketImpl` in options."
600
+ );
601
+ }
602
+ this.wsImpl = impl;
603
+ this.open();
604
+ }
605
+ options;
606
+ socket = null;
607
+ listeners = /* @__PURE__ */ new Map();
608
+ reconnectAttempt = 0;
609
+ closedByUser = false;
610
+ wsImpl;
611
+ open() {
612
+ const url = toWsUrl(this.options.baseUrl, this.options.route ?? "events", this.options.apiKey);
613
+ const socket = new this.wsImpl(url);
614
+ socket.onopen = () => {
615
+ this.reconnectAttempt = 0;
616
+ this.emit("open", void 0);
617
+ };
618
+ socket.onmessage = (event) => {
619
+ const raw = typeof event.data === "string" ? event.data : String(event.data);
620
+ const parsed = safeParse(raw);
621
+ this.emit("message", parsed);
622
+ if (parsed && typeof parsed === "object" && "type" in parsed) {
623
+ const type = parsed.type;
624
+ if (type === "console" || type === "event") this.emit(type, parsed);
625
+ }
626
+ };
627
+ socket.onerror = (event) => {
628
+ this.emit("error", event);
629
+ };
630
+ socket.onclose = () => {
631
+ this.emit("close", void 0);
632
+ if (!this.closedByUser && this.options.autoReconnect !== false) {
633
+ this.scheduleReconnect();
634
+ }
635
+ };
636
+ this.socket = socket;
637
+ }
638
+ scheduleReconnect() {
639
+ const maxDelay = this.options.maxReconnectDelayMs ?? 3e4;
640
+ const delay = Math.min(maxDelay, 500 * 2 ** this.reconnectAttempt);
641
+ this.reconnectAttempt++;
642
+ setTimeout(() => {
643
+ if (!this.closedByUser) this.open();
644
+ }, delay);
645
+ }
646
+ on(event, listener) {
647
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
648
+ this.listeners.get(event).add(listener);
649
+ return () => this.listeners.get(event)?.delete(listener);
650
+ }
651
+ off(event, listener) {
652
+ this.listeners.get(event)?.delete(listener);
653
+ }
654
+ emit(event, payload) {
655
+ for (const listener of this.listeners.get(event) ?? []) listener(payload);
656
+ }
657
+ /** Send a raw payload over the socket, JSON-stringified if not already a string. */
658
+ send(payload) {
659
+ if (!this.socket || this.socket.readyState !== this.wsImpl.OPEN) {
660
+ throw new Error("Cannot send: socket is not open");
661
+ }
662
+ this.socket.send(typeof payload === "string" ? payload : JSON.stringify(payload));
663
+ }
664
+ /** Close the socket and stop reconnecting. */
665
+ close() {
666
+ this.closedByUser = true;
667
+ this.socket?.close();
668
+ }
669
+ };
670
+ function safeParse(text) {
671
+ try {
672
+ return JSON.parse(text);
673
+ } catch {
674
+ return text;
675
+ }
676
+ }
677
+
678
+ // src/client.ts
679
+ var NoxAeApiClient = class _NoxAeApiClient {
680
+ players;
681
+ economy;
682
+ server;
683
+ worlds;
684
+ plugins;
685
+ advancements;
686
+ placeholders;
687
+ /** Only works if LuckPerms is loaded on the target server. */
688
+ luckperms;
689
+ /** Only works if `noxauth.enabled: true` is set in the server config. */
690
+ noxauth;
691
+ http;
692
+ baseUrl;
693
+ apiKey;
694
+ constructor(options) {
695
+ this.http = new HttpEngine(options);
696
+ this.baseUrl = options.baseUrl;
697
+ this.apiKey = options.apiKey;
698
+ this.players = new PlayersModule(this.http);
699
+ this.economy = new EconomyModule(this.http);
700
+ this.server = new ServerModule(this.http);
701
+ this.worlds = new WorldsModule(this.http);
702
+ this.plugins = new PluginsModule(this.http);
703
+ this.advancements = new AdvancementsModule(this.http);
704
+ this.placeholders = new PlaceholdersModule(this.http);
705
+ this.luckperms = new LuckPermsModule(this.http);
706
+ this.noxauth = new NoxAuthModule(this.http);
707
+ }
708
+ /**
709
+ * Build a client from environment variables:
710
+ * `NOXAEAPI_BASE_URL` and `NOXAEAPI_KEY`.
711
+ *
712
+ * This is a convenience for Node-like runtimes; the SDK itself never
713
+ * reads `process.env` implicitly outside of this method, and does not
714
+ * load `.env` files — use a library like `dotenv` in your own app if
715
+ * you want that, then call `NoxAeApiClient.fromEnv()` after it's loaded.
716
+ */
717
+ static fromEnv(overrides = {}) {
718
+ const env = globalThis.process?.env;
719
+ const baseUrl = overrides.baseUrl ?? env?.NOXAEAPI_BASE_URL;
720
+ const apiKey = overrides.apiKey ?? env?.NOXAEAPI_KEY;
721
+ if (!baseUrl) {
722
+ throw new Error(
723
+ "NoxAeApiClient.fromEnv(): NOXAEAPI_BASE_URL is not set and no baseUrl override was given."
724
+ );
725
+ }
726
+ return new _NoxAeApiClient({ ...overrides, baseUrl, apiKey });
727
+ }
728
+ /** Open a WebSocket connection to the server (console tail or event stream). */
729
+ connect(options = {}) {
730
+ return new NoxAeApiSocket({
731
+ baseUrl: this.baseUrl,
732
+ apiKey: this.apiKey,
733
+ ...options
734
+ });
735
+ }
736
+ };
737
+
738
+ export { NoxAeApiClient, NoxAeApiError, NoxAeApiForbiddenError, NoxAeApiNetworkError, NoxAeApiNotFoundError, NoxAeApiRateLimitError, NoxAeApiServerError, NoxAeApiSocket, NoxAeApiUnauthorizedError };
739
+ //# sourceMappingURL=index.js.map
740
+ //# sourceMappingURL=index.js.map