lunibee 0.1.0 → 0.1.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.
Files changed (67) hide show
  1. package/README.md +229 -0
  2. package/dist/builders/attachment.d.ts +20 -0
  3. package/dist/builders/commands.d.ts +118 -0
  4. package/dist/builders/components.d.ts +151 -0
  5. package/dist/builders/embed.d.ts +59 -0
  6. package/dist/builders/index.d.ts +5 -0
  7. package/dist/builders/index.js +828 -0
  8. package/dist/builders/index.js.map +13 -0
  9. package/dist/collection/cache.d.ts +37 -0
  10. package/dist/collection/index.d.ts +47 -0
  11. package/dist/collection/index.js +214 -0
  12. package/dist/collection/index.js.map +11 -0
  13. package/dist/core/client-state.d.ts +10 -0
  14. package/dist/core/collector.d.ts +27 -0
  15. package/dist/core/events.d.ts +5 -0
  16. package/dist/core/index.d.ts +111 -0
  17. package/dist/core/index.js +3188 -0
  18. package/dist/core/index.js.map +29 -0
  19. package/dist/core/permissions.d.ts +293 -0
  20. package/dist/formatters/index.d.ts +10 -0
  21. package/dist/formatters/index.js +43 -0
  22. package/dist/formatters/index.js.map +10 -0
  23. package/dist/handlers/index.d.ts +16 -0
  24. package/dist/handlers/index.js +37 -0
  25. package/dist/handlers/index.js.map +10 -0
  26. package/{src/index.ts → dist/index.d.ts} +2 -29
  27. package/dist/index.js +4182 -0
  28. package/dist/index.js.map +41 -0
  29. package/dist/managers/application.d.ts +28 -0
  30. package/dist/managers/base.d.ts +28 -0
  31. package/dist/managers/index.d.ts +96 -0
  32. package/dist/managers/index.js +947 -0
  33. package/dist/managers/index.js.map +22 -0
  34. package/dist/managers/member.d.ts +35 -0
  35. package/dist/managers/message.d.ts +25 -0
  36. package/dist/managers/role.d.ts +34 -0
  37. package/dist/managers/thread.d.ts +14 -0
  38. package/dist/rest/index.d.ts +49 -0
  39. package/dist/rest/index.js +344 -0
  40. package/dist/rest/index.js.map +12 -0
  41. package/dist/rest/routes.d.ts +44 -0
  42. package/dist/rest/types.d.ts +26 -0
  43. package/dist/rest/webhook.d.ts +40 -0
  44. package/dist/sharding/bus.d.ts +22 -0
  45. package/dist/sharding/index.d.ts +34 -0
  46. package/dist/sharding/index.js +732 -0
  47. package/dist/sharding/index.js.map +13 -0
  48. package/dist/structures/base.d.ts +75 -0
  49. package/dist/structures/index.d.ts +39 -0
  50. package/dist/structures/index.js +571 -0
  51. package/dist/structures/index.js.map +14 -0
  52. package/dist/structures/interactions.d.ts +136 -0
  53. package/dist/structures/permissions.d.ts +73 -0
  54. package/dist/structures/resources.d.ts +64 -0
  55. package/dist/types/index.d.ts +535 -0
  56. package/dist/types/index.js +132 -0
  57. package/dist/types/index.js.map +10 -0
  58. package/dist/utils/index.d.ts +6 -0
  59. package/dist/utils/index.js +18 -0
  60. package/dist/utils/index.js.map +10 -0
  61. package/dist/voice/index.d.ts +97 -0
  62. package/dist/voice/index.js +161 -0
  63. package/dist/voice/index.js.map +10 -0
  64. package/dist/ws/index.d.ts +76 -0
  65. package/dist/ws/index.js +582 -0
  66. package/dist/ws/index.js.map +11 -0
  67. package/package.json +113 -28
@@ -0,0 +1,344 @@
1
+ // @bun
2
+ // packages/rest/src/routes.ts
3
+ var Routes = {
4
+ user: () => "/users/@me",
5
+ userById: (userId) => `/users/${snowflake(userId, "User ID")}`,
6
+ guild: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}`,
7
+ guildChannels: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/channels`,
8
+ guildMember: (guildId, userId) => `/guilds/${snowflake(guildId, "Guild ID")}/members/${snowflake(userId, "User ID")}`,
9
+ guildRoles: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/roles`,
10
+ guildRole: (guildId, roleId) => `/guilds/${snowflake(guildId, "Guild ID")}/roles/${snowflake(roleId, "Role ID")}`,
11
+ guildMemberRole: (guildId, userId, roleId) => `/guilds/${snowflake(guildId, "Guild ID")}/members/${snowflake(userId, "User ID")}/roles/${snowflake(roleId, "Role ID")}`,
12
+ guildBans: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/bans`,
13
+ guildBan: (guildId, userId) => `/guilds/${snowflake(guildId, "Guild ID")}/bans/${snowflake(userId, "User ID")}`,
14
+ channel: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}`,
15
+ channelMessages: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/messages`,
16
+ message: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/${snowflake(messageId, "Message ID")}`,
17
+ crosspostMessage: (channelId, messageId) => `${Routes.message(channelId, messageId)}/crosspost`,
18
+ messageReactions: (channelId, messageId, emoji) => `${Routes.message(channelId, messageId)}/reactions/${encodeURIComponent(emoji)}`,
19
+ messageReactionsAll: (channelId, messageId) => `${Routes.message(channelId, messageId)}/reactions`,
20
+ channelPins: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/pins`,
21
+ channelPin: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/pins/${snowflake(messageId, "Message ID")}`,
22
+ messageThread: (channelId, messageId) => `${Routes.message(channelId, messageId)}/threads`,
23
+ channelBulkDelete: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/bulk-delete`,
24
+ channelWebhooks: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/webhooks`,
25
+ channelInvites: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/invites`,
26
+ webhook: (webhookId, token) => `/webhooks/${snowflake(webhookId, "Webhook ID")}${token ? `/${encodeURIComponent(token)}` : ""}`,
27
+ webhookMessage: (webhookId, token, messageId) => `/webhooks/${snowflake(webhookId, "Webhook ID")}/${encodeURIComponent(token)}/messages/${snowflake(messageId, "Message ID")}`,
28
+ applicationCommands: (applicationId) => `/applications/${snowflake(applicationId, "Application ID")}/commands`,
29
+ applicationCommand: (applicationId, commandId) => `/applications/${snowflake(applicationId, "Application ID")}/commands/${snowflake(commandId, "Command ID")}`,
30
+ guildApplicationCommands: (applicationId, guildId) => `/applications/${snowflake(applicationId, "Application ID")}/guilds/${snowflake(guildId, "Guild ID")}/commands`,
31
+ guildApplicationCommand: (applicationId, guildId, commandId) => `/applications/${snowflake(applicationId, "Application ID")}/guilds/${snowflake(guildId, "Guild ID")}/commands/${snowflake(commandId, "Command ID")}`,
32
+ interactionCallback: (interactionId, token) => `/interactions/${snowflake(interactionId, "Interaction ID")}/${encodeURIComponent(token)}/callback`,
33
+ interactionOriginalResponse: (applicationId, token) => `/webhooks/${snowflake(applicationId, "Application ID")}/${encodeURIComponent(token)}/messages/@original`,
34
+ guildActiveThreads: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/threads/active`,
35
+ channelPublicArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads/archived/public`,
36
+ channelPrivateArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads/archived/private`,
37
+ threadMembers: (threadId) => `/channels/${snowflake(threadId, "Thread ID")}/thread-members`,
38
+ guildScheduledEvents: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events`,
39
+ guildScheduledEvent: (guildId, eventId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events/${snowflake(eventId, "Event ID")}`,
40
+ guildAutoModerationRules: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/auto-moderation/rules`,
41
+ guildAutoModerationRule: (guildId, ruleId) => `/guilds/${snowflake(guildId, "Guild ID")}/auto-moderation/rules/${snowflake(ruleId, "Rule ID")}`,
42
+ voiceRegions: () => "/voice/regions",
43
+ gateway: () => "/gateway",
44
+ gatewayBot: () => "/gateway/bot"
45
+ };
46
+ function snowflake(value, field) {
47
+ if (!/^\d{1,20}$/.test(value))
48
+ throw new TypeError(`${field} must be a valid Discord snowflake.`);
49
+ return value;
50
+ }
51
+ // packages/rest/src/webhook.ts
52
+ class WebhookClient {
53
+ id;
54
+ token;
55
+ #rest;
56
+ constructor(options, restOptions) {
57
+ if (options.url) {
58
+ const match = options.url.match(/discord(?:app)?.com\/api\/webhooks\/(\d+)\/([A-Za-z0-9_-]+)/);
59
+ if (!match)
60
+ throw new Error("Invalid Discord webhook URL format.");
61
+ this.id = match[1];
62
+ this.token = match[2];
63
+ } else if (options.id && options.token) {
64
+ this.id = options.id;
65
+ this.token = options.token;
66
+ } else {
67
+ throw new Error("WebhookClient requires either a webhook 'url' or both 'id' and 'token'.");
68
+ }
69
+ this.#rest = new REST(restOptions);
70
+ }
71
+ async send(options) {
72
+ const payload = typeof options === "string" ? { content: options } : options;
73
+ const { thread_id, ...body } = payload;
74
+ const query = thread_id ? `?thread_id=${thread_id}&wait=true` : `?wait=true`;
75
+ const formattedEmbeds = body.embeds?.map((e) => typeof e === "object" && e !== null && ("toJSON" in e) ? e.toJSON() : e);
76
+ return this.#rest.post(`${Routes.webhook(this.id, this.token)}${query}`, {
77
+ ...body,
78
+ embeds: formattedEmbeds
79
+ });
80
+ }
81
+ async editMessage(messageId, options) {
82
+ const payload = typeof options === "string" ? { content: options } : options;
83
+ return this.#rest.patch(Routes.webhookMessage(this.id, this.token, messageId), payload);
84
+ }
85
+ async deleteMessage(messageId) {
86
+ await this.#rest.delete(Routes.webhookMessage(this.id, this.token, messageId));
87
+ }
88
+ }
89
+
90
+ // packages/rest/src/index.ts
91
+ class RESTError extends Error {
92
+ status;
93
+ code;
94
+ errors;
95
+ method;
96
+ path;
97
+ constructor(message, status, code, errors, options = {}) {
98
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
99
+ this.name = "RESTError";
100
+ this.status = status;
101
+ this.code = code;
102
+ this.errors = errors;
103
+ this.method = options.method;
104
+ this.path = options.path;
105
+ }
106
+ }
107
+ function createRetryPolicy(maxRetries = 2) {
108
+ if (!Number.isFinite(maxRetries) || maxRetries < 0)
109
+ throw new TypeError("maxRetries must be a non-negative finite number.");
110
+ return {
111
+ maxRetries: Math.floor(maxRetries),
112
+ shouldRetry(method, status) {
113
+ const normalized = method.toUpperCase();
114
+ return status === 429 || status >= 500 && status <= 599 && ["GET", "HEAD", "PUT", "DELETE"].includes(normalized);
115
+ },
116
+ getDelay(attempt, retryAfter) {
117
+ if (retryAfter !== undefined)
118
+ return Math.max(0, retryAfter * 1000);
119
+ return Math.min(1e4, 500 * 2 ** attempt) + Math.random() * 250;
120
+ }
121
+ };
122
+ }
123
+
124
+ class REST {
125
+ #baseURL;
126
+ #timeout;
127
+ #retryPolicy;
128
+ #token;
129
+ #globalResetAt = 0;
130
+ #buckets = new Map;
131
+ #routeBuckets = new Map;
132
+ constructor(options = {}) {
133
+ this.#token = options.token;
134
+ this.#timeout = Math.max(1, options.timeout ?? 15000);
135
+ this.#retryPolicy = options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);
136
+ this.#baseURL = (options.baseURL ?? "https://discord.com/api/v10").replace(/\/$/, "");
137
+ }
138
+ setToken(token) {
139
+ if (!token.trim())
140
+ throw new TypeError("A Discord bot token is required.");
141
+ this.#token = token;
142
+ }
143
+ async request(method, path, body, options = {}) {
144
+ const normalizedMethod = method.toUpperCase();
145
+ if (!normalizedMethod)
146
+ throw new TypeError("REST method is required.");
147
+ if (!path.startsWith("/"))
148
+ throw new TypeError("REST paths must start with '/'.");
149
+ const route = `${normalizedMethod}:${this.#normalizeRoute(path)}`;
150
+ let bucketKey = this.#routeBuckets.get(route) ?? route;
151
+ let bucket = this.#buckets.get(bucketKey) ?? {
152
+ remaining: 1,
153
+ resetAt: 0,
154
+ queue: Promise.resolve()
155
+ };
156
+ this.#buckets.set(bucketKey, bucket);
157
+ const previous = bucket.queue;
158
+ let release;
159
+ bucket.queue = new Promise((resolve) => {
160
+ release = resolve;
161
+ });
162
+ await this.#abortable(previous, options.signal, path);
163
+ try {
164
+ for (let attempt = 0;attempt <= this.#retryPolicy.maxRetries; attempt++) {
165
+ await this.#wait(bucket, options.signal, path);
166
+ await this.#waitGlobal(options.signal, path);
167
+ const controller = new AbortController;
168
+ const onAbort = () => controller.abort(options.signal?.reason);
169
+ if (options.signal?.aborted)
170
+ throw this.#abortError(path, options.signal.reason);
171
+ options.signal?.addEventListener("abort", onAbort, { once: true });
172
+ const timer = setTimeout(() => controller.abort(new DOMException("REST request timeout", "TimeoutError")), this.#timeout);
173
+ try {
174
+ const response = await fetch(`${this.#baseURL}${path}`, {
175
+ method: normalizedMethod,
176
+ headers: {
177
+ ...this.#token ? { Authorization: `Bot ${this.#token}` } : {},
178
+ "Content-Type": "application/json",
179
+ "User-Agent": "Lunibee/0.1.0"
180
+ },
181
+ body: body === undefined ? undefined : JSON.stringify(body),
182
+ signal: controller.signal
183
+ });
184
+ bucket = this.#update(response, bucket, route, bucketKey);
185
+ bucketKey = this.#routeBuckets.get(route) ?? bucketKey;
186
+ const payload = await this.#readPayload(response);
187
+ if (response.ok)
188
+ return response.status === 204 ? undefined : payload;
189
+ const data = this.#errorData(payload);
190
+ const retryAfter = response.status === 429 ? this.#retryAfter(response, data) : undefined;
191
+ if (data.global && retryAfter !== undefined)
192
+ this.#globalResetAt = Date.now() + retryAfter * 1000;
193
+ if (attempt < this.#retryPolicy.maxRetries && this.#retryPolicy.shouldRetry(normalizedMethod, response.status)) {
194
+ await this.#sleep(this.#retryPolicy.getDelay(attempt, retryAfter), options.signal, path);
195
+ continue;
196
+ }
197
+ throw new RESTError(data.message ?? response.statusText ?? `Discord REST request failed with status ${response.status}`, response.status, data.code, payload, { method: normalizedMethod, path });
198
+ } catch (error) {
199
+ if (error instanceof RESTError)
200
+ throw error;
201
+ if (options.signal?.aborted)
202
+ throw this.#abortError(path, options.signal.reason);
203
+ const isTimeout = error instanceof DOMException && (error.name === "TimeoutError" || error.name === "AbortError");
204
+ if (!isTimeout && attempt < this.#retryPolicy.maxRetries && this.#retryPolicy.shouldRetry(normalizedMethod, 0)) {
205
+ await this.#sleep(this.#retryPolicy.getDelay(attempt), options.signal, path);
206
+ continue;
207
+ }
208
+ throw new RESTError(isTimeout ? `Discord REST request timed out after ${this.#timeout}ms` : "Discord REST request failed", 0, undefined, undefined, { method: normalizedMethod, path, cause: error });
209
+ } finally {
210
+ clearTimeout(timer);
211
+ options.signal?.removeEventListener("abort", onAbort);
212
+ }
213
+ }
214
+ throw new RESTError("Discord REST request exhausted its retry attempts", 0, undefined, undefined, { method: normalizedMethod, path });
215
+ } finally {
216
+ release();
217
+ }
218
+ }
219
+ get(path, options) {
220
+ return this.request("GET", path, undefined, options);
221
+ }
222
+ post(path, body, options) {
223
+ return this.request("POST", path, body, options);
224
+ }
225
+ patch(path, body, options) {
226
+ return this.request("PATCH", path, body, options);
227
+ }
228
+ put(path, body, options) {
229
+ return this.request("PUT", path, body, options);
230
+ }
231
+ delete(path, options) {
232
+ return this.request("DELETE", path, undefined, options);
233
+ }
234
+ async#wait(bucket, signal, path) {
235
+ const delay = bucket.resetAt - Date.now();
236
+ if (bucket.remaining <= 0 && delay > 0)
237
+ await this.#sleep(delay, signal, path);
238
+ }
239
+ async#waitGlobal(signal, path) {
240
+ const delay = this.#globalResetAt - Date.now();
241
+ if (delay > 0)
242
+ await this.#sleep(delay, signal, path);
243
+ }
244
+ async#sleep(delay, signal, path) {
245
+ if (delay <= 0)
246
+ return;
247
+ await new Promise((resolve, reject) => {
248
+ const timer = setTimeout(resolve, delay);
249
+ const abort = () => {
250
+ clearTimeout(timer);
251
+ reject(this.#abortError(path, signal?.reason));
252
+ };
253
+ if (signal?.aborted)
254
+ return abort();
255
+ signal?.addEventListener("abort", abort, { once: true });
256
+ });
257
+ }
258
+ async#abortable(promise, signal, path) {
259
+ if (!signal)
260
+ return promise;
261
+ if (signal.aborted)
262
+ throw this.#abortError(path, signal.reason);
263
+ return Promise.race([
264
+ promise,
265
+ new Promise((_, reject) => signal.addEventListener("abort", () => reject(this.#abortError(path, signal.reason)), { once: true }))
266
+ ]);
267
+ }
268
+ #abortError(path, reason) {
269
+ return new RESTError(reason instanceof Error ? reason.message : "Discord REST request was aborted", 0, undefined, undefined, { path, cause: reason });
270
+ }
271
+ async#readPayload(response) {
272
+ if (response.status === 204)
273
+ return;
274
+ const contentType = response.headers.get("content-type") ?? "";
275
+ if (contentType.includes("application/json"))
276
+ return response.json().catch(() => {
277
+ return;
278
+ });
279
+ return response.text().catch(() => {
280
+ return;
281
+ });
282
+ }
283
+ #errorData(payload) {
284
+ if (!payload || typeof payload !== "object")
285
+ return {};
286
+ const data = payload;
287
+ return {
288
+ message: typeof data.message === "string" ? data.message : undefined,
289
+ code: typeof data.code === "number" ? data.code : undefined,
290
+ retry_after: typeof data.retry_after === "number" ? data.retry_after : undefined,
291
+ global: data.global === true
292
+ };
293
+ }
294
+ #retryAfter(response, data) {
295
+ const header = Number(response.headers.get("Retry-After"));
296
+ const retryAfter = data.retry_after ?? (Number.isFinite(header) ? header : 1);
297
+ return Math.max(0, retryAfter);
298
+ }
299
+ #normalizeRoute(path) {
300
+ const queryIndex = path.indexOf("?");
301
+ const routePath = queryIndex === -1 ? path : path.slice(0, queryIndex);
302
+ return routePath.replace(/\/\d+(?=\/|$)/g, "/:id");
303
+ }
304
+ #update(response, bucket, route, bucketKey) {
305
+ const serverBucket = response.headers.get("X-RateLimit-Bucket");
306
+ if (serverBucket) {
307
+ this.#routeBuckets.set(route, serverBucket);
308
+ if (serverBucket !== bucketKey) {
309
+ const shared = this.#buckets.get(serverBucket) ?? bucket;
310
+ this.#buckets.set(serverBucket, shared);
311
+ bucket = shared;
312
+ }
313
+ }
314
+ const remaining = Number(response.headers.get("X-RateLimit-Remaining"));
315
+ const resetAfter = Number(response.headers.get("X-RateLimit-Reset-After"));
316
+ const reset = Number(response.headers.get("X-RateLimit-Reset"));
317
+ if (Number.isFinite(remaining))
318
+ bucket.remaining = Math.max(0, remaining);
319
+ if (Number.isFinite(resetAfter))
320
+ bucket.resetAt = Date.now() + Math.max(0, resetAfter) * 1000;
321
+ else if (Number.isFinite(reset))
322
+ bucket.resetAt = reset * 1000;
323
+ if (response.status === 429) {
324
+ const retryAfter = Number(response.headers.get("Retry-After"));
325
+ if (Number.isFinite(retryAfter))
326
+ bucket.resetAt = Math.max(bucket.resetAt, Date.now() + retryAfter * 1000);
327
+ }
328
+ if (response.headers.get("X-RateLimit-Global") === "true") {
329
+ const retryAfter = Number(response.headers.get("Retry-After"));
330
+ if (Number.isFinite(retryAfter))
331
+ this.#globalResetAt = Math.max(this.#globalResetAt, Date.now() + retryAfter * 1000);
332
+ }
333
+ return bucket;
334
+ }
335
+ }
336
+ export {
337
+ REST,
338
+ RESTError,
339
+ Routes,
340
+ WebhookClient,
341
+ createRetryPolicy
342
+ };
343
+
344
+ //# debugId=69F9FDE9A0B8ECD864756E2164756E21
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../packages/rest/src/routes.ts", "../../packages/rest/src/webhook.ts", "../../packages/rest/src/index.ts"],
4
+ "sourcesContent": [
5
+ "/** Discord REST API route definitions. */\r\nexport const Routes = {\r\n /** Returns the current bot user. @returns User route. */ user: () =>\r\n \"/users/@me\",\r\n /** Returns one user. @param userId User identifier. @returns User route. */ userById:\r\n (userId: string) => `/users/${snowflake(userId, \"User ID\")}`,\r\n /** Returns a guild. @param guildId Guild identifier. @returns Guild route. */ guild:\r\n (guildId: string) => `/guilds/${snowflake(guildId, \"Guild ID\")}`,\r\n /** Returns channels in a guild. @param guildId Guild identifier. @returns Channel collection route. */ guildChannels:\r\n (guildId: string) => `/guilds/${snowflake(guildId, \"Guild ID\")}/channels`,\r\n /** Returns a guild member. @param guildId Guild identifier. @param userId User identifier. @returns Guild-member route. */ guildMember:\r\n (guildId: string, userId: string) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/members/${snowflake(userId, \"User ID\")}`,\r\n /** Returns all guild roles. @param guildId Guild identifier. @returns Guild-role route. */ guildRoles:\r\n (guildId: string) => `/guilds/${snowflake(guildId, \"Guild ID\")}/roles`,\r\n /** Returns an individual guild role. */ guildRole: (\r\n guildId: string,\r\n roleId: string,\r\n ) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/roles/${snowflake(roleId, \"Role ID\")}`,\r\n /** Returns a member role route. */ guildMemberRole: (\r\n guildId: string,\r\n userId: string,\r\n roleId: string,\r\n ) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/members/${snowflake(userId, \"User ID\")}/roles/${snowflake(roleId, \"Role ID\")}`,\r\n /** Returns guild bans collection. */ guildBans: (guildId: string) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/bans`,\r\n /** Returns an individual guild ban. */ guildBan: (\r\n guildId: string,\r\n userId: string,\r\n ) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/bans/${snowflake(userId, \"User ID\")}`,\r\n /** Returns a channel. @param channelId Channel identifier. @returns Channel route. */ channel:\r\n (channelId: string) => `/channels/${snowflake(channelId, \"Channel ID\")}`,\r\n /** Returns messages in a channel. @param channelId Channel identifier. @returns Message collection route. */ channelMessages:\r\n (channelId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/messages`,\r\n /** Returns a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Message route. */ message:\r\n (channelId: string, messageId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/messages/${snowflake(messageId, \"Message ID\")}`,\r\n /** Crossposts a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Crosspost route. */ crosspostMessage:\r\n (channelId: string, messageId: string) =>\r\n `${Routes.message(channelId, messageId)}/crosspost`,\r\n /** Returns reactions for a message and emoji. @param channelId Channel identifier. @param messageId Message identifier. @param emoji Emoji name or ID. @returns Reaction route. */ messageReactions:\r\n (channelId: string, messageId: string, emoji: string) =>\r\n `${Routes.message(channelId, messageId)}/reactions/${encodeURIComponent(emoji)}`,\r\n /** Removes all reactions from a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Reaction collection route. */ messageReactionsAll:\r\n (channelId: string, messageId: string) =>\r\n `${Routes.message(channelId, messageId)}/reactions`,\r\n /** Returns pinned messages. @param channelId Channel identifier. @returns Pins route. */ channelPins:\r\n (channelId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/pins`,\r\n /** Pins a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Pin route. */ channelPin:\r\n (channelId: string, messageId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/pins/${snowflake(messageId, \"Message ID\")}`,\r\n /** Creates a thread from a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Message-thread route. */ messageThread:\r\n (channelId: string, messageId: string) =>\r\n `${Routes.message(channelId, messageId)}/threads`,\r\n /** Bulk deletes messages. @param channelId Channel identifier. @returns Bulk-delete route. */ channelBulkDelete:\r\n (channelId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/messages/bulk-delete`,\r\n /** Returns channel webhooks. @param channelId Channel identifier. @returns Webhook collection route. */ channelWebhooks:\r\n (channelId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/webhooks`,\r\n /** Returns channel invites. @param channelId Channel identifier. @returns Invite collection route. */ channelInvites:\r\n (channelId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/invites`,\r\n /** Returns a webhook. @param webhookId Webhook identifier. @param token Optional webhook token. @returns Webhook route. */ webhook:\r\n (webhookId: string, token?: string) =>\r\n `/webhooks/${snowflake(webhookId, \"Webhook ID\")}${token ? `/${encodeURIComponent(token)}` : \"\"}`,\r\n /** Returns a webhook message. */ webhookMessage: (\r\n webhookId: string,\r\n token: string,\r\n messageId: string,\r\n ) =>\r\n `/webhooks/${snowflake(webhookId, \"Webhook ID\")}/${encodeURIComponent(token)}/messages/${snowflake(messageId, \"Message ID\")}`,\r\n /** Returns application commands for an application. @param applicationId Application identifier. @returns Global command route. */ applicationCommands:\r\n (applicationId: string) =>\r\n `/applications/${snowflake(applicationId, \"Application ID\")}/commands`,\r\n /** Returns one application command. @param applicationId Application identifier. @param commandId Command identifier. @returns Command route. */ applicationCommand:\r\n (applicationId: string, commandId: string) =>\r\n `/applications/${snowflake(applicationId, \"Application ID\")}/commands/${snowflake(commandId, \"Command ID\")}`,\r\n /** Returns guild application commands. @param applicationId Application identifier. @param guildId Guild identifier. @returns Guild command route. */ guildApplicationCommands:\r\n (applicationId: string, guildId: string) =>\r\n `/applications/${snowflake(applicationId, \"Application ID\")}/guilds/${snowflake(guildId, \"Guild ID\")}/commands`,\r\n /** Returns a single guild application command. @param applicationId Application identifier. @param guildId Guild identifier. @param commandId Command identifier. @returns Guild command route. */ guildApplicationCommand:\r\n (applicationId: string, guildId: string, commandId: string) =>\r\n `/applications/${snowflake(applicationId, \"Application ID\")}/guilds/${snowflake(guildId, \"Guild ID\")}/commands/${snowflake(commandId, \"Command ID\")}`,\r\n /** Returns an interaction callback endpoint. @param interactionId Interaction identifier. @param token Interaction token. @returns Interaction callback route. */ interactionCallback:\r\n (interactionId: string, token: string) =>\r\n `/interactions/${snowflake(interactionId, \"Interaction ID\")}/${encodeURIComponent(token)}/callback`,\r\n /** Returns the original interaction response endpoint. @param applicationId Application identifier. @param token Interaction token. @returns Original-response route. */ interactionOriginalResponse:\r\n (applicationId: string, token: string) =>\r\n `/webhooks/${snowflake(applicationId, \"Application ID\")}/${encodeURIComponent(token)}/messages/@original`,\r\n /** Returns guild threads that are active. @param guildId Guild identifier. @returns Active-thread route. */ guildActiveThreads:\r\n (guildId: string) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/threads/active`,\r\n /** Returns public archived threads for a channel. @param channelId Channel identifier. @returns Public-archive route. */ channelPublicArchivedThreads:\r\n (channelId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/threads/archived/public`,\r\n /** Returns private archived threads for a channel. @param channelId Channel identifier. @returns Private-archive route. */ channelPrivateArchivedThreads:\r\n (channelId: string) =>\r\n `/channels/${snowflake(channelId, \"Channel ID\")}/threads/archived/private`,\r\n /** Returns a thread's members. @param threadId Thread identifier. @returns Thread-member route. */ threadMembers:\r\n (threadId: string) =>\r\n `/channels/${snowflake(threadId, \"Thread ID\")}/thread-members`,\r\n /** Returns guild scheduled events. @param guildId Guild identifier. @returns Scheduled-event collection route. */ guildScheduledEvents:\r\n (guildId: string) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/scheduled-events`,\r\n /** Returns one guild scheduled event. @param guildId Guild identifier. @param eventId Event identifier. @returns Scheduled-event route. */ guildScheduledEvent:\r\n (guildId: string, eventId: string) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/scheduled-events/${snowflake(eventId, \"Event ID\")}`,\r\n /** Returns guild automod rules. @param guildId Guild identifier. @returns Automod-rule collection route. */ guildAutoModerationRules:\r\n (guildId: string) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/auto-moderation/rules`,\r\n /** Returns one automod rule. @param guildId Guild identifier. @param ruleId Rule identifier. @returns Automod-rule route. */ guildAutoModerationRule:\r\n (guildId: string, ruleId: string) =>\r\n `/guilds/${snowflake(guildId, \"Guild ID\")}/auto-moderation/rules/${snowflake(ruleId, \"Rule ID\")}`,\r\n /** Returns current voice regions. @returns Voice-region route. */ voiceRegions:\r\n () => \"/voice/regions\",\r\n /** Returns the generic Gateway endpoint. @returns Gateway route. */ gateway:\r\n () => \"/gateway\",\r\n /** Returns the Gateway endpoint and recommended shard count for bots. @returns Gateway bot route. */ gatewayBot:\r\n () => \"/gateway/bot\",\r\n} as const;\r\n\r\n/** Validates a Discord snowflake route parameter. @param value Candidate identifier. @param field Human-readable field name. @returns The validated identifier. @throws {TypeError} If the identifier is not a Discord snowflake. */\r\nfunction snowflake(value: string, field: string): string {\r\n if (!/^\\d{1,20}$/.test(value))\r\n throw new TypeError(`${field} must be a valid Discord snowflake.`);\r\n return value;\r\n}\r\n",
6
+ "import { REST } from \"./index.js\";\nimport { Routes } from \"./routes.js\";\nimport type { APIEmbed } from \"@lunibee/types\";\n\nexport interface WebhookClientOptions {\n id?: string;\n token?: string;\n url?: string;\n}\n\nexport interface WebhookMessageOptions {\n content?: string;\n username?: string;\n avatar_url?: string;\n tts?: boolean;\n embeds?: (APIEmbed | { toJSON(): APIEmbed })[];\n components?: any[];\n files?: any[];\n flags?: number;\n thread_id?: string;\n}\n\n/** Standalone client for sending Discord webhooks. */\nexport class WebhookClient {\n /** Webhook ID. */\n public readonly id: string;\n /** Webhook security token. */\n public readonly token: string;\n readonly #rest: REST;\n\n public constructor(\n options: WebhookClientOptions,\n restOptions?: { timeout?: number; retries?: number },\n ) {\n if (options.url) {\n const match = options.url.match(\n /discord(?:app)?.com\\/api\\/webhooks\\/(\\d+)\\/([A-Za-z0-9_-]+)/,\n );\n if (!match) throw new Error(\"Invalid Discord webhook URL format.\");\n this.id = match[1]!;\n this.token = match[2]!;\n } else if (options.id && options.token) {\n this.id = options.id;\n this.token = options.token;\n } else {\n throw new Error(\n \"WebhookClient requires either a webhook 'url' or both 'id' and 'token'.\",\n );\n }\n this.#rest = new REST(restOptions);\n }\n\n /** Sends a message to the webhook. */\n public async send(options: string | WebhookMessageOptions): Promise<any> {\n const payload =\n typeof options === \"string\" ? { content: options } : options;\n const { thread_id, ...body } = payload;\n const query = thread_id\n ? `?thread_id=${thread_id}&wait=true`\n : `?wait=true`;\n const formattedEmbeds = body.embeds?.map((e) =>\n typeof e === \"object\" && e !== null && \"toJSON\" in e\n ? (e as any).toJSON()\n : e,\n );\n return this.#rest.post(`${Routes.webhook(this.id, this.token)}${query}`, {\n ...body,\n embeds: formattedEmbeds,\n });\n }\n\n /** Edits a previously sent webhook message. */\n public async editMessage(\n messageId: string,\n options: string | { content?: string; embeds?: any[] },\n ): Promise<any> {\n const payload =\n typeof options === \"string\" ? { content: options } : options;\n return this.#rest.patch(\n Routes.webhookMessage(this.id, this.token, messageId),\n payload,\n );\n }\n\n /** Deletes a previously sent webhook message. */\n public async deleteMessage(messageId: string): Promise<void> {\n await this.#rest.delete(\n Routes.webhookMessage(this.id, this.token, messageId),\n );\n }\n}\n",
7
+ "/** Error thrown when Discord rejects a REST request. */\nexport class RESTError extends Error {\n /** HTTP status returned by Discord. */ public readonly status: number;\n /** Discord API error code, when provided. */ public readonly code?: number;\n /** Raw Discord validation/error payload. */ public readonly errors?: unknown;\n /** HTTP method used for the failed request. */ public readonly method?: string;\n /** API path used for the failed request. */ public readonly path?: string;\n /** Creates a REST error with request context. @param message Error message. @param status HTTP status. @param code Discord error code. @param errors Raw error payload. @param options Request context and cause. */\n public constructor(\n message: string,\n status: number,\n code?: number,\n errors?: unknown,\n options: { method?: string; path?: string; cause?: unknown } = {},\n ) {\n super(\n message,\n options.cause === undefined ? undefined : { cause: options.cause },\n );\n this.name = \"RESTError\";\n this.status = status;\n this.code = code;\n this.errors = errors;\n this.method = options.method;\n this.path = options.path;\n }\n}\n\n/** Internal state shared by requests mapped to one Discord rate-limit bucket. */\ntype Bucket = { remaining: number; resetAt: number; queue: Promise<void> };\n/** Options controlling an individual REST request. */\nexport interface RESTRequestOptions {\n /** Abort signal used to cancel the request and any queued wait. */ signal?: AbortSignal;\n}\n\n/** Configures which REST failures may be retried. */\nexport interface RetryPolicy {\n /** Maximum retry attempts. */ maxRetries: number;\n /** Determines whether a request may be retried. @param method HTTP method. @param status HTTP status, or zero for transport errors. @returns True when retrying is safe under the policy. */ shouldRetry(\n method: string,\n status: number,\n ): boolean;\n /** Calculates delay before the next attempt. @param attempt Zero-based retry number. @param retryAfter Server-provided delay, when available. @returns Delay in milliseconds. */ getDelay(\n attempt: number,\n retryAfter?: number,\n ): number;\n}\n/** Creates the default conservative REST retry policy. @param maxRetries Maximum retries. @returns Retry policy that retries rate limits and idempotent transient failures. @throws {TypeError} If maxRetries is negative or not finite. */\nexport function createRetryPolicy(maxRetries = 2): RetryPolicy {\n if (!Number.isFinite(maxRetries) || maxRetries < 0)\n throw new TypeError(\"maxRetries must be a non-negative finite number.\");\n return {\n maxRetries: Math.floor(maxRetries),\n shouldRetry(method, status) {\n const normalized = method.toUpperCase();\n return (\n status === 429 ||\n (status >= 500 &&\n status <= 599 &&\n [\"GET\", \"HEAD\", \"PUT\", \"DELETE\"].includes(normalized))\n );\n },\n getDelay(attempt, retryAfter) {\n if (retryAfter !== undefined) return Math.max(0, retryAfter * 1000);\n return Math.min(10_000, 500 * 2 ** attempt) + Math.random() * 250;\n },\n };\n}\n\n/** Bun-native REST transport with Discord bucket-aware rate limiting, retries, and cancellation. */\nexport class REST {\n readonly #baseURL: string;\n readonly #timeout: number;\n readonly #retryPolicy: RetryPolicy;\n #token?: string;\n #globalResetAt = 0;\n readonly #buckets = new Map<string, Bucket>();\n readonly #routeBuckets = new Map<string, string>();\n /** Creates a REST transport. @param options Transport configuration. @throws {TypeError} If retry configuration is invalid. */\n public constructor(\n options: {\n token?: string;\n timeout?: number;\n retries?: number;\n retryPolicy?: RetryPolicy;\n baseURL?: string;\n } = {},\n ) {\n this.#token = options.token;\n this.#timeout = Math.max(1, options.timeout ?? 15_000);\n this.#retryPolicy =\n options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);\n this.#baseURL = (options.baseURL ?? \"https://discord.com/api/v10\").replace(\n /\\/$/,\n \"\",\n );\n }\n /** Sets the authentication token. @param token Discord bot token. @returns Nothing. @throws {TypeError} If token is empty. */\n public setToken(token: string): void {\n if (!token.trim()) throw new TypeError(\"A Discord bot token is required.\");\n this.#token = token;\n }\n /** Executes an HTTP request with Discord bucket/global rate limits, retry handling, and AbortSignal support. @param method HTTP method. @param path API path. @param body Optional JSON body. @param options Cancellation options. @returns Decoded response body. @throws {RESTError} If Discord or transport rejects the request. */\n public async request<T>(\n method: string,\n path: string,\n body?: unknown,\n options: RESTRequestOptions = {},\n ): Promise<T> {\n const normalizedMethod = method.toUpperCase();\n if (!normalizedMethod) throw new TypeError(\"REST method is required.\");\n if (!path.startsWith(\"/\"))\n throw new TypeError(\"REST paths must start with '/'.\");\n const route = `${normalizedMethod}:${this.#normalizeRoute(path)}`;\n let bucketKey = this.#routeBuckets.get(route) ?? route;\n let bucket = this.#buckets.get(bucketKey) ?? {\n remaining: 1,\n resetAt: 0,\n queue: Promise.resolve(),\n };\n this.#buckets.set(bucketKey, bucket);\n const previous = bucket.queue;\n let release!: () => void;\n bucket.queue = new Promise((resolve) => {\n release = resolve;\n });\n await this.#abortable(previous, options.signal, path);\n try {\n for (\n let attempt = 0;\n attempt <= this.#retryPolicy.maxRetries;\n attempt++\n ) {\n await this.#wait(bucket, options.signal, path);\n await this.#waitGlobal(options.signal, path);\n const controller = new AbortController();\n const onAbort = () => controller.abort(options.signal?.reason);\n if (options.signal?.aborted)\n throw this.#abortError(path, options.signal.reason);\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n const timer = setTimeout(\n () =>\n controller.abort(\n new DOMException(\"REST request timeout\", \"TimeoutError\"),\n ),\n this.#timeout,\n );\n try {\n const response = await fetch(`${this.#baseURL}${path}`, {\n method: normalizedMethod,\n headers: {\n ...(this.#token ? { Authorization: `Bot ${this.#token}` } : {}),\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"Lunibee/0.1.0\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n bucket = this.#update(response, bucket, route, bucketKey);\n bucketKey = this.#routeBuckets.get(route) ?? bucketKey;\n const payload = await this.#readPayload(response);\n if (response.ok)\n return (response.status === 204 ? undefined : payload) as T;\n const data = this.#errorData(payload);\n const retryAfter =\n response.status === 429\n ? this.#retryAfter(response, data)\n : undefined;\n if (data.global && retryAfter !== undefined)\n this.#globalResetAt = Date.now() + retryAfter * 1000;\n if (\n attempt < this.#retryPolicy.maxRetries &&\n this.#retryPolicy.shouldRetry(normalizedMethod, response.status)\n ) {\n await this.#sleep(\n this.#retryPolicy.getDelay(attempt, retryAfter),\n options.signal,\n path,\n );\n continue;\n }\n throw new RESTError(\n data.message ??\n response.statusText ??\n `Discord REST request failed with status ${response.status}`,\n response.status,\n data.code,\n payload,\n { method: normalizedMethod, path },\n );\n } catch (error) {\n if (error instanceof RESTError) throw error;\n if (options.signal?.aborted)\n throw this.#abortError(path, options.signal.reason);\n const isTimeout =\n error instanceof DOMException &&\n (error.name === \"TimeoutError\" || error.name === \"AbortError\");\n if (\n !isTimeout &&\n attempt < this.#retryPolicy.maxRetries &&\n this.#retryPolicy.shouldRetry(normalizedMethod, 0)\n ) {\n await this.#sleep(\n this.#retryPolicy.getDelay(attempt),\n options.signal,\n path,\n );\n continue;\n }\n throw new RESTError(\n isTimeout\n ? `Discord REST request timed out after ${this.#timeout}ms`\n : \"Discord REST request failed\",\n 0,\n undefined,\n undefined,\n { method: normalizedMethod, path, cause: error },\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n throw new RESTError(\n \"Discord REST request exhausted its retry attempts\",\n 0,\n undefined,\n undefined,\n { method: normalizedMethod, path },\n );\n } finally {\n release();\n }\n }\n /** Sends a GET request. @param path API path. @param options Cancellation options. @returns Decoded response. @throws {RESTError} When the request fails. */ public get<\n T,\n >(path: string, options?: RESTRequestOptions): Promise<T> {\n return this.request<T>(\"GET\", path, undefined, options);\n }\n /** Sends a POST request. @param path API path. @param body Optional JSON body. @param options Cancellation options. @returns Decoded response. @throws {RESTError} When the request fails. */ public post<\n T,\n >(path: string, body?: unknown, options?: RESTRequestOptions): Promise<T> {\n return this.request<T>(\"POST\", path, body, options);\n }\n /** Sends a PATCH request. @param path API path. @param body Optional JSON body. @param options Cancellation options. @returns Decoded response. @throws {RESTError} When the request fails. */ public patch<\n T,\n >(path: string, body?: unknown, options?: RESTRequestOptions): Promise<T> {\n return this.request<T>(\"PATCH\", path, body, options);\n }\n /** Sends a PUT request. @param path API path. @param body Optional JSON body. @param options Cancellation options. @returns Decoded response. @throws {RESTError} When the request fails. */ public put<\n T,\n >(path: string, body?: unknown, options?: RESTRequestOptions): Promise<T> {\n return this.request<T>(\"PUT\", path, body, options);\n }\n /** Sends a DELETE request. @param path API path. @param options Cancellation options. @returns Decoded response. @throws {RESTError} When the request fails. */ public delete<\n T,\n >(path: string, options?: RESTRequestOptions): Promise<T> {\n return this.request<T>(\"DELETE\", path, undefined, options);\n }\n /** Waits for a route bucket while respecting cancellation. @param bucket Bucket state. @param signal Optional cancellation signal. @param path Request path for error context. @returns Promise fulfilled when sending is permitted. @throws {RESTError} If the request is aborted. */\n async #wait(\n bucket: Bucket,\n signal: AbortSignal | undefined,\n path: string,\n ): Promise<void> {\n const delay = bucket.resetAt - Date.now();\n if (bucket.remaining <= 0 && delay > 0)\n await this.#sleep(delay, signal, path);\n }\n /** Waits for the global Discord rate limit while respecting cancellation. @param signal Optional cancellation signal. @param path Request path for error context. @returns Promise fulfilled when the global limit expires. @throws {RESTError} If the request is aborted. */\n async #waitGlobal(\n signal: AbortSignal | undefined,\n path: string,\n ): Promise<void> {\n const delay = this.#globalResetAt - Date.now();\n if (delay > 0) await this.#sleep(delay, signal, path);\n }\n /** Waits for a delay or aborts immediately. @param delay Delay in milliseconds. @param signal Optional cancellation signal. @param path Request path. @returns Promise fulfilled after delay. @throws {RESTError} If aborted. */\n async #sleep(\n delay: number,\n signal: AbortSignal | undefined,\n path: string,\n ): Promise<void> {\n if (delay <= 0) return;\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, delay);\n const abort = () => {\n clearTimeout(timer);\n reject(this.#abortError(path, signal?.reason));\n };\n if (signal?.aborted) return abort();\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n }\n /** Waits for a queued request or aborts. @param promise Queue predecessor. @param signal Optional cancellation signal. @param path Request path. @returns Promise fulfilled when predecessor releases. @throws {RESTError} If aborted. */\n async #abortable(\n promise: Promise<void>,\n signal: AbortSignal | undefined,\n path: string,\n ): Promise<void> {\n if (!signal) return promise;\n if (signal.aborted) throw this.#abortError(path, signal.reason);\n return Promise.race([\n promise,\n new Promise<void>((_, reject) =>\n signal.addEventListener(\n \"abort\",\n () => reject(this.#abortError(path, signal.reason)),\n { once: true },\n ),\n ),\n ]);\n }\n /** Creates a consistent cancellation error. @param path Request path. @param reason Abort reason. @returns REST cancellation error. */\n #abortError(path: string, reason: unknown): RESTError {\n return new RESTError(\n reason instanceof Error\n ? reason.message\n : \"Discord REST request was aborted\",\n 0,\n undefined,\n undefined,\n { path, cause: reason },\n );\n }\n /** Reads a Discord response payload. @param response HTTP response. @returns JSON or text payload. */\n async #readPayload(response: Response): Promise<unknown> {\n if (response.status === 204) return undefined;\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\"))\n return response.json().catch(() => undefined);\n return response.text().catch(() => undefined);\n }\n /** Extracts retry metadata from a Discord payload. @param payload Response payload. @returns Normalized error metadata. */\n #errorData(payload: unknown): {\n message?: string;\n code?: number;\n retry_after?: number;\n global?: boolean;\n } {\n if (!payload || typeof payload !== \"object\") return {};\n const data = payload as Record<string, unknown>;\n return {\n message: typeof data.message === \"string\" ? data.message : undefined,\n code: typeof data.code === \"number\" ? data.code : undefined,\n retry_after:\n typeof data.retry_after === \"number\" ? data.retry_after : undefined,\n global: data.global === true,\n };\n }\n /** Resolves Retry-After from headers or JSON. @param response HTTP response. @param data Parsed response metadata. @returns Retry delay in seconds. */\n #retryAfter(response: Response, data: { retry_after?: number }): number {\n const header = Number(response.headers.get(\"Retry-After\"));\n const retryAfter =\n data.retry_after ?? (Number.isFinite(header) ? header : 1);\n return Math.max(0, retryAfter);\n }\n /** Normalizes Discord routes for stable bucket discovery. @param path API path. @returns Normalized route. */\n #normalizeRoute(path: string): string {\n const queryIndex = path.indexOf(\"?\");\n const routePath = queryIndex === -1 ? path : path.slice(0, queryIndex);\n return routePath.replace(/\\/\\d+(?=\\/|$)/g, \"/:id\");\n }\n /** Updates bucket state from Discord's rate-limit headers and records the server bucket hash. @param response HTTP response. @param bucket Current bucket. @param route Normalized local route key. @param bucketKey Current bucket identifier. @returns Bucket state used for subsequent attempts. */\n #update(\n response: Response,\n bucket: Bucket,\n route: string,\n bucketKey: string,\n ): Bucket {\n const serverBucket = response.headers.get(\"X-RateLimit-Bucket\");\n if (serverBucket) {\n this.#routeBuckets.set(route, serverBucket);\n if (serverBucket !== bucketKey) {\n const shared = this.#buckets.get(serverBucket) ?? bucket;\n this.#buckets.set(serverBucket, shared);\n bucket = shared;\n }\n }\n const remaining = Number(response.headers.get(\"X-RateLimit-Remaining\"));\n const resetAfter = Number(response.headers.get(\"X-RateLimit-Reset-After\"));\n const reset = Number(response.headers.get(\"X-RateLimit-Reset\"));\n if (Number.isFinite(remaining)) bucket.remaining = Math.max(0, remaining);\n if (Number.isFinite(resetAfter))\n bucket.resetAt = Date.now() + Math.max(0, resetAfter) * 1000;\n else if (Number.isFinite(reset)) bucket.resetAt = reset * 1000;\n if (response.status === 429) {\n const retryAfter = Number(response.headers.get(\"Retry-After\"));\n if (Number.isFinite(retryAfter))\n bucket.resetAt = Math.max(\n bucket.resetAt,\n Date.now() + retryAfter * 1000,\n );\n }\n if (response.headers.get(\"X-RateLimit-Global\") === \"true\") {\n const retryAfter = Number(response.headers.get(\"Retry-After\"));\n if (Number.isFinite(retryAfter))\n this.#globalResetAt = Math.max(\n this.#globalResetAt,\n Date.now() + retryAfter * 1000,\n );\n }\n return bucket;\n }\n}\n\nexport { Routes } from \"./routes.js\";\n\nexport {\n WebhookClient,\n type WebhookClientOptions,\n type WebhookMessageOptions,\n} from \"./webhook.js\";\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "69F9FDE9A0B8ECD864756E2164756E21",
11
+ "names": []
12
+ }
@@ -0,0 +1,44 @@
1
+ /** Discord REST API route definitions. */
2
+ export declare const Routes: {
3
+ /** Returns the current bot user. @returns User route. */ readonly user: () => string;
4
+ /** Returns one user. @param userId User identifier. @returns User route. */ readonly userById: (userId: string) => string;
5
+ /** Returns a guild. @param guildId Guild identifier. @returns Guild route. */ readonly guild: (guildId: string) => string;
6
+ /** Returns channels in a guild. @param guildId Guild identifier. @returns Channel collection route. */ readonly guildChannels: (guildId: string) => string;
7
+ /** Returns a guild member. @param guildId Guild identifier. @param userId User identifier. @returns Guild-member route. */ readonly guildMember: (guildId: string, userId: string) => string;
8
+ /** Returns all guild roles. @param guildId Guild identifier. @returns Guild-role route. */ readonly guildRoles: (guildId: string) => string;
9
+ /** Returns an individual guild role. */ readonly guildRole: (guildId: string, roleId: string) => string;
10
+ /** Returns a member role route. */ readonly guildMemberRole: (guildId: string, userId: string, roleId: string) => string;
11
+ /** Returns guild bans collection. */ readonly guildBans: (guildId: string) => string;
12
+ /** Returns an individual guild ban. */ readonly guildBan: (guildId: string, userId: string) => string;
13
+ /** Returns a channel. @param channelId Channel identifier. @returns Channel route. */ readonly channel: (channelId: string) => string;
14
+ /** Returns messages in a channel. @param channelId Channel identifier. @returns Message collection route. */ readonly channelMessages: (channelId: string) => string;
15
+ /** Returns a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Message route. */ readonly message: (channelId: string, messageId: string) => string;
16
+ /** Crossposts a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Crosspost route. */ readonly crosspostMessage: (channelId: string, messageId: string) => string;
17
+ /** Returns reactions for a message and emoji. @param channelId Channel identifier. @param messageId Message identifier. @param emoji Emoji name or ID. @returns Reaction route. */ readonly messageReactions: (channelId: string, messageId: string, emoji: string) => string;
18
+ /** Removes all reactions from a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Reaction collection route. */ readonly messageReactionsAll: (channelId: string, messageId: string) => string;
19
+ /** Returns pinned messages. @param channelId Channel identifier. @returns Pins route. */ readonly channelPins: (channelId: string) => string;
20
+ /** Pins a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Pin route. */ readonly channelPin: (channelId: string, messageId: string) => string;
21
+ /** Creates a thread from a message. @param channelId Channel identifier. @param messageId Message identifier. @returns Message-thread route. */ readonly messageThread: (channelId: string, messageId: string) => string;
22
+ /** Bulk deletes messages. @param channelId Channel identifier. @returns Bulk-delete route. */ readonly channelBulkDelete: (channelId: string) => string;
23
+ /** Returns channel webhooks. @param channelId Channel identifier. @returns Webhook collection route. */ readonly channelWebhooks: (channelId: string) => string;
24
+ /** Returns channel invites. @param channelId Channel identifier. @returns Invite collection route. */ readonly channelInvites: (channelId: string) => string;
25
+ /** Returns a webhook. @param webhookId Webhook identifier. @param token Optional webhook token. @returns Webhook route. */ readonly webhook: (webhookId: string, token?: string) => string;
26
+ /** Returns a webhook message. */ readonly webhookMessage: (webhookId: string, token: string, messageId: string) => string;
27
+ /** Returns application commands for an application. @param applicationId Application identifier. @returns Global command route. */ readonly applicationCommands: (applicationId: string) => string;
28
+ /** Returns one application command. @param applicationId Application identifier. @param commandId Command identifier. @returns Command route. */ readonly applicationCommand: (applicationId: string, commandId: string) => string;
29
+ /** Returns guild application commands. @param applicationId Application identifier. @param guildId Guild identifier. @returns Guild command route. */ readonly guildApplicationCommands: (applicationId: string, guildId: string) => string;
30
+ /** Returns a single guild application command. @param applicationId Application identifier. @param guildId Guild identifier. @param commandId Command identifier. @returns Guild command route. */ readonly guildApplicationCommand: (applicationId: string, guildId: string, commandId: string) => string;
31
+ /** Returns an interaction callback endpoint. @param interactionId Interaction identifier. @param token Interaction token. @returns Interaction callback route. */ readonly interactionCallback: (interactionId: string, token: string) => string;
32
+ /** Returns the original interaction response endpoint. @param applicationId Application identifier. @param token Interaction token. @returns Original-response route. */ readonly interactionOriginalResponse: (applicationId: string, token: string) => string;
33
+ /** Returns guild threads that are active. @param guildId Guild identifier. @returns Active-thread route. */ readonly guildActiveThreads: (guildId: string) => string;
34
+ /** Returns public archived threads for a channel. @param channelId Channel identifier. @returns Public-archive route. */ readonly channelPublicArchivedThreads: (channelId: string) => string;
35
+ /** Returns private archived threads for a channel. @param channelId Channel identifier. @returns Private-archive route. */ readonly channelPrivateArchivedThreads: (channelId: string) => string;
36
+ /** Returns a thread's members. @param threadId Thread identifier. @returns Thread-member route. */ readonly threadMembers: (threadId: string) => string;
37
+ /** Returns guild scheduled events. @param guildId Guild identifier. @returns Scheduled-event collection route. */ readonly guildScheduledEvents: (guildId: string) => string;
38
+ /** Returns one guild scheduled event. @param guildId Guild identifier. @param eventId Event identifier. @returns Scheduled-event route. */ readonly guildScheduledEvent: (guildId: string, eventId: string) => string;
39
+ /** Returns guild automod rules. @param guildId Guild identifier. @returns Automod-rule collection route. */ readonly guildAutoModerationRules: (guildId: string) => string;
40
+ /** Returns one automod rule. @param guildId Guild identifier. @param ruleId Rule identifier. @returns Automod-rule route. */ readonly guildAutoModerationRule: (guildId: string, ruleId: string) => string;
41
+ /** Returns current voice regions. @returns Voice-region route. */ readonly voiceRegions: () => string;
42
+ /** Returns the generic Gateway endpoint. @returns Gateway route. */ readonly gateway: () => string;
43
+ /** Returns the Gateway endpoint and recommended shard count for bots. @returns Gateway bot route. */ readonly gatewayBot: () => string;
44
+ };
@@ -0,0 +1,26 @@
1
+ /** Options accepted by a REST request. */
2
+ export interface RequestOptions {
3
+ /** Additional request headers. */
4
+ headers?: HeadersInit;
5
+ /** Request body. Objects are encoded as JSON. */
6
+ body?: unknown;
7
+ /** Abort signal used to cancel the request. */
8
+ signal?: AbortSignal;
9
+ /** Request timeout in milliseconds. Overrides the client default. */
10
+ timeout?: number;
11
+ /** Number of retry attempts. Overrides the client default. */
12
+ retries?: number;
13
+ }
14
+ /** Result metadata returned by Discord rate-limit headers. */
15
+ export interface RateLimitInfo {
16
+ /** Route bucket identifier. */
17
+ bucket: string | null;
18
+ /** Number of requests remaining in the bucket. */
19
+ remaining: number | null;
20
+ /** Number of requests allowed by the bucket. */
21
+ limit: number | null;
22
+ /** Seconds until the bucket resets. */
23
+ resetAfter: number | null;
24
+ /** Whether the response was globally rate limited. */
25
+ global: boolean;
26
+ }
@@ -0,0 +1,40 @@
1
+ import type { APIEmbed } from "@lunibee/types";
2
+ export interface WebhookClientOptions {
3
+ id?: string;
4
+ token?: string;
5
+ url?: string;
6
+ }
7
+ export interface WebhookMessageOptions {
8
+ content?: string;
9
+ username?: string;
10
+ avatar_url?: string;
11
+ tts?: boolean;
12
+ embeds?: (APIEmbed | {
13
+ toJSON(): APIEmbed;
14
+ })[];
15
+ components?: any[];
16
+ files?: any[];
17
+ flags?: number;
18
+ thread_id?: string;
19
+ }
20
+ /** Standalone client for sending Discord webhooks. */
21
+ export declare class WebhookClient {
22
+ #private;
23
+ /** Webhook ID. */
24
+ readonly id: string;
25
+ /** Webhook security token. */
26
+ readonly token: string;
27
+ constructor(options: WebhookClientOptions, restOptions?: {
28
+ timeout?: number;
29
+ retries?: number;
30
+ });
31
+ /** Sends a message to the webhook. */
32
+ send(options: string | WebhookMessageOptions): Promise<any>;
33
+ /** Edits a previously sent webhook message. */
34
+ editMessage(messageId: string, options: string | {
35
+ content?: string;
36
+ embeds?: any[];
37
+ }): Promise<any>;
38
+ /** Deletes a previously sent webhook message. */
39
+ deleteMessage(messageId: string): Promise<void>;
40
+ }
@@ -0,0 +1,22 @@
1
+ /** Message envelope exchanged between Lunibee shards. */
2
+ export interface ShardMessage<T = unknown> {
3
+ /** Sending shard ID. */ source: number;
4
+ /** Target shard ID, or null for broadcast. */ target: number | null;
5
+ /** Application message type. */ type: string;
6
+ /** Message payload. */ data: T;
7
+ /** Unique message ID. */ id: string;
8
+ }
9
+ /** Handler invoked for shard messages. @typeParam T Message payload type. */
10
+ export type ShardMessageHandler<T = unknown> = (message: ShardMessage<T>) => unknown;
11
+ /** Bun/Node-compatible cross-shard transport using BroadcastChannel. */
12
+ export declare class ShardBus {
13
+ #private;
14
+ /** BroadcastChannel name used by this bus. */ readonly channelName: string;
15
+ /** Creates a shard bus. @param shardId Shard ID. @param channelName Application-specific channel name. @throws {RangeError} If shard ID is invalid. @throws {TypeError} If channel name is empty. */
16
+ constructor(shardId: number, channelName: string);
17
+ /** Registers a message handler. @param type Message type. @param handler Handler callback. @returns This bus. @throws {TypeError} If type or handler is invalid. */ on<T>(type: string, handler: ShardMessageHandler<T>): this;
18
+ /** Removes a message handler. @param type Message type. @param handler Handler callback. @returns This bus. */ off<T>(type: string, handler: ShardMessageHandler<T>): this;
19
+ /** Sends a targeted shard message. @param target Target shard ID. @param type Message type. @param data Payload. @returns Unique message ID. */ send<T>(target: number, type: string, data: T): string;
20
+ /** Broadcasts to all other shards. @param type Message type. @param data Payload. @returns Unique message ID. */ broadcast<T>(type: string, data: T): string;
21
+ /** Closes the transport. @returns Nothing. */ close(): void;
22
+ }
@@ -0,0 +1,34 @@
1
+ import { Gateway } from "@lunibee/ws";
2
+ export { ShardBus } from "./bus.js";
3
+ export type { ShardMessage, ShardMessageHandler } from "./bus.js";
4
+ /** Configuration for a sharded Gateway client. */
5
+ export interface ShardManagerOptions {
6
+ /** Bot token. */ token: string;
7
+ /** Gateway intents. */ intents: number;
8
+ /** Number of shards. Use `"auto"` to request Discord's recommended count. */ shardCount?: number | "auto";
9
+ /** Gateway reconnect behavior. */ reconnect?: boolean;
10
+ /** Delay between shard starts in milliseconds. */ spawnDelay?: number;
11
+ }
12
+ /** Runtime state for a managed shard. */
13
+ export interface ShardInfo {
14
+ /** Shard identifier. */ id: number;
15
+ /** Gateway instance. */ gateway: Gateway;
16
+ }
17
+ /** Manages independent Discord Gateway shards with explicit destruction and reinitialization semantics. */
18
+ export declare class ShardManager {
19
+ #private;
20
+ /** Active Gateway shards indexed by shard identifier. */ readonly shards: Map<number, Gateway>;
21
+ /** Number of shards managed by this instance after initialization. */ get shardCount(): number;
22
+ /** Creates a shard manager. @param options Sharding configuration. @throws {TypeError} If token or intents are invalid. @throws {RangeError} If shard count is invalid. */
23
+ constructor(options: ShardManagerOptions);
24
+ /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */
25
+ fetchRecommendedShardCount(): Promise<number>;
26
+ /** Connects all shards sequentially. A destroyed manager is reinitialized before connecting. @returns A promise fulfilled after all shards connect. @throws {Error} If a shard fails to connect. */
27
+ connect(): Promise<void>;
28
+ /** Permanently closes the current shard set and releases Gateway resources. The next connect recreates them. @returns Nothing. */
29
+ destroy(): void;
30
+ /** Calculates the target shard ID for a Discord guild snowflake. @param guildId Guild snowflake. @returns Shard identifier. */
31
+ getShardIdForGuild(guildId: string): number;
32
+ /** Gets a shard by ID. @param id Shard identifier. @returns Gateway instance or undefined. */ get(id: number): Gateway | undefined;
33
+ /** Returns information for all managed shards. @returns Shard information snapshots. */ values(): ShardInfo[];
34
+ }