lunibee 0.1.6 → 0.1.8

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 (80) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +223 -223
  3. package/dist/builders/commands.d.ts +39 -0
  4. package/dist/builders/components.d.ts +37 -12
  5. package/dist/builders/embed.d.ts +3 -1
  6. package/dist/builders/index.d.ts +2 -2
  7. package/dist/builders/index.js +192 -29
  8. package/dist/builders/index.js.map +9 -7
  9. package/dist/collection/index.d.ts +30 -0
  10. package/dist/collection/index.js +79 -5
  11. package/dist/collection/index.js.map +4 -4
  12. package/dist/core/events.d.ts +88 -2
  13. package/dist/core/index.d.ts +46 -85
  14. package/dist/core/index.js +2395 -789
  15. package/dist/core/index.js.map +44 -22
  16. package/dist/core/permissions.d.ts +112 -53
  17. package/dist/formatters/index.d.ts +43 -3
  18. package/dist/formatters/index.js +64 -1
  19. package/dist/formatters/index.js.map +3 -3
  20. package/dist/handlers/index.js.map +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +3783 -1009
  23. package/dist/index.js.map +63 -38
  24. package/dist/managers/emoji.d.ts +38 -0
  25. package/dist/managers/guild-resources.d.ts +109 -0
  26. package/dist/managers/guild.d.ts +117 -1
  27. package/dist/managers/index.d.ts +37 -1
  28. package/dist/managers/index.js +1125 -81
  29. package/dist/managers/index.js.map +25 -20
  30. package/dist/rest/decoder.d.ts +23 -0
  31. package/dist/rest/errors.d.ts +20 -0
  32. package/dist/rest/index.d.ts +119 -25
  33. package/dist/rest/index.js +676 -204
  34. package/dist/rest/index.js.map +14 -6
  35. package/dist/rest/limiter.d.ts +42 -0
  36. package/dist/rest/redis.d.ts +54 -0
  37. package/dist/rest/route.d.ts +31 -0
  38. package/dist/rest/routes.d.ts +24 -0
  39. package/dist/rest/scheduler.d.ts +42 -0
  40. package/dist/rest/store.d.ts +67 -0
  41. package/dist/rest/transport.d.ts +40 -0
  42. package/dist/rest/webhook.d.ts +2 -2
  43. package/dist/sharding/bus.d.ts +31 -0
  44. package/dist/sharding/cluster.d.ts +71 -0
  45. package/dist/sharding/index.d.ts +18 -2
  46. package/dist/sharding/index.js +1202 -317
  47. package/dist/sharding/index.js.map +18 -7
  48. package/dist/structures/audit-log.d.ts +26 -0
  49. package/dist/structures/base.d.ts +40 -0
  50. package/dist/structures/channels.d.ts +56 -0
  51. package/dist/structures/index.d.ts +30 -1
  52. package/dist/structures/index.js +963 -87
  53. package/dist/structures/index.js.map +17 -10
  54. package/dist/structures/interactions.d.ts +12 -52
  55. package/dist/structures/options.d.ts +55 -0
  56. package/dist/structures/resources.d.ts +194 -6
  57. package/dist/types/gateway-events.d.ts +132 -0
  58. package/dist/types/gateway.d.ts +181 -0
  59. package/dist/types/index.d.ts +78 -263
  60. package/dist/types/index.js +44 -2
  61. package/dist/types/index.js.map +5 -4
  62. package/dist/utils/index.js.map +2 -2
  63. package/dist/voice/index.d.ts +116 -0
  64. package/dist/voice/index.js +261 -10
  65. package/dist/voice/index.js.map +3 -3
  66. package/dist/ws/close-codes.d.ts +23 -0
  67. package/dist/ws/decoder.d.ts +58 -0
  68. package/dist/ws/heartbeat.d.ts +87 -0
  69. package/dist/ws/index.d.ts +24 -32
  70. package/dist/ws/index.js +920 -318
  71. package/dist/ws/index.js.map +15 -5
  72. package/dist/ws/opcodes.d.ts +14 -0
  73. package/dist/ws/protocol.d.ts +110 -0
  74. package/dist/ws/reconnect.d.ts +108 -0
  75. package/dist/ws/send-budget.d.ts +17 -0
  76. package/dist/ws/session.d.ts +100 -0
  77. package/dist/ws/state.d.ts +27 -0
  78. package/dist/ws/transport.d.ts +80 -0
  79. package/package.json +23 -22
  80. package/dist/structures/permissions.d.ts +0 -73
@@ -1,9 +1,9 @@
1
1
  // @bun
2
2
  // packages/core/src/permissions.ts
3
3
  var PermissionOverwriteType;
4
- ((PermissionOverwriteType2) => {
5
- PermissionOverwriteType2[PermissionOverwriteType2["Role"] = 0] = "Role";
6
- PermissionOverwriteType2[PermissionOverwriteType2["Member"] = 1] = "Member";
4
+ ((PermissionOverwriteType) => {
5
+ PermissionOverwriteType[PermissionOverwriteType["Role"] = 0] = "Role";
6
+ PermissionOverwriteType[PermissionOverwriteType["Member"] = 1] = "Member";
7
7
  })(PermissionOverwriteType ||= {});
8
8
  var Permission = {
9
9
  createInstantInvite: 1n << 0n,
@@ -107,58 +107,57 @@ var Permissions = {
107
107
  SendPolls: 1n << 49n,
108
108
  UseExternalApps: 1n << 50n
109
109
  };
110
- var PermissionFlagsBits;
111
- ((PermissionFlagsBits2) => {
112
- PermissionFlagsBits2["CreateInstantInvite"] = "1";
113
- PermissionFlagsBits2["KickMembers"] = "2";
114
- PermissionFlagsBits2["BanMembers"] = "4";
115
- PermissionFlagsBits2["Administrator"] = "8";
116
- PermissionFlagsBits2["ManageChannels"] = "16";
117
- PermissionFlagsBits2["ManageGuild"] = "32";
118
- PermissionFlagsBits2["AddReactions"] = "64";
119
- PermissionFlagsBits2["ViewAuditLog"] = "128";
120
- PermissionFlagsBits2["PrioritySpeaker"] = "256";
121
- PermissionFlagsBits2["Stream"] = "512";
122
- PermissionFlagsBits2["ViewChannel"] = "1024";
123
- PermissionFlagsBits2["SendMessages"] = "2048";
124
- PermissionFlagsBits2["SendTTSMessages"] = "4096";
125
- PermissionFlagsBits2["ManageMessages"] = "8192";
126
- PermissionFlagsBits2["EmbedLinks"] = "16384";
127
- PermissionFlagsBits2["AttachFiles"] = "32768";
128
- PermissionFlagsBits2["ReadMessageHistory"] = "65536";
129
- PermissionFlagsBits2["MentionEveryone"] = "131072";
130
- PermissionFlagsBits2["UseExternalEmojis"] = "262144";
131
- PermissionFlagsBits2["ViewGuildInsights"] = "524288";
132
- PermissionFlagsBits2["Connect"] = "1048576";
133
- PermissionFlagsBits2["Speak"] = "2097152";
134
- PermissionFlagsBits2["MuteMembers"] = "4194304";
135
- PermissionFlagsBits2["DeafenMembers"] = "8388608";
136
- PermissionFlagsBits2["MoveMembers"] = "16777216";
137
- PermissionFlagsBits2["UseVAD"] = "33554432";
138
- PermissionFlagsBits2["ChangeNickname"] = "67108864";
139
- PermissionFlagsBits2["ManageNicknames"] = "134217728";
140
- PermissionFlagsBits2["ManageRoles"] = "268435456";
141
- PermissionFlagsBits2["ManageWebhooks"] = "536870912";
142
- PermissionFlagsBits2["ManageGuildExpressions"] = "1073741824";
143
- PermissionFlagsBits2["UseApplicationCommands"] = "2147483648";
144
- PermissionFlagsBits2["RequestToSpeak"] = "4294967296";
145
- PermissionFlagsBits2["ManageEvents"] = "8589934592";
146
- PermissionFlagsBits2["ManageThreads"] = "17179869184";
147
- PermissionFlagsBits2["CreatePublicThreads"] = "34359738368";
148
- PermissionFlagsBits2["CreatePrivateThreads"] = "68719476736";
149
- PermissionFlagsBits2["UseExternalStickers"] = "137438953472";
150
- PermissionFlagsBits2["SendMessagesInThreads"] = "274877906944";
151
- PermissionFlagsBits2["UseEmbeddedActivities"] = "549755813888";
152
- PermissionFlagsBits2["ModerateMembers"] = "1099511627776";
153
- PermissionFlagsBits2["ViewCreatorMonetizationAnalytics"] = "2199023255552";
154
- PermissionFlagsBits2["UseSoundboard"] = "4398046511104";
155
- PermissionFlagsBits2["CreateGuildExpressions"] = "8796093022208";
156
- PermissionFlagsBits2["CreateEvents"] = "17592186044416";
157
- PermissionFlagsBits2["UseExternalSounds"] = "35184372088832";
158
- PermissionFlagsBits2["SendVoiceMessages"] = "70368744177664";
159
- PermissionFlagsBits2["SendPolls"] = "562949953421312";
160
- PermissionFlagsBits2["UseExternalApps"] = "1125899906842624";
161
- })(PermissionFlagsBits ||= {});
110
+ var PermissionFlagsBits = {
111
+ CreateInstantInvite: 1n << 0n,
112
+ KickMembers: 1n << 1n,
113
+ BanMembers: 1n << 2n,
114
+ Administrator: 1n << 3n,
115
+ ManageChannels: 1n << 4n,
116
+ ManageGuild: 1n << 5n,
117
+ AddReactions: 1n << 6n,
118
+ ViewAuditLog: 1n << 7n,
119
+ PrioritySpeaker: 1n << 8n,
120
+ Stream: 1n << 9n,
121
+ ViewChannel: 1n << 10n,
122
+ SendMessages: 1n << 11n,
123
+ SendTTSMessages: 1n << 12n,
124
+ ManageMessages: 1n << 13n,
125
+ EmbedLinks: 1n << 14n,
126
+ AttachFiles: 1n << 15n,
127
+ ReadMessageHistory: 1n << 16n,
128
+ MentionEveryone: 1n << 17n,
129
+ UseExternalEmojis: 1n << 18n,
130
+ ViewGuildInsights: 1n << 19n,
131
+ Connect: 1n << 20n,
132
+ Speak: 1n << 21n,
133
+ MuteMembers: 1n << 22n,
134
+ DeafenMembers: 1n << 23n,
135
+ MoveMembers: 1n << 24n,
136
+ UseVAD: 1n << 25n,
137
+ ChangeNickname: 1n << 26n,
138
+ ManageNicknames: 1n << 27n,
139
+ ManageRoles: 1n << 28n,
140
+ ManageWebhooks: 1n << 29n,
141
+ ManageGuildExpressions: 1n << 30n,
142
+ UseApplicationCommands: 1n << 31n,
143
+ RequestToSpeak: 1n << 32n,
144
+ ManageEvents: 1n << 33n,
145
+ ManageThreads: 1n << 34n,
146
+ CreatePublicThreads: 1n << 35n,
147
+ CreatePrivateThreads: 1n << 36n,
148
+ UseExternalStickers: 1n << 37n,
149
+ SendMessagesInThreads: 1n << 38n,
150
+ UseEmbeddedActivities: 1n << 39n,
151
+ ModerateMembers: 1n << 40n,
152
+ ViewCreatorMonetizationAnalytics: 1n << 41n,
153
+ UseSoundboard: 1n << 42n,
154
+ CreateGuildExpressions: 1n << 43n,
155
+ CreateEvents: 1n << 44n,
156
+ UseExternalSounds: 1n << 45n,
157
+ SendVoiceMessages: 1n << 46n,
158
+ SendPolls: 1n << 49n,
159
+ UseExternalApps: 1n << 50n
160
+ };
162
161
 
163
162
  class PermissionSet {
164
163
  bitfield;
@@ -403,7 +402,7 @@ class PermissionSet {
403
402
  if (permission in Permissions)
404
403
  return Permissions[permission];
405
404
  if (permission in PermissionFlagsBits)
406
- return BigInt(PermissionFlagsBits[permission]);
405
+ return PermissionFlagsBits[permission];
407
406
  const lower = permission.charAt(0).toLowerCase() + permission.slice(1);
408
407
  if (lower in Permission)
409
408
  return Permission[lower];
@@ -422,71 +421,147 @@ class PermissionsBitField extends PermissionSet {
422
421
  }
423
422
  // packages/core/src/events.ts
424
423
  var ClientEvent;
425
- ((ClientEvent2) => {
426
- ClientEvent2["Ready"] = "ready";
427
- ClientEvent2["Raw"] = "raw";
428
- ClientEvent2["Error"] = "error";
429
- ClientEvent2["Open"] = "open";
430
- ClientEvent2["Close"] = "close";
431
- ClientEvent2["MessageCreate"] = "messageCreate";
432
- ClientEvent2["MessageUpdate"] = "messageUpdate";
433
- ClientEvent2["MessageDelete"] = "messageDelete";
434
- ClientEvent2["MessageDeleteBulk"] = "messageDeleteBulk";
435
- ClientEvent2["MessageReactionAdd"] = "messageReactionAdd";
436
- ClientEvent2["MessageReactionRemove"] = "messageReactionRemove";
437
- ClientEvent2["MessageReactionRemoveAll"] = "messageReactionRemoveAll";
438
- ClientEvent2["MessageReactionRemoveEmoji"] = "messageReactionRemoveEmoji";
439
- ClientEvent2["MessagePollVoteAdd"] = "messagePollVoteAdd";
440
- ClientEvent2["MessagePollVoteRemove"] = "messagePollVoteRemove";
441
- ClientEvent2["GuildCreate"] = "guildCreate";
442
- ClientEvent2["GuildUpdate"] = "guildUpdate";
443
- ClientEvent2["GuildDelete"] = "guildDelete";
444
- ClientEvent2["GuildAvailable"] = "guildAvailable";
445
- ClientEvent2["GuildUnavailable"] = "guildUnavailable";
446
- ClientEvent2["GuildMemberAdd"] = "guildMemberAdd";
447
- ClientEvent2["GuildMemberUpdate"] = "guildMemberUpdate";
448
- ClientEvent2["GuildMemberRemove"] = "guildMemberRemove";
449
- ClientEvent2["GuildMembersChunk"] = "guildMembersChunk";
450
- ClientEvent2["GuildBanAdd"] = "guildBanAdd";
451
- ClientEvent2["GuildBanRemove"] = "guildBanRemove";
452
- ClientEvent2["GuildRoleCreate"] = "guildRoleCreate";
453
- ClientEvent2["GuildRoleUpdate"] = "guildRoleUpdate";
454
- ClientEvent2["GuildRoleDelete"] = "guildRoleDelete";
455
- ClientEvent2["GuildEmojisUpdate"] = "guildEmojisUpdate";
456
- ClientEvent2["GuildStickersUpdate"] = "guildStickersUpdate";
457
- ClientEvent2["GuildIntegrationsUpdate"] = "guildIntegrationsUpdate";
458
- ClientEvent2["GuildScheduledEventCreate"] = "guildScheduledEventCreate";
459
- ClientEvent2["GuildScheduledEventUpdate"] = "guildScheduledEventUpdate";
460
- ClientEvent2["GuildScheduledEventDelete"] = "guildScheduledEventDelete";
461
- ClientEvent2["GuildScheduledEventUserAdd"] = "guildScheduledEventUserAdd";
462
- ClientEvent2["GuildScheduledEventUserRemove"] = "guildScheduledEventUserRemove";
463
- ClientEvent2["AutoModerationRuleCreate"] = "autoModerationRuleCreate";
464
- ClientEvent2["AutoModerationRuleUpdate"] = "autoModerationRuleUpdate";
465
- ClientEvent2["AutoModerationRuleDelete"] = "autoModerationRuleDelete";
466
- ClientEvent2["AutoModerationActionExecution"] = "autoModerationActionExecution";
467
- ClientEvent2["ChannelCreate"] = "channelCreate";
468
- ClientEvent2["ChannelUpdate"] = "channelUpdate";
469
- ClientEvent2["ChannelDelete"] = "channelDelete";
470
- ClientEvent2["ChannelPinsUpdate"] = "channelPinsUpdate";
471
- ClientEvent2["ThreadCreate"] = "threadCreate";
472
- ClientEvent2["ThreadUpdate"] = "threadUpdate";
473
- ClientEvent2["ThreadDelete"] = "threadDelete";
474
- ClientEvent2["ThreadListSync"] = "threadListSync";
475
- ClientEvent2["ThreadMembersUpdate"] = "threadMembersUpdate";
476
- ClientEvent2["ThreadMemberUpdate"] = "threadMemberUpdate";
477
- ClientEvent2["StageInstanceCreate"] = "stageInstanceCreate";
478
- ClientEvent2["StageInstanceUpdate"] = "stageInstanceUpdate";
479
- ClientEvent2["StageInstanceDelete"] = "stageInstanceDelete";
480
- ClientEvent2["InviteCreate"] = "inviteCreate";
481
- ClientEvent2["InviteDelete"] = "inviteDelete";
482
- ClientEvent2["WebhooksUpdate"] = "webhooksUpdate";
483
- ClientEvent2["VoiceStateUpdate"] = "voiceStateUpdate";
484
- ClientEvent2["VoiceServerUpdate"] = "voiceServerUpdate";
485
- ClientEvent2["PresenceUpdate"] = "presenceUpdate";
486
- ClientEvent2["TypingStart"] = "typingStart";
487
- ClientEvent2["InteractionCreate"] = "interactionCreate";
424
+ ((ClientEvent) => {
425
+ ClientEvent["Ready"] = "ready";
426
+ ClientEvent["Resumed"] = "resumed";
427
+ ClientEvent["InvalidSession"] = "invalidSession";
428
+ ClientEvent["Raw"] = "raw";
429
+ ClientEvent["Error"] = "error";
430
+ ClientEvent["Open"] = "open";
431
+ ClientEvent["Close"] = "close";
432
+ ClientEvent["MessageCreate"] = "messageCreate";
433
+ ClientEvent["MessageUpdate"] = "messageUpdate";
434
+ ClientEvent["MessageDelete"] = "messageDelete";
435
+ ClientEvent["MessageDeleteBulk"] = "messageDeleteBulk";
436
+ ClientEvent["MessageReactionAdd"] = "messageReactionAdd";
437
+ ClientEvent["MessageReactionRemove"] = "messageReactionRemove";
438
+ ClientEvent["MessageReactionRemoveAll"] = "messageReactionRemoveAll";
439
+ ClientEvent["MessageReactionRemoveEmoji"] = "messageReactionRemoveEmoji";
440
+ ClientEvent["MessagePollVoteAdd"] = "messagePollVoteAdd";
441
+ ClientEvent["MessagePollVoteRemove"] = "messagePollVoteRemove";
442
+ ClientEvent["GuildCreate"] = "guildCreate";
443
+ ClientEvent["GuildUpdate"] = "guildUpdate";
444
+ ClientEvent["GuildDelete"] = "guildDelete";
445
+ ClientEvent["GuildAvailable"] = "guildAvailable";
446
+ ClientEvent["GuildUnavailable"] = "guildUnavailable";
447
+ ClientEvent["GuildMemberAdd"] = "guildMemberAdd";
448
+ ClientEvent["GuildMemberUpdate"] = "guildMemberUpdate";
449
+ ClientEvent["GuildMemberRemove"] = "guildMemberRemove";
450
+ ClientEvent["GuildMembersChunk"] = "guildMembersChunk";
451
+ ClientEvent["GuildBanAdd"] = "guildBanAdd";
452
+ ClientEvent["GuildBanRemove"] = "guildBanRemove";
453
+ ClientEvent["GuildRoleCreate"] = "guildRoleCreate";
454
+ ClientEvent["GuildRoleUpdate"] = "guildRoleUpdate";
455
+ ClientEvent["GuildRoleDelete"] = "guildRoleDelete";
456
+ ClientEvent["GuildEmojisUpdate"] = "guildEmojisUpdate";
457
+ ClientEvent["GuildStickersUpdate"] = "guildStickersUpdate";
458
+ ClientEvent["GuildIntegrationsUpdate"] = "guildIntegrationsUpdate";
459
+ ClientEvent["GuildScheduledEventCreate"] = "guildScheduledEventCreate";
460
+ ClientEvent["GuildScheduledEventUpdate"] = "guildScheduledEventUpdate";
461
+ ClientEvent["GuildScheduledEventDelete"] = "guildScheduledEventDelete";
462
+ ClientEvent["GuildScheduledEventUserAdd"] = "guildScheduledEventUserAdd";
463
+ ClientEvent["GuildScheduledEventUserRemove"] = "guildScheduledEventUserRemove";
464
+ ClientEvent["AutoModerationRuleCreate"] = "autoModerationRuleCreate";
465
+ ClientEvent["AutoModerationRuleUpdate"] = "autoModerationRuleUpdate";
466
+ ClientEvent["AutoModerationRuleDelete"] = "autoModerationRuleDelete";
467
+ ClientEvent["AutoModerationActionExecution"] = "autoModerationActionExecution";
468
+ ClientEvent["ChannelCreate"] = "channelCreate";
469
+ ClientEvent["ChannelUpdate"] = "channelUpdate";
470
+ ClientEvent["ChannelDelete"] = "channelDelete";
471
+ ClientEvent["ChannelPinsUpdate"] = "channelPinsUpdate";
472
+ ClientEvent["ThreadCreate"] = "threadCreate";
473
+ ClientEvent["ThreadUpdate"] = "threadUpdate";
474
+ ClientEvent["ThreadDelete"] = "threadDelete";
475
+ ClientEvent["ThreadListSync"] = "threadListSync";
476
+ ClientEvent["ThreadMembersUpdate"] = "threadMembersUpdate";
477
+ ClientEvent["ThreadMemberUpdate"] = "threadMemberUpdate";
478
+ ClientEvent["StageInstanceCreate"] = "stageInstanceCreate";
479
+ ClientEvent["StageInstanceUpdate"] = "stageInstanceUpdate";
480
+ ClientEvent["StageInstanceDelete"] = "stageInstanceDelete";
481
+ ClientEvent["InviteCreate"] = "inviteCreate";
482
+ ClientEvent["InviteDelete"] = "inviteDelete";
483
+ ClientEvent["WebhooksUpdate"] = "webhooksUpdate";
484
+ ClientEvent["VoiceStateUpdate"] = "voiceStateUpdate";
485
+ ClientEvent["VoiceServerUpdate"] = "voiceServerUpdate";
486
+ ClientEvent["PresenceUpdate"] = "presenceUpdate";
487
+ ClientEvent["TypingStart"] = "typingStart";
488
+ ClientEvent["InteractionCreate"] = "interactionCreate";
488
489
  })(ClientEvent ||= {});
489
- // packages/types/src/index.ts
490
+ // packages/core/src/collector.ts
491
+ import { EventEmitter } from "events";
492
+
493
+ class Collector extends EventEmitter {
494
+ collected = new Map;
495
+ options;
496
+ ended = false;
497
+ endReason;
498
+ totalProcessed = 0;
499
+ #timeoutTimer;
500
+ constructor(options = {}) {
501
+ super();
502
+ this.options = options;
503
+ if (options.time && options.time > 0) {
504
+ this.#timeoutTimer = setTimeout(() => {
505
+ this.stop("time");
506
+ }, options.time);
507
+ }
508
+ }
509
+ async handle(key, item) {
510
+ if (this.ended)
511
+ return false;
512
+ this.totalProcessed++;
513
+ const reachedProcessed = !!this.options.maxProcessed && this.totalProcessed >= this.options.maxProcessed;
514
+ let passed = true;
515
+ if (this.options.filter)
516
+ passed = !!await this.options.filter(item);
517
+ if (this.ended)
518
+ return false;
519
+ if (!passed) {
520
+ if (reachedProcessed)
521
+ this.stop("processedLimit");
522
+ return false;
523
+ }
524
+ this.collected.set(key, item);
525
+ this.emit("collect", item);
526
+ if (this.options.max && this.collected.size >= this.options.max) {
527
+ this.stop("limit");
528
+ return true;
529
+ }
530
+ if (reachedProcessed) {
531
+ this.stop("processedLimit");
532
+ return true;
533
+ }
534
+ return true;
535
+ }
536
+ stop(reason = "user") {
537
+ if (this.ended)
538
+ return;
539
+ this.ended = true;
540
+ this.endReason = reason;
541
+ if (this.#timeoutTimer)
542
+ clearTimeout(this.#timeoutTimer);
543
+ this.emit("end", this.collected, reason);
544
+ this.removeAllListeners();
545
+ }
546
+ next() {
547
+ return new Promise((resolve, reject) => {
548
+ if (this.ended) {
549
+ return reject(new Error(`Collector already ended: ${this.endReason ?? "unknown"}`));
550
+ }
551
+ const onCollect = (item) => {
552
+ this.off("end", onEnd);
553
+ resolve(item);
554
+ };
555
+ const onEnd = (_collected, reason) => {
556
+ this.off("collect", onCollect);
557
+ reject(new Error(`Collector ended before item was collected: ${reason}`));
558
+ };
559
+ this.once("collect", onCollect);
560
+ this.once("end", onEnd);
561
+ });
562
+ }
563
+ }
564
+ // packages/types/src/gateway.ts
490
565
  var GatewayIntentBits = {
491
566
  Guilds: 1 << 0,
492
567
  GuildMembers: 1 << 1,
@@ -542,6 +617,8 @@ var Intents = IntentBits;
542
617
  function resolveGatewayIntents(intents) {
543
618
  if (typeof intents === "number")
544
619
  return intents;
620
+ if (intents && typeof intents === "object" && !Array.isArray(intents))
621
+ return intents.bitfield;
545
622
  if (Array.isArray(intents)) {
546
623
  return intents.reduce((acc, intent) => {
547
624
  return acc | resolveGatewayIntents(intent);
@@ -561,6 +638,44 @@ function resolveGatewayIntents(intents) {
561
638
  }
562
639
  return 0;
563
640
  }
641
+
642
+ class IntentsBitField {
643
+ static Flags = GatewayIntentBits;
644
+ bitfield;
645
+ constructor(bits = 0) {
646
+ this.bitfield = resolveGatewayIntents(bits);
647
+ }
648
+ has(bits) {
649
+ const resolved = resolveGatewayIntents(bits);
650
+ return (this.bitfield & resolved) === resolved;
651
+ }
652
+ any(bits) {
653
+ return (this.bitfield & resolveGatewayIntents(bits)) !== 0;
654
+ }
655
+ add(...bits) {
656
+ for (const bit of bits)
657
+ this.bitfield |= resolveGatewayIntents(bit);
658
+ return this;
659
+ }
660
+ remove(...bits) {
661
+ for (const bit of bits)
662
+ this.bitfield &= ~resolveGatewayIntents(bit);
663
+ return this;
664
+ }
665
+ toArray() {
666
+ return Object.keys(GatewayIntentBits).filter((name) => this.has(GatewayIntentBits[name]));
667
+ }
668
+ equals(bits) {
669
+ return this.bitfield === resolveGatewayIntents(bits);
670
+ }
671
+ valueOf() {
672
+ return this.bitfield;
673
+ }
674
+ toJSON() {
675
+ return this.bitfield;
676
+ }
677
+ }
678
+ // packages/types/src/index.ts
564
679
  var InteractionResponseType = {
565
680
  Pong: 1,
566
681
  ChannelMessage: 4,
@@ -716,6 +831,75 @@ class Collection extends Map {
716
831
  }
717
832
  return result;
718
833
  }
834
+ partition(predicate) {
835
+ const pass = new Collection;
836
+ const fail = new Collection;
837
+ for (const [key, value] of this) {
838
+ if (predicate(value, key, this))
839
+ pass.set(key, value);
840
+ else
841
+ fail.set(key, value);
842
+ }
843
+ return [pass, fail];
844
+ }
845
+ tap(fn) {
846
+ fn(this);
847
+ return this;
848
+ }
849
+ flatMap(transform) {
850
+ const result = [];
851
+ for (const [key, value] of this) {
852
+ for (const item of transform(value, key, this))
853
+ result.push(item);
854
+ }
855
+ return result;
856
+ }
857
+ reduce(fn, initialValue) {
858
+ let acc = initialValue;
859
+ for (const [key, value] of this)
860
+ acc = fn(acc, value, key, this);
861
+ return acc;
862
+ }
863
+ sorted(comparator) {
864
+ const decorated = [...this.entries()].map((entry, index) => ({
865
+ entry,
866
+ index
867
+ }));
868
+ if (comparator)
869
+ decorated.sort((x, y) => {
870
+ const comparison = comparator(x.entry[1], y.entry[1], x.entry[0], y.entry[0]);
871
+ return comparison !== 0 ? comparison : x.index - y.index;
872
+ });
873
+ const result = new Collection;
874
+ for (const { entry } of decorated)
875
+ result.set(entry[0], entry[1]);
876
+ return result;
877
+ }
878
+ random() {
879
+ const arr = [...this.values()];
880
+ if (!arr.length)
881
+ return;
882
+ return arr[Math.floor(Math.random() * arr.length)];
883
+ }
884
+ randomKey() {
885
+ const arr = [...this.keys()];
886
+ if (!arr.length)
887
+ return;
888
+ return arr[Math.floor(Math.random() * arr.length)];
889
+ }
890
+ at(index) {
891
+ const arr = [...this.values()];
892
+ const i = index < 0 ? arr.length + index : index;
893
+ return arr[i];
894
+ }
895
+ keyAt(index) {
896
+ const arr = [...this.keys()];
897
+ const i = index < 0 ? arr.length + index : index;
898
+ return arr[i];
899
+ }
900
+ toJSON() {
901
+ return [...this.entries()];
902
+ }
719
903
  }
720
904
 
721
905
  // packages/managers/src/base.ts
@@ -787,7 +971,7 @@ class ResourceManager extends Manager {
787
971
  // packages/rest/package.json
788
972
  var package_default = {
789
973
  name: "@lunibee/rest",
790
- version: "0.1.6",
974
+ version: "0.1.8",
791
975
  type: "module",
792
976
  module: "./src/index.ts",
793
977
  types: "./src/index.ts",
@@ -796,60 +980,57 @@ var package_default = {
796
980
  }
797
981
  };
798
982
 
799
- // packages/rest/src/routes.ts
800
- var Routes = {
801
- user: () => "/users/@me",
802
- userById: (userId) => `/users/${snowflake(userId, "User ID")}`,
803
- guild: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}`,
804
- guildPreview: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/preview`,
805
- guildActiveThreads: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/threads/active`,
806
- guildWebhooks: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/webhooks`,
807
- guildInvites: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/invites`,
808
- guildChannels: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/channels`,
809
- guildMember: (guildId, userId) => `/guilds/${snowflake(guildId, "Guild ID")}/members/${snowflake(userId, "User ID")}`,
810
- guildRoles: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/roles`,
811
- guildRole: (guildId, roleId) => `/guilds/${snowflake(guildId, "Guild ID")}/roles/${snowflake(roleId, "Role ID")}`,
812
- guildMemberRole: (guildId, userId, roleId) => `/guilds/${snowflake(guildId, "Guild ID")}/members/${snowflake(userId, "User ID")}/roles/${snowflake(roleId, "Role ID")}`,
813
- guildBans: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/bans`,
814
- guildBan: (guildId, userId) => `/guilds/${snowflake(guildId, "Guild ID")}/bans/${snowflake(userId, "User ID")}`,
815
- channel: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}`,
816
- channelMessages: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/messages`,
817
- message: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/${snowflake(messageId, "Message ID")}`,
818
- crosspostMessage: (channelId, messageId) => `${Routes.message(channelId, messageId)}/crosspost`,
819
- messageReactions: (channelId, messageId, emoji) => `${Routes.message(channelId, messageId)}/reactions/${encodeURIComponent(emoji)}`,
820
- messageReactionsAll: (channelId, messageId) => `${Routes.message(channelId, messageId)}/reactions`,
821
- channelPins: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/pins`,
822
- channelPin: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/pins/${snowflake(messageId, "Message ID")}`,
823
- messageThread: (channelId, messageId) => `${Routes.message(channelId, messageId)}/threads`,
824
- channelBulkDelete: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/bulk-delete`,
825
- channelWebhooks: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/webhooks`,
826
- channelInvites: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/invites`,
827
- webhook: (webhookId, token) => `/webhooks/${snowflake(webhookId, "Webhook ID")}${token ? `/${encodeURIComponent(token)}` : ""}`,
828
- webhookMessage: (webhookId, token, messageId) => `/webhooks/${snowflake(webhookId, "Webhook ID")}/${encodeURIComponent(token)}/messages/${snowflake(messageId, "Message ID")}`,
829
- applicationCommands: (applicationId) => `/applications/${snowflake(applicationId, "Application ID")}/commands`,
830
- applicationCommand: (applicationId, commandId) => `/applications/${snowflake(applicationId, "Application ID")}/commands/${snowflake(commandId, "Command ID")}`,
831
- guildApplicationCommands: (applicationId, guildId) => `/applications/${snowflake(applicationId, "Application ID")}/guilds/${snowflake(guildId, "Guild ID")}/commands`,
832
- guildApplicationCommand: (applicationId, guildId, commandId) => `/applications/${snowflake(applicationId, "Application ID")}/guilds/${snowflake(guildId, "Guild ID")}/commands/${snowflake(commandId, "Command ID")}`,
833
- interactionCallback: (interactionId, token) => `/interactions/${snowflake(interactionId, "Interaction ID")}/${encodeURIComponent(token)}/callback`,
834
- interactionOriginalResponse: (applicationId, token) => `/webhooks/${snowflake(applicationId, "Application ID")}/${encodeURIComponent(token)}/messages/@original`,
835
- channelPublicArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads/archived/public`,
836
- channelPrivateArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads/archived/private`,
837
- threadMembers: (threadId) => `/channels/${snowflake(threadId, "Thread ID")}/thread-members`,
838
- guildScheduledEvents: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events`,
839
- guildScheduledEvent: (guildId, eventId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events/${snowflake(eventId, "Event ID")}`,
840
- guildAutoModerationRules: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/auto-moderation/rules`,
841
- guildAutoModerationRule: (guildId, ruleId) => `/guilds/${snowflake(guildId, "Guild ID")}/auto-moderation/rules/${snowflake(ruleId, "Rule ID")}`,
842
- voiceRegions: () => "/voice/regions",
843
- gateway: () => "/gateway",
844
- gatewayBot: () => "/gateway/bot"
845
- };
846
- function snowflake(value, field) {
847
- if (!/^\d{1,20}$/.test(value))
848
- throw new TypeError(`${field} must be a valid Discord snowflake.`);
849
- return value;
983
+ // packages/rest/src/store.ts
984
+ class MemoryRateLimitStore {
985
+ #buckets = new Map;
986
+ #routeBuckets = new Map;
987
+ #globalResetAt = 0;
988
+ static #PRUNE_INTERVAL = 60000;
989
+ #lastPruneAt = 0;
990
+ getBucketHash(route) {
991
+ return this.#routeBuckets.get(route);
992
+ }
993
+ setBucketHash(route, hash) {
994
+ this.#routeBuckets.set(route, hash);
995
+ }
996
+ getBucket(key) {
997
+ return this.#buckets.get(key);
998
+ }
999
+ reserve(key) {
1000
+ const state = this.#buckets.get(key);
1001
+ const now = Date.now();
1002
+ if (!state || state.resetAt <= now)
1003
+ return { granted: true };
1004
+ if (state.remaining > 0) {
1005
+ state.remaining -= 1;
1006
+ return { granted: true };
1007
+ }
1008
+ return { granted: false, retryAfterMs: state.resetAt - now };
1009
+ }
1010
+ updateBucket(key, state) {
1011
+ this.#buckets.set(key, state);
1012
+ const now = Date.now();
1013
+ if (now - this.#lastPruneAt >= MemoryRateLimitStore.#PRUNE_INTERVAL) {
1014
+ this.#lastPruneAt = now;
1015
+ this.prune();
1016
+ }
1017
+ }
1018
+ prune(maxAge = 0) {
1019
+ const cutoff = Date.now() - Math.max(0, maxAge);
1020
+ for (const [key, state] of this.#buckets) {
1021
+ if (state.resetAt <= cutoff)
1022
+ this.#buckets.delete(key);
1023
+ }
1024
+ }
1025
+ getGlobalReset() {
1026
+ return this.#globalResetAt;
1027
+ }
1028
+ setGlobalReset(resetAt) {
1029
+ this.#globalResetAt = resetAt;
1030
+ }
850
1031
  }
851
1032
 
852
- // packages/rest/src/index.ts
1033
+ // packages/rest/src/errors.ts
853
1034
  class RESTError extends Error {
854
1035
  status;
855
1036
  code;
@@ -866,196 +1047,254 @@ class RESTError extends Error {
866
1047
  this.path = options.path;
867
1048
  }
868
1049
  }
869
- var LIBRARY_VERSION = package_default.version;
870
- var LIBRARY_URL = "https://github.com/Ekretos/lunibee";
871
- var USER_AGENT = `DiscordBot (${LIBRARY_URL}, ${package_default.version})`;
872
- function createRetryPolicy(maxRetries = 2) {
873
- if (!Number.isFinite(maxRetries) || maxRetries < 0)
874
- throw new TypeError("maxRetries must be a non-negative finite number.");
1050
+ function abortError(path, reason) {
1051
+ return new RESTError(reason instanceof Error ? reason.message : "Discord REST request was aborted", 0, undefined, undefined, { path, cause: reason });
1052
+ }
1053
+ async function sleep(delay, signal, path) {
1054
+ if (delay <= 0)
1055
+ return;
1056
+ await new Promise((resolve, reject) => {
1057
+ const timer = setTimeout(resolve, delay);
1058
+ const abort = () => {
1059
+ clearTimeout(timer);
1060
+ reject(abortError(path, signal?.reason));
1061
+ };
1062
+ if (signal?.aborted)
1063
+ return abort();
1064
+ signal?.addEventListener("abort", abort, { once: true });
1065
+ });
1066
+ }
1067
+ async function abortable(promise, signal, path) {
1068
+ if (!signal)
1069
+ return promise;
1070
+ if (signal.aborted)
1071
+ throw abortError(path, signal.reason);
1072
+ return Promise.race([
1073
+ promise,
1074
+ new Promise((_, reject) => signal.addEventListener("abort", () => reject(abortError(path, signal.reason)), { once: true }))
1075
+ ]);
1076
+ }
1077
+
1078
+ // packages/rest/src/route.ts
1079
+ function normalizeRoutePath(path) {
1080
+ const queryIndex = path.indexOf("?");
1081
+ const routePath = queryIndex === -1 ? path : path.slice(0, queryIndex);
1082
+ return routePath.replace(/\/\d+(?=\/|$)/g, "/:id");
1083
+ }
1084
+ function majorParameter(path) {
1085
+ const match = /^\/(channels|guilds|webhooks)\/(\d+)(?:\/([^/?]+))?/.exec(path);
1086
+ if (!match)
1087
+ return "@none";
1088
+ if (match[1] === "webhooks" && match[3] !== undefined)
1089
+ return `${match[2]}:${match[3]}`;
1090
+ return match[2];
1091
+ }
1092
+ function scopeBucket(hashOrRoute, major) {
1093
+ return `${hashOrRoute}|${major}`;
1094
+ }
1095
+ function createRouteKey(method, path) {
1096
+ const normalizedMethod = method.toUpperCase();
875
1097
  return {
876
- maxRetries: Math.floor(maxRetries),
877
- shouldRetry(method, status) {
878
- const normalized = method.toUpperCase();
879
- return status === 429 || status >= 500 && status <= 599 && ["GET", "HEAD", "PUT", "DELETE"].includes(normalized);
880
- },
881
- getDelay(attempt, retryAfter) {
882
- if (retryAfter !== undefined)
883
- return Math.max(0, retryAfter * 1000);
884
- return Math.min(1e4, 500 * 2 ** attempt) + Math.random() * 250;
885
- }
1098
+ method: normalizedMethod,
1099
+ path,
1100
+ route: `${normalizedMethod}:${normalizeRoutePath(path)}`,
1101
+ major: majorParameter(path)
886
1102
  };
887
1103
  }
1104
+ function serializeQuery(query) {
1105
+ if (query === undefined)
1106
+ return "";
1107
+ if (typeof query === "string")
1108
+ return query.length === 0 || query.startsWith("?") ? query : `?${query}`;
1109
+ const params = query instanceof URLSearchParams ? query : new URLSearchParams(Object.entries(query).flatMap(([key, value]) => value === undefined || value === null ? [] : [[key, String(value)]]));
1110
+ const serialized = params.toString();
1111
+ return serialized.length === 0 ? "" : `?${serialized}`;
1112
+ }
888
1113
 
889
- class REST {
890
- #baseURL;
891
- #timeout;
892
- #retryPolicy;
893
- #token;
894
- #globalResetAt = 0;
895
- #buckets = new Map;
896
- #routeBuckets = new Map;
897
- constructor(options = {}) {
898
- this.#token = options.token;
899
- this.#timeout = Math.max(1, options.timeout ?? 15000);
900
- this.#retryPolicy = options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);
901
- this.#baseURL = (options.baseURL ?? "https://discord.com/api/v10").replace(/\/$/, "");
902
- }
903
- setToken(token) {
904
- if (!token.trim())
905
- throw new TypeError("A Discord bot token is required.");
906
- this.#token = token;
1114
+ // packages/rest/src/scheduler.ts
1115
+ class RequestScheduler {
1116
+ static MAX_REMAPS = 2;
1117
+ #queues = new Map;
1118
+ get size() {
1119
+ return this.#queues.size;
1120
+ }
1121
+ async runResolved(resolveKey, signal, path, task) {
1122
+ let key = await resolveKey();
1123
+ for (let hop = 0;hop < RequestScheduler.MAX_REMAPS; hop++) {
1124
+ const outcome = await this.run(key, signal, path, async (release) => {
1125
+ const current = await resolveKey();
1126
+ if (current !== key)
1127
+ return { done: false, key: current };
1128
+ return { done: true, value: await task(key, release) };
1129
+ });
1130
+ if (outcome.done)
1131
+ return outcome.value;
1132
+ key = outcome.key;
1133
+ }
1134
+ return this.run(key, signal, path, (release) => task(key, release));
907
1135
  }
908
- async request(method, path, body, options = {}) {
909
- const normalizedMethod = method.toUpperCase();
910
- if (!normalizedMethod)
911
- throw new TypeError("REST method is required.");
912
- if (!path.startsWith("/"))
913
- throw new TypeError("REST paths must start with '/'.");
914
- const route = `${normalizedMethod}:${this.#normalizeRoute(path)}`;
915
- let bucketKey = this.#routeBuckets.get(route) ?? route;
916
- let bucket = this.#buckets.get(bucketKey) ?? {
917
- remaining: 1,
918
- resetAt: 0,
919
- queue: Promise.resolve()
920
- };
921
- this.#buckets.set(bucketKey, bucket);
922
- const previous = bucket.queue;
923
- let release;
924
- bucket.queue = new Promise((resolve) => {
925
- release = resolve;
1136
+ async run(key, signal, path, task) {
1137
+ const previous = this.#queues.get(key) ?? Promise.resolve();
1138
+ let open;
1139
+ const gate = new Promise((resolve) => {
1140
+ open = resolve;
926
1141
  });
927
- await this.#abortable(previous, options.signal, path);
1142
+ this.#queues.set(key, gate);
1143
+ let released = false;
1144
+ const release = () => {
1145
+ if (released)
1146
+ return;
1147
+ released = true;
1148
+ open();
1149
+ if (this.#queues.get(key) === gate)
1150
+ this.#queues.delete(key);
1151
+ };
928
1152
  try {
929
- for (let attempt = 0;attempt <= this.#retryPolicy.maxRetries; attempt++) {
930
- await this.#wait(bucket, options.signal, path);
931
- await this.#waitGlobal(options.signal, path);
932
- const controller = new AbortController;
933
- const onAbort = () => controller.abort(options.signal?.reason);
934
- if (options.signal?.aborted)
935
- throw this.#abortError(path, options.signal.reason);
936
- options.signal?.addEventListener("abort", onAbort, { once: true });
937
- const timer = setTimeout(() => controller.abort(new DOMException("REST request timeout", "TimeoutError")), this.#timeout);
938
- const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
939
- const headers = {
940
- ...this.#token ? { Authorization: `Bot ${this.#token}` } : {},
941
- "User-Agent": USER_AGENT
942
- };
943
- if (!isFormData)
944
- headers["Content-Type"] = "application/json";
945
- const fetchBody = isFormData ? body : body === undefined ? undefined : JSON.stringify(body);
946
- try {
947
- const response = await fetch(`${this.#baseURL}${path}`, {
948
- method: normalizedMethod,
949
- headers,
950
- body: fetchBody,
951
- signal: controller.signal
952
- });
953
- bucket = this.#update(response, bucket, route, bucketKey);
954
- bucketKey = this.#routeBuckets.get(route) ?? bucketKey;
955
- const payload = await this.#readPayload(response);
956
- if (response.ok)
957
- return response.status === 204 ? undefined : payload;
958
- const data = this.#errorData(payload);
959
- const retryAfter = response.status === 429 ? this.#retryAfter(response, data) : undefined;
960
- if (data.global && retryAfter !== undefined)
961
- this.#globalResetAt = Date.now() + retryAfter * 1000;
962
- if (attempt < this.#retryPolicy.maxRetries && this.#retryPolicy.shouldRetry(normalizedMethod, response.status)) {
963
- await this.#sleep(this.#retryPolicy.getDelay(attempt, retryAfter), options.signal, path);
964
- continue;
965
- }
966
- throw new RESTError(data.message ?? response.statusText ?? `Discord REST request failed with status ${response.status}`, response.status, data.code, payload, { method: normalizedMethod, path });
967
- } catch (error) {
968
- if (error instanceof RESTError)
969
- throw error;
970
- if (options.signal?.aborted)
971
- throw this.#abortError(path, options.signal.reason);
972
- const isTimeout = error instanceof DOMException && (error.name === "TimeoutError" || error.name === "AbortError");
973
- if (!isTimeout && attempt < this.#retryPolicy.maxRetries && this.#retryPolicy.shouldRetry(normalizedMethod, 0)) {
974
- await this.#sleep(this.#retryPolicy.getDelay(attempt), options.signal, path);
975
- continue;
976
- }
977
- 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 });
978
- } finally {
979
- clearTimeout(timer);
980
- options.signal?.removeEventListener("abort", onAbort);
981
- }
982
- }
983
- throw new RESTError("Discord REST request exhausted its retry attempts", 0, undefined, undefined, { method: normalizedMethod, path });
1153
+ await abortable(previous, signal, path);
1154
+ return await task(release);
984
1155
  } finally {
985
1156
  release();
986
1157
  }
987
1158
  }
988
- get(path, options) {
989
- return this.request("GET", path, undefined, options);
1159
+ }
1160
+
1161
+ // packages/rest/src/limiter.ts
1162
+ class RateLimiter {
1163
+ #store;
1164
+ #concurrent;
1165
+ constructor(store, options = {}) {
1166
+ this.#store = store;
1167
+ this.#concurrent = options.concurrent ?? false;
1168
+ }
1169
+ get store() {
1170
+ return this.#store;
1171
+ }
1172
+ async resolveBucketKey(route, major) {
1173
+ return scopeBucket(await this.#store.getBucketHash(route) ?? route, major);
1174
+ }
1175
+ get reserves() {
1176
+ return typeof this.#store.reserve === "function";
1177
+ }
1178
+ async acquire(bucketKey, signal, path) {
1179
+ await this.#waitGlobal(signal, path);
1180
+ if (!this.#store.reserve) {
1181
+ const bucket = await this.#store.getBucket(bucketKey);
1182
+ if (bucket) {
1183
+ const delay = bucket.resetAt - Date.now();
1184
+ if (bucket.remaining <= 0 && delay > 0)
1185
+ await sleep(delay, signal, path);
1186
+ }
1187
+ return;
1188
+ }
1189
+ for (let attempt = 0;attempt < 8; attempt++) {
1190
+ const reservation = await this.#store.reserve(bucketKey);
1191
+ if (reservation.granted)
1192
+ return;
1193
+ await sleep(Math.max(1, reservation.retryAfterMs), signal, path);
1194
+ await this.#waitGlobal(signal, path);
1195
+ }
990
1196
  }
991
- post(path, body, options) {
992
- return this.request("POST", path, body, options);
1197
+ async#waitGlobal(signal, path) {
1198
+ const globalDelay = await this.#store.getGlobalReset() - Date.now();
1199
+ if (globalDelay > 0)
1200
+ await sleep(globalDelay, signal, path);
993
1201
  }
994
- patch(path, body, options) {
995
- return this.request("PATCH", path, body, options);
1202
+ async noteGlobalReset(resetAt) {
1203
+ const current = await this.#store.getGlobalReset();
1204
+ await this.#store.setGlobalReset(Math.max(current, resetAt));
996
1205
  }
997
- put(path, body, options) {
998
- return this.request("PUT", path, body, options);
999
- }
1000
- delete(path, options) {
1001
- return this.request("DELETE", path, undefined, options);
1002
- }
1003
- #fileForm(payload, files) {
1004
- const form = new FormData;
1005
- form.append("payload_json", JSON.stringify(payload));
1006
- for (let i = 0;i < files.length; i++) {
1007
- const file = files[i];
1008
- const blob = file.data instanceof Blob ? file.data : new Blob([file.data], {
1009
- type: file.contentType ?? "application/octet-stream"
1010
- });
1011
- form.append(`files[${i}]`, blob, file.name);
1206
+ async applyResponse(response, bucketKey, route, major) {
1207
+ let currentKey = bucketKey;
1208
+ const serverBucket = response.headers.get("X-RateLimit-Bucket");
1209
+ if (serverBucket) {
1210
+ await this.#store.setBucketHash(route, serverBucket);
1211
+ currentKey = scopeBucket(serverBucket, major);
1012
1212
  }
1013
- return form;
1014
- }
1015
- postWithFiles(path, payload, files, options) {
1016
- return this.request("POST", path, this.#fileForm(payload, files), options);
1017
- }
1018
- patchWithFiles(path, payload, files, options) {
1019
- return this.request("PATCH", path, this.#fileForm(payload, files), options);
1213
+ const stored = await this.#store.getBucket(currentKey);
1214
+ const bucket = stored ? { ...stored } : { remaining: 1, resetAt: 0 };
1215
+ const windowActive = this.#concurrent && !!stored && stored.resetAt > Date.now();
1216
+ const remaining = Number(response.headers.get("X-RateLimit-Remaining"));
1217
+ const resetAfter = Number(response.headers.get("X-RateLimit-Reset-After"));
1218
+ const reset = Number(response.headers.get("X-RateLimit-Reset"));
1219
+ if (Number.isFinite(remaining))
1220
+ bucket.remaining = Math.max(0, remaining);
1221
+ if (Number.isFinite(resetAfter))
1222
+ bucket.resetAt = Date.now() + Math.max(0, resetAfter) * 1000;
1223
+ else if (Number.isFinite(reset))
1224
+ bucket.resetAt = reset * 1000;
1225
+ if (windowActive && stored) {
1226
+ bucket.remaining = Math.min(bucket.remaining, stored.remaining);
1227
+ bucket.resetAt = Math.max(bucket.resetAt, stored.resetAt);
1228
+ }
1229
+ if (response.status === 429) {
1230
+ const retryAfter = Number(response.headers.get("Retry-After"));
1231
+ if (Number.isFinite(retryAfter))
1232
+ bucket.resetAt = Math.max(bucket.resetAt, Date.now() + retryAfter * 1000);
1233
+ }
1234
+ await this.#store.updateBucket(currentKey, bucket);
1235
+ if (response.headers.get("X-RateLimit-Global") === "true") {
1236
+ const retryAfter = Number(response.headers.get("Retry-After"));
1237
+ if (Number.isFinite(retryAfter))
1238
+ await this.noteGlobalReset(Date.now() + retryAfter * 1000);
1239
+ }
1240
+ return currentKey;
1020
1241
  }
1021
- async#wait(bucket, signal, path) {
1022
- const delay = bucket.resetAt - Date.now();
1023
- if (bucket.remaining <= 0 && delay > 0)
1024
- await this.#sleep(delay, signal, path);
1242
+ }
1243
+
1244
+ // packages/rest/src/transport.ts
1245
+ class TransportError extends Error {
1246
+ timedOut;
1247
+ constructor(message, timedOut, cause) {
1248
+ super(message, cause === undefined ? undefined : { cause });
1249
+ this.name = "TransportError";
1250
+ this.timedOut = timedOut;
1025
1251
  }
1026
- async#waitGlobal(signal, path) {
1027
- const delay = this.#globalResetAt - Date.now();
1028
- if (delay > 0)
1029
- await this.#sleep(delay, signal, path);
1252
+ }
1253
+
1254
+ class HttpTransport {
1255
+ #baseURL;
1256
+ #timeout;
1257
+ #fetch;
1258
+ constructor(options = {}) {
1259
+ this.#baseURL = (options.baseURL ?? "https://discord.com/api/v10").replace(/\/$/, "");
1260
+ this.#timeout = Math.max(1, options.timeout ?? 15000);
1261
+ this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
1030
1262
  }
1031
- async#sleep(delay, signal, path) {
1032
- if (delay <= 0)
1033
- return;
1034
- await new Promise((resolve, reject) => {
1035
- const timer = setTimeout(resolve, delay);
1036
- const abort = () => {
1037
- clearTimeout(timer);
1038
- reject(this.#abortError(path, signal?.reason));
1039
- };
1040
- if (signal?.aborted)
1041
- return abort();
1042
- signal?.addEventListener("abort", abort, { once: true });
1043
- });
1263
+ get baseURL() {
1264
+ return this.#baseURL;
1044
1265
  }
1045
- async#abortable(promise, signal, path) {
1046
- if (!signal)
1047
- return promise;
1048
- if (signal.aborted)
1049
- throw this.#abortError(path, signal.reason);
1050
- return Promise.race([
1051
- promise,
1052
- new Promise((_, reject) => signal.addEventListener("abort", () => reject(this.#abortError(path, signal.reason)), { once: true }))
1053
- ]);
1266
+ get timeout() {
1267
+ return this.#timeout;
1054
1268
  }
1055
- #abortError(path, reason) {
1056
- return new RESTError(reason instanceof Error ? reason.message : "Discord REST request was aborted", 0, undefined, undefined, { path, cause: reason });
1269
+ async send(request) {
1270
+ if (request.signal?.aborted)
1271
+ throw abortError(request.path, request.signal.reason);
1272
+ const controller = new AbortController;
1273
+ const onAbort = () => controller.abort(request.signal?.reason);
1274
+ request.signal?.addEventListener("abort", onAbort, { once: true });
1275
+ const timer = setTimeout(() => controller.abort(new DOMException("REST request timeout", "TimeoutError")), this.#timeout);
1276
+ try {
1277
+ return await this.#fetch(`${this.#baseURL}${request.path}`, {
1278
+ method: request.method,
1279
+ headers: request.headers,
1280
+ body: request.body,
1281
+ signal: controller.signal
1282
+ });
1283
+ } catch (error) {
1284
+ if (error instanceof RESTError)
1285
+ throw error;
1286
+ const timedOut = error instanceof DOMException && (error.name === "TimeoutError" || error.name === "AbortError");
1287
+ throw new TransportError(timedOut ? `Discord REST request timed out after ${this.#timeout}ms` : "Discord REST request failed", timedOut, error);
1288
+ } finally {
1289
+ clearTimeout(timer);
1290
+ request.signal?.removeEventListener("abort", onAbort);
1291
+ }
1057
1292
  }
1058
- async#readPayload(response) {
1293
+ }
1294
+
1295
+ // packages/rest/src/decoder.ts
1296
+ class ResponseDecoder {
1297
+ async read(response) {
1059
1298
  if (response.status === 204)
1060
1299
  return;
1061
1300
  const contentType = response.headers.get("content-type") ?? "";
@@ -1067,7 +1306,7 @@ class REST {
1067
1306
  return;
1068
1307
  });
1069
1308
  }
1070
- #errorData(payload) {
1309
+ errorData(payload) {
1071
1310
  if (!payload || typeof payload !== "object")
1072
1311
  return {};
1073
1312
  const data = payload;
@@ -1078,46 +1317,310 @@ class REST {
1078
1317
  global: data.global === true
1079
1318
  };
1080
1319
  }
1081
- #retryAfter(response, data) {
1082
- const header = Number(response.headers.get("Retry-After"));
1320
+ retryAfter(response, data) {
1321
+ const raw = response.headers.get("Retry-After");
1322
+ const header = raw === null ? Number.NaN : Number(raw);
1083
1323
  const retryAfter = data.retry_after ?? (Number.isFinite(header) ? header : 1);
1084
1324
  return Math.max(0, retryAfter);
1085
1325
  }
1086
- #normalizeRoute(path) {
1087
- const queryIndex = path.indexOf("?");
1088
- const routePath = queryIndex === -1 ? path : path.slice(0, queryIndex);
1089
- return routePath.replace(/\/\d+(?=\/|$)/g, "/:id");
1326
+ }
1327
+
1328
+ // packages/rest/src/routes.ts
1329
+ var Routes = {
1330
+ user: () => "/users/@me",
1331
+ userById: (userId) => `/users/${snowflake(userId, "User ID")}`,
1332
+ guild: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}`,
1333
+ guildPreview: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/preview`,
1334
+ guildActiveThreads: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/threads/active`,
1335
+ guildWebhooks: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/webhooks`,
1336
+ guildInvites: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/invites`,
1337
+ guildChannels: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/channels`,
1338
+ guildMember: (guildId, userId) => `/guilds/${snowflake(guildId, "Guild ID")}/members/${snowflake(userId, "User ID")}`,
1339
+ guildRoles: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/roles`,
1340
+ guildRole: (guildId, roleId) => `/guilds/${snowflake(guildId, "Guild ID")}/roles/${snowflake(roleId, "Role ID")}`,
1341
+ guildMemberRole: (guildId, userId, roleId) => `/guilds/${snowflake(guildId, "Guild ID")}/members/${snowflake(userId, "User ID")}/roles/${snowflake(roleId, "Role ID")}`,
1342
+ guildBans: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/bans`,
1343
+ guildBan: (guildId, userId) => `/guilds/${snowflake(guildId, "Guild ID")}/bans/${snowflake(userId, "User ID")}`,
1344
+ channel: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}`,
1345
+ channelMessages: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/messages`,
1346
+ message: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/${snowflake(messageId, "Message ID")}`,
1347
+ crosspostMessage: (channelId, messageId) => `${Routes.message(channelId, messageId)}/crosspost`,
1348
+ messageReactions: (channelId, messageId, emoji) => `${Routes.message(channelId, messageId)}/reactions/${encodeURIComponent(emoji)}`,
1349
+ messageReactionsAll: (channelId, messageId) => `${Routes.message(channelId, messageId)}/reactions`,
1350
+ channelPins: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/pins`,
1351
+ channelPin: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/pins/${snowflake(messageId, "Message ID")}`,
1352
+ messageThread: (channelId, messageId) => `${Routes.message(channelId, messageId)}/threads`,
1353
+ channelBulkDelete: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/bulk-delete`,
1354
+ channelWebhooks: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/webhooks`,
1355
+ channelInvites: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/invites`,
1356
+ webhook: (webhookId, token) => `/webhooks/${snowflake(webhookId, "Webhook ID")}${token ? `/${encodeURIComponent(token)}` : ""}`,
1357
+ webhookMessage: (webhookId, token, messageId) => `/webhooks/${snowflake(webhookId, "Webhook ID")}/${encodeURIComponent(token)}/messages/${snowflake(messageId, "Message ID")}`,
1358
+ applicationCommands: (applicationId) => `/applications/${snowflake(applicationId, "Application ID")}/commands`,
1359
+ applicationCommand: (applicationId, commandId) => `/applications/${snowflake(applicationId, "Application ID")}/commands/${snowflake(commandId, "Command ID")}`,
1360
+ guildApplicationCommands: (applicationId, guildId) => `/applications/${snowflake(applicationId, "Application ID")}/guilds/${snowflake(guildId, "Guild ID")}/commands`,
1361
+ guildApplicationCommand: (applicationId, guildId, commandId) => `/applications/${snowflake(applicationId, "Application ID")}/guilds/${snowflake(guildId, "Guild ID")}/commands/${snowflake(commandId, "Command ID")}`,
1362
+ interactionCallback: (interactionId, token) => `/interactions/${snowflake(interactionId, "Interaction ID")}/${encodeURIComponent(token)}/callback`,
1363
+ interactionOriginalResponse: (applicationId, token) => `/webhooks/${snowflake(applicationId, "Application ID")}/${encodeURIComponent(token)}/messages/@original`,
1364
+ channelPublicArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads/archived/public`,
1365
+ channelPrivateArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads/archived/private`,
1366
+ threadMembers: (threadId) => `/channels/${snowflake(threadId, "Thread ID")}/thread-members`,
1367
+ guildScheduledEvents: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events`,
1368
+ guildScheduledEvent: (guildId, eventId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events/${snowflake(eventId, "Event ID")}`,
1369
+ guildScheduledEventUsers: (guildId, eventId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events/${snowflake(eventId, "Event ID")}/users`,
1370
+ channelPermission: (channelId, overwriteId) => `/channels/${snowflake(channelId, "Channel ID")}/permissions/${snowflake(overwriteId, "Overwrite ID")}`,
1371
+ guildAutoModerationRules: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/auto-moderation/rules`,
1372
+ guildAutoModerationRule: (guildId, ruleId) => `/guilds/${snowflake(guildId, "Guild ID")}/auto-moderation/rules/${snowflake(ruleId, "Rule ID")}`,
1373
+ voiceRegions: () => "/voice/regions",
1374
+ gateway: () => "/gateway",
1375
+ gatewayBot: () => "/gateway/bot",
1376
+ guildAuditLog: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/audit-logs`,
1377
+ guildMembers: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/members`,
1378
+ guildWelcomeScreen: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/welcome-screen`,
1379
+ guildOnboarding: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/onboarding`,
1380
+ guildPrune: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/prune`,
1381
+ stageInstances: () => "/stage-instances",
1382
+ stageInstanceByChannel: (channelId) => `/stage-instances/${snowflake(channelId, "Channel ID")}`,
1383
+ invite: (code) => `/invites/${encodeURIComponent(code)}`,
1384
+ guildVanity: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/vanity-url`,
1385
+ guildEmoji: (guildId, emojiId) => emojiId ? `/guilds/${snowflake(guildId, "Guild ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/guilds/${snowflake(guildId, "Guild ID")}/emojis`,
1386
+ applicationEmoji: (applicationId, emojiId) => emojiId ? `/applications/${snowflake(applicationId, "Application ID")}/emojis/${snowflake(emojiId, "Emoji ID")}` : `/applications/${snowflake(applicationId, "Application ID")}/emojis`
1387
+ };
1388
+ function snowflake(value, field) {
1389
+ if (!/^\d{1,20}$/.test(value))
1390
+ throw new TypeError(`${field} must be a valid Discord snowflake.`);
1391
+ return value;
1392
+ }
1393
+
1394
+ // packages/rest/src/index.ts
1395
+ var LIBRARY_VERSION = package_default.version;
1396
+ var LIBRARY_URL = "https://github.com/Ekretos/lunibee";
1397
+ var USER_AGENT = `DiscordBot (${LIBRARY_URL}, ${package_default.version})`;
1398
+ var REQUEST_DATA_KEYS = new Set([
1399
+ "body",
1400
+ "files",
1401
+ "query",
1402
+ "headers",
1403
+ "reason",
1404
+ "auth",
1405
+ "signal"
1406
+ ]);
1407
+ function isRequestData(value) {
1408
+ if (value === null || typeof value !== "object")
1409
+ return false;
1410
+ if (Array.isArray(value))
1411
+ return false;
1412
+ if (typeof FormData !== "undefined" && value instanceof FormData)
1413
+ return false;
1414
+ if (value instanceof Blob || value instanceof Uint8Array || value instanceof ArrayBuffer)
1415
+ return false;
1416
+ const keys = Object.keys(value);
1417
+ if (keys.length === 0)
1418
+ return false;
1419
+ return keys.every((key) => REQUEST_DATA_KEYS.has(key));
1420
+ }
1421
+ function createRetryPolicy(maxRetries = 2) {
1422
+ if (!Number.isFinite(maxRetries) || maxRetries < 0)
1423
+ throw new TypeError("maxRetries must be a non-negative finite number.");
1424
+ return {
1425
+ maxRetries: Math.floor(maxRetries),
1426
+ shouldRetry(method, status) {
1427
+ const normalized = method.toUpperCase();
1428
+ const idempotent = ["GET", "HEAD", "PUT", "DELETE"].includes(normalized);
1429
+ if (status === 0)
1430
+ return idempotent;
1431
+ return status === 429 || status >= 500 && status <= 599 && idempotent;
1432
+ },
1433
+ getDelay(attempt, retryAfter) {
1434
+ if (retryAfter !== undefined)
1435
+ return Math.max(0, retryAfter * 1000);
1436
+ return Math.min(1e4, 500 * 2 ** attempt) + Math.random() * 250;
1437
+ }
1438
+ };
1439
+ }
1440
+
1441
+ class REST {
1442
+ #retryPolicy;
1443
+ #token;
1444
+ #limiter;
1445
+ #scheduler = new RequestScheduler;
1446
+ #transport;
1447
+ #decoder = new ResponseDecoder;
1448
+ #hooks = {};
1449
+ #concurrentBuckets;
1450
+ constructor(options = {}) {
1451
+ this.#token = options.token;
1452
+ this.#retryPolicy = options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);
1453
+ this.#transport = options.transport ?? new HttpTransport({
1454
+ baseURL: options.baseURL,
1455
+ timeout: options.timeout
1456
+ });
1457
+ const store = options.store ?? new MemoryRateLimitStore;
1458
+ this.#concurrentBuckets = (options.concurrentBuckets ?? false) && typeof store.reserve === "function";
1459
+ this.#limiter = new RateLimiter(store, {
1460
+ concurrent: this.#concurrentBuckets
1461
+ });
1462
+ if (options.hooks)
1463
+ this.#hooks = options.hooks;
1090
1464
  }
1091
- #update(response, bucket, route, bucketKey) {
1092
- const serverBucket = response.headers.get("X-RateLimit-Bucket");
1093
- if (serverBucket) {
1094
- this.#routeBuckets.set(route, serverBucket);
1095
- if (serverBucket !== bucketKey) {
1096
- const shared = this.#buckets.get(serverBucket) ?? bucket;
1097
- this.#buckets.set(serverBucket, shared);
1098
- bucket = shared;
1465
+ get store() {
1466
+ return this.#limiter.store;
1467
+ }
1468
+ setHooks(hooks) {
1469
+ this.#hooks = hooks;
1470
+ return this;
1471
+ }
1472
+ setToken(token) {
1473
+ if (!token.trim())
1474
+ throw new TypeError("A Discord bot token is required.");
1475
+ this.#token = token;
1476
+ }
1477
+ async request(method, path, body, options = {}) {
1478
+ if (!method.toUpperCase())
1479
+ throw new TypeError("REST method is required.");
1480
+ if (!path.startsWith("/"))
1481
+ throw new TypeError("REST paths must start with '/'.");
1482
+ const route = createRouteKey(method, path);
1483
+ const files = options.files;
1484
+ if (files && files.length > 0 && !(body instanceof FormData))
1485
+ body = this.#fileForm(body, files);
1486
+ const query = serializeQuery(options.query);
1487
+ const requestPath = query ? `${path}${query}` : path;
1488
+ return this.#scheduler.runResolved(() => this.#limiter.resolveBucketKey(route.route, route.major), options.signal, path, (bucketKey, release) => this.#attempts(route, requestPath, body, options, bucketKey, release));
1489
+ }
1490
+ async#attempts(route, requestPath, body, options, initialBucketKey, release) {
1491
+ const { method, path } = route;
1492
+ let bucketKey = initialBucketKey;
1493
+ for (let attempt = 0;attempt <= this.#retryPolicy.maxRetries; attempt++) {
1494
+ await this.#limiter.acquire(bucketKey, options.signal, path);
1495
+ if (this.#concurrentBuckets) {
1496
+ const bucket = await this.#limiter.store.getBucket(bucketKey);
1497
+ if (bucket && bucket.resetAt > Date.now())
1498
+ release();
1099
1499
  }
1500
+ this.#emit(this.#hooks.onRequest, { method, path, attempt });
1501
+ const requestStart = Date.now();
1502
+ let response;
1503
+ try {
1504
+ response = await this.#transport.send({
1505
+ method,
1506
+ path: requestPath,
1507
+ headers: this.#headers(body, options),
1508
+ body: this.#encodeBody(body),
1509
+ signal: options.signal
1510
+ });
1511
+ } catch (error) {
1512
+ if (error instanceof RESTError)
1513
+ throw error;
1514
+ if (options.signal?.aborted)
1515
+ throw abortError(path, options.signal.reason);
1516
+ const failure = error instanceof TransportError ? error : new TransportError("Discord REST request failed", false, error);
1517
+ if (!failure.timedOut && attempt < this.#retryPolicy.maxRetries && this.#retryPolicy.shouldRetry(method, 0)) {
1518
+ await sleep(this.#retryPolicy.getDelay(attempt), options.signal, path);
1519
+ continue;
1520
+ }
1521
+ throw new RESTError(failure.message, 0, undefined, undefined, {
1522
+ method,
1523
+ path,
1524
+ cause: failure.cause ?? failure
1525
+ });
1526
+ }
1527
+ this.#emit(this.#hooks.onResponse, {
1528
+ method,
1529
+ path,
1530
+ status: response.status,
1531
+ durationMs: Date.now() - requestStart
1532
+ });
1533
+ bucketKey = await this.#limiter.applyResponse(response, bucketKey, route.route, route.major);
1534
+ const payload = await this.#decoder.read(response);
1535
+ if (response.ok)
1536
+ return response.status === 204 ? undefined : payload;
1537
+ const data = this.#decoder.errorData(payload);
1538
+ const retryAfter = response.status === 429 ? this.#decoder.retryAfter(response, data) : undefined;
1539
+ if (data.global && retryAfter !== undefined)
1540
+ await this.#limiter.noteGlobalReset(Date.now() + retryAfter * 1000);
1541
+ if (response.status === 429)
1542
+ this.#emit(this.#hooks.onRateLimit, {
1543
+ method,
1544
+ path,
1545
+ retryAfterMs: (retryAfter ?? 1) * 1000,
1546
+ global: data.global ?? false,
1547
+ bucket: await this.#limiter.store.getBucketHash(route.route)
1548
+ });
1549
+ if (attempt < this.#retryPolicy.maxRetries && this.#retryPolicy.shouldRetry(method, response.status)) {
1550
+ const delay = this.#retryPolicy.getDelay(attempt, retryAfter);
1551
+ this.#emit(this.#hooks.onRetry, {
1552
+ method,
1553
+ path,
1554
+ attempt,
1555
+ status: response.status,
1556
+ delayMs: delay
1557
+ });
1558
+ await sleep(delay, options.signal, path);
1559
+ continue;
1560
+ }
1561
+ throw new RESTError(data.message ?? response.statusText ?? `Discord REST request failed with status ${response.status}`, response.status, data.code, payload, { method, path });
1100
1562
  }
1101
- const remaining = Number(response.headers.get("X-RateLimit-Remaining"));
1102
- const resetAfter = Number(response.headers.get("X-RateLimit-Reset-After"));
1103
- const reset = Number(response.headers.get("X-RateLimit-Reset"));
1104
- if (Number.isFinite(remaining))
1105
- bucket.remaining = Math.max(0, remaining);
1106
- if (Number.isFinite(resetAfter))
1107
- bucket.resetAt = Date.now() + Math.max(0, resetAfter) * 1000;
1108
- else if (Number.isFinite(reset))
1109
- bucket.resetAt = reset * 1000;
1110
- if (response.status === 429) {
1111
- const retryAfter = Number(response.headers.get("Retry-After"));
1112
- if (Number.isFinite(retryAfter))
1113
- bucket.resetAt = Math.max(bucket.resetAt, Date.now() + retryAfter * 1000);
1114
- }
1115
- if (response.headers.get("X-RateLimit-Global") === "true") {
1116
- const retryAfter = Number(response.headers.get("Retry-After"));
1117
- if (Number.isFinite(retryAfter))
1118
- this.#globalResetAt = Math.max(this.#globalResetAt, Date.now() + retryAfter * 1000);
1563
+ throw new RESTError("Discord REST request exhausted its retry attempts", 0, undefined, undefined, { method, path });
1564
+ }
1565
+ #headers(body, options) {
1566
+ const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
1567
+ const headers = { ...options.headers };
1568
+ if (options.auth !== false && this.#token)
1569
+ headers["Authorization"] = `Bot ${this.#token}`;
1570
+ headers["User-Agent"] = USER_AGENT;
1571
+ if (options.reason !== undefined)
1572
+ headers["X-Audit-Log-Reason"] = encodeURIComponent(options.reason);
1573
+ if (!isFormData)
1574
+ headers["Content-Type"] = "application/json";
1575
+ return headers;
1576
+ }
1577
+ #encodeBody(body) {
1578
+ if (typeof FormData !== "undefined" && body instanceof FormData)
1579
+ return body;
1580
+ return body === undefined ? undefined : JSON.stringify(body);
1581
+ }
1582
+ #dispatch(method, path, bodyOrOptions, options) {
1583
+ if (options === undefined && isRequestData(bodyOrOptions))
1584
+ return this.request(method, path, bodyOrOptions.body, bodyOrOptions);
1585
+ return this.request(method, path, bodyOrOptions, options);
1586
+ }
1587
+ get(path, options) {
1588
+ return this.request("GET", path, options?.body, options);
1589
+ }
1590
+ post(path, body, options) {
1591
+ return this.#dispatch("POST", path, body, options);
1592
+ }
1593
+ patch(path, body, options) {
1594
+ return this.#dispatch("PATCH", path, body, options);
1595
+ }
1596
+ put(path, body, options) {
1597
+ return this.#dispatch("PUT", path, body, options);
1598
+ }
1599
+ delete(path, options) {
1600
+ return this.request("DELETE", path, options?.body, options);
1601
+ }
1602
+ #fileForm(payload, files) {
1603
+ const form = new FormData;
1604
+ form.append("payload_json", JSON.stringify(payload));
1605
+ for (let i = 0;i < files.length; i++) {
1606
+ const file = files[i];
1607
+ const blob = file.data instanceof Blob ? file.data : new Blob([file.data], {
1608
+ type: file.contentType ?? "application/octet-stream"
1609
+ });
1610
+ form.append(`files[${i}]`, blob, file.name);
1119
1611
  }
1120
- return bucket;
1612
+ return form;
1613
+ }
1614
+ postWithFiles(path, payload, files, options) {
1615
+ return this.request("POST", path, this.#fileForm(payload, files), options);
1616
+ }
1617
+ patchWithFiles(path, payload, files, options) {
1618
+ return this.request("PATCH", path, this.#fileForm(payload, files), options);
1619
+ }
1620
+ #emit(hook, ctx) {
1621
+ if (!hook)
1622
+ return;
1623
+ Promise.resolve().then(() => hook(ctx)).catch(() => {});
1121
1624
  }
1122
1625
  }
1123
1626
 
@@ -1240,6 +1743,18 @@ class Channel extends BaseStructure {
1240
1743
  update(options) {
1241
1744
  return this.edit(options);
1242
1745
  }
1746
+ isThread() {
1747
+ return this.type === 10 || this.type === 11 || this.type === 12;
1748
+ }
1749
+ isTextBased() {
1750
+ return [0, 1, 2, 3, 5, 10, 11, 12, 13].includes(this.type);
1751
+ }
1752
+ isVoiceBased() {
1753
+ return this.type === 2 || this.type === 13;
1754
+ }
1755
+ isDMBased() {
1756
+ return this.type === 1 || this.type === 3;
1757
+ }
1243
1758
  toString() {
1244
1759
  return `<#${this.id}>`;
1245
1760
  }
@@ -1263,6 +1778,13 @@ class Guild extends BaseStructure {
1263
1778
  vanityUrlCode;
1264
1779
  nsfwLevel;
1265
1780
  discoverySplash;
1781
+ rulesChannelId;
1782
+ systemChannelId;
1783
+ systemChannelFlags;
1784
+ maxMembers;
1785
+ mfaLevel;
1786
+ explicitContentFilter;
1787
+ defaultMessageNotifications;
1266
1788
  constructor(data) {
1267
1789
  super(data.id);
1268
1790
  if (!data.name)
@@ -1284,6 +1806,14 @@ class Guild extends BaseStructure {
1284
1806
  this.vanityUrlCode = data.vanity_url_code ?? null;
1285
1807
  this.nsfwLevel = data.nsfw_level ?? 0;
1286
1808
  this.discoverySplash = data.discovery_splash ?? null;
1809
+ const d = data;
1810
+ this.rulesChannelId = d.rules_channel_id ?? null;
1811
+ this.systemChannelId = d.system_channel_id ?? null;
1812
+ this.systemChannelFlags = d.system_channel_flags ?? 0;
1813
+ this.maxMembers = d.max_members ?? 0;
1814
+ this.mfaLevel = d.mfa_level ?? 0;
1815
+ this.explicitContentFilter = d.explicit_content_filter ?? 0;
1816
+ this.defaultMessageNotifications = d.default_message_notifications ?? 0;
1287
1817
  }
1288
1818
  iconURL(options = {}) {
1289
1819
  if (!this.icon)
@@ -1305,12 +1835,27 @@ class Guild extends BaseStructure {
1305
1835
  return null;
1306
1836
  return cdnURL(`/discovery-splashes/${this.id}`, this.discoverySplash, options);
1307
1837
  }
1838
+ widgetImageURL(style = "shield") {
1839
+ return `https://discord.com/api/guilds/${this.id}/widget.png?style=${style}`;
1840
+ }
1308
1841
  hasFeature(feature) {
1309
1842
  return this.features.includes(feature);
1310
1843
  }
1311
1844
  get isCommunity() {
1312
1845
  return this.hasFeature("COMMUNITY");
1313
1846
  }
1847
+ get isPartnered() {
1848
+ return this.hasFeature("PARTNERED");
1849
+ }
1850
+ get isVerified() {
1851
+ return this.hasFeature("VERIFIED");
1852
+ }
1853
+ get isBoosted() {
1854
+ return this.premiumSubscriptionCount > 0;
1855
+ }
1856
+ get boostLevel() {
1857
+ return this.premiumTier;
1858
+ }
1314
1859
  get hasVanityUrl() {
1315
1860
  return this.vanityUrlCode !== null;
1316
1861
  }
@@ -1321,66 +1866,158 @@ class Guild extends BaseStructure {
1321
1866
  return this.name;
1322
1867
  }
1323
1868
  }
1324
- // packages/structures/src/permissions.ts
1325
- var Permissions2 = {
1326
- CreateInstantInvite: 1n << 0n,
1327
- KickMembers: 1n << 1n,
1328
- BanMembers: 1n << 2n,
1329
- Administrator: 1n << 3n,
1330
- ManageChannels: 1n << 4n,
1331
- ManageGuild: 1n << 5n,
1332
- AddReactions: 1n << 6n,
1333
- ViewAuditLog: 1n << 7n,
1334
- PrioritySpeaker: 1n << 8n,
1335
- Stream: 1n << 9n,
1336
- ViewChannel: 1n << 10n,
1337
- SendMessages: 1n << 11n,
1338
- SendTTSMessages: 1n << 12n,
1339
- ManageMessages: 1n << 13n,
1340
- EmbedLinks: 1n << 14n,
1341
- AttachFiles: 1n << 15n,
1342
- ReadMessageHistory: 1n << 16n,
1343
- MentionEveryone: 1n << 17n,
1344
- UseExternalEmojis: 1n << 18n,
1345
- ViewGuildInsights: 1n << 19n,
1346
- Connect: 1n << 20n,
1347
- Speak: 1n << 21n,
1348
- MuteMembers: 1n << 22n,
1349
- DeafenMembers: 1n << 23n,
1350
- MoveMembers: 1n << 24n,
1351
- UseVAD: 1n << 25n,
1352
- ChangeNickname: 1n << 26n,
1353
- ManageNicknames: 1n << 27n,
1354
- ManageRoles: 1n << 28n,
1355
- ManageWebhooks: 1n << 29n,
1356
- ManageGuildExpressions: 1n << 30n,
1357
- UseApplicationCommands: 1n << 31n,
1358
- RequestToSpeak: 1n << 32n,
1359
- ManageEvents: 1n << 33n,
1360
- ManageThreads: 1n << 34n,
1361
- CreatePublicThreads: 1n << 35n,
1362
- CreatePrivateThreads: 1n << 36n,
1363
- UseExternalStickers: 1n << 37n,
1364
- SendMessagesInThreads: 1n << 38n,
1365
- UseEmbeddedActivities: 1n << 39n,
1366
- ModerateMembers: 1n << 40n,
1367
- ViewCreatorMonetizationAnalytics: 1n << 41n,
1368
- UseSoundboard: 1n << 42n,
1369
- CreateGuildExpressions: 1n << 43n,
1370
- CreateEvents: 1n << 44n,
1371
- UseExternalSounds: 1n << 45n,
1372
- SendVoiceMessages: 1n << 46n,
1373
- SendPolls: 1n << 49n,
1374
- UseExternalApps: 1n << 50n
1375
- };
1376
- // packages/structures/src/interactions.ts
1377
- var InteractionType = {
1378
- Ping: 1,
1379
- ApplicationCommand: 2,
1380
- MessageComponent: 3,
1381
- ApplicationCommandAutocomplete: 4,
1382
- ModalSubmit: 5
1383
- };
1869
+ // packages/structures/src/resources.ts
1870
+ class TextChannel extends Channel {
1871
+ constructor(data, context) {
1872
+ super(data, context);
1873
+ }
1874
+ toString() {
1875
+ return `<#${this.id}>`;
1876
+ }
1877
+ }
1878
+ class AutoModerationRule extends BaseStructure {
1879
+ guildId;
1880
+ name;
1881
+ creatorId;
1882
+ eventType;
1883
+ triggerType;
1884
+ triggerMetadata;
1885
+ actions;
1886
+ enabled;
1887
+ exemptRoles;
1888
+ exemptChannels;
1889
+ constructor(data) {
1890
+ super(data.id);
1891
+ if (!/^\d{1,20}$/.test(data.guild_id))
1892
+ throw new TypeError("AutoModerationRule guild_id must be a valid snowflake.");
1893
+ this.guildId = data.guild_id;
1894
+ this.name = data.name;
1895
+ this.creatorId = data.creator_id;
1896
+ this.eventType = data.event_type;
1897
+ this.triggerType = data.trigger_type;
1898
+ this.triggerMetadata = data.trigger_metadata;
1899
+ this.actions = data.actions;
1900
+ this.enabled = data.enabled;
1901
+ this.exemptRoles = data.exempt_roles;
1902
+ this.exemptChannels = data.exempt_channels;
1903
+ }
1904
+ toJSON() {
1905
+ return {
1906
+ id: this.id,
1907
+ guildId: this.guildId,
1908
+ name: this.name,
1909
+ creatorId: this.creatorId,
1910
+ eventType: this.eventType,
1911
+ triggerType: this.triggerType,
1912
+ triggerMetadata: this.triggerMetadata,
1913
+ actions: this.actions,
1914
+ enabled: this.enabled,
1915
+ exemptRoles: this.exemptRoles,
1916
+ exemptChannels: this.exemptChannels
1917
+ };
1918
+ }
1919
+ }
1920
+ // packages/structures/src/channels.ts
1921
+ class NewsChannel extends TextChannel {
1922
+ }
1923
+
1924
+ class DMChannel extends Channel {
1925
+ recipients;
1926
+ ownerId;
1927
+ constructor(data, context) {
1928
+ super(data, context);
1929
+ this.recipients = (data.recipients ?? []).map((u) => new User(u));
1930
+ this.ownerId = data.owner_id ?? null;
1931
+ }
1932
+ }
1933
+
1934
+ class VoiceChannel extends Channel {
1935
+ bitrate;
1936
+ userLimit;
1937
+ rtcRegion;
1938
+ videoQualityMode;
1939
+ constructor(data, context) {
1940
+ super(data, context);
1941
+ this.bitrate = data.bitrate ?? 64000;
1942
+ this.userLimit = data.user_limit ?? 0;
1943
+ this.rtcRegion = data.rtc_region ?? null;
1944
+ this.videoQualityMode = data.video_quality_mode ?? 1;
1945
+ }
1946
+ }
1947
+
1948
+ class StageChannel extends VoiceChannel {
1949
+ }
1950
+
1951
+ class CategoryChannel extends Channel {
1952
+ }
1953
+
1954
+ class ThreadChannel extends Channel {
1955
+ ownerId;
1956
+ messageCount;
1957
+ memberCount;
1958
+ metadata;
1959
+ constructor(data, context) {
1960
+ super(data, context);
1961
+ this.ownerId = data.owner_id ?? null;
1962
+ this.messageCount = data.message_count ?? 0;
1963
+ this.memberCount = data.member_count ?? 0;
1964
+ this.metadata = data.thread_metadata ?? null;
1965
+ }
1966
+ get archived() {
1967
+ return this.metadata?.archived ?? false;
1968
+ }
1969
+ get locked() {
1970
+ return this.metadata?.locked ?? false;
1971
+ }
1972
+ get autoArchiveDuration() {
1973
+ return this.metadata?.auto_archive_duration ?? null;
1974
+ }
1975
+ }
1976
+
1977
+ class ForumChannel extends Channel {
1978
+ availableTags;
1979
+ defaultReactionEmoji;
1980
+ defaultThreadRateLimitPerUser;
1981
+ defaultAutoArchiveDuration;
1982
+ constructor(data, context) {
1983
+ super(data, context);
1984
+ this.availableTags = data.available_tags ?? [];
1985
+ this.defaultReactionEmoji = data.default_reaction_emoji ?? null;
1986
+ this.defaultThreadRateLimitPerUser = data.default_thread_rate_limit_per_user ?? 0;
1987
+ this.defaultAutoArchiveDuration = data.default_auto_archive_duration ?? null;
1988
+ }
1989
+ }
1990
+
1991
+ class MediaChannel extends ForumChannel {
1992
+ }
1993
+ function createChannel(data, context) {
1994
+ switch (data.type) {
1995
+ case 0:
1996
+ return new TextChannel(data, context);
1997
+ case 1:
1998
+ case 3:
1999
+ return new DMChannel(data, context);
2000
+ case 2:
2001
+ return new VoiceChannel(data, context);
2002
+ case 4:
2003
+ return new CategoryChannel(data, context);
2004
+ case 5:
2005
+ return new NewsChannel(data, context);
2006
+ case 10:
2007
+ case 11:
2008
+ case 12:
2009
+ return new ThreadChannel(data, context);
2010
+ case 13:
2011
+ return new StageChannel(data, context);
2012
+ case 15:
2013
+ return new ForumChannel(data, context);
2014
+ case 16:
2015
+ return new MediaChannel(data, context);
2016
+ default:
2017
+ return new Channel(data, context);
2018
+ }
2019
+ }
2020
+ // packages/structures/src/options.ts
1384
2021
  var OptionType = {
1385
2022
  SubCommand: 1,
1386
2023
  SubCommandGroup: 2,
@@ -1436,6 +2073,12 @@ class CommandOptions {
1436
2073
  }
1437
2074
  return this.#subcommandGroup;
1438
2075
  }
2076
+ getFocused(full = false) {
2077
+ const focused = this.#options.find((o) => o.focused);
2078
+ if (!focused)
2079
+ throw new TypeError("No focused option was provided.");
2080
+ return full ? focused : focused.value ?? "";
2081
+ }
1439
2082
  getString(name, required = false) {
1440
2083
  const opt = this.#get(name);
1441
2084
  if (!opt || opt.type !== OptionType.String) {
@@ -1541,6 +2184,14 @@ class CommandOptions {
1541
2184
  return this.#options;
1542
2185
  }
1543
2186
  }
2187
+ // packages/structures/src/interactions.ts
2188
+ var InteractionType = {
2189
+ Ping: 1,
2190
+ ApplicationCommand: 2,
2191
+ MessageComponent: 3,
2192
+ ApplicationCommandAutocomplete: 4,
2193
+ ModalSubmit: 5
2194
+ };
1544
2195
 
1545
2196
  class InteractionResponse {
1546
2197
  type;
@@ -1604,6 +2255,30 @@ class Interaction {
1604
2255
  isAutocomplete() {
1605
2256
  return this.type === InteractionType.ApplicationCommandAutocomplete;
1606
2257
  }
2258
+ #componentType() {
2259
+ return this.isMessageComponent() ? this.data.data?.component_type ?? 0 : 0;
2260
+ }
2261
+ isButton() {
2262
+ return this.#componentType() === 2;
2263
+ }
2264
+ isStringSelectMenu() {
2265
+ return this.#componentType() === 3;
2266
+ }
2267
+ isUserSelectMenu() {
2268
+ return this.#componentType() === 5;
2269
+ }
2270
+ isRoleSelectMenu() {
2271
+ return this.#componentType() === 6;
2272
+ }
2273
+ isMentionableSelectMenu() {
2274
+ return this.#componentType() === 7;
2275
+ }
2276
+ isChannelSelectMenu() {
2277
+ return this.#componentType() === 8;
2278
+ }
2279
+ isAnySelectMenu() {
2280
+ return [3, 5, 6, 7, 8].includes(this.#componentType());
2281
+ }
1607
2282
  assertUnacknowledged() {
1608
2283
  if (this.replied || this.deferred)
1609
2284
  throw new Error("Interaction has already been acknowledged.");
@@ -1663,8 +2338,7 @@ class CommandInteraction extends Interaction {
1663
2338
  super(client, data);
1664
2339
  const rawOptions = data.data?.options ?? [];
1665
2340
  const resolved = data.data?.resolved ?? {};
1666
- const sub = rawOptions.find((o) => o.type === 1 || o.type === 2);
1667
- this.options = new CommandOptions(sub?.options ?? rawOptions, resolved);
2341
+ this.options = new CommandOptions(rawOptions, resolved);
1668
2342
  }
1669
2343
  }
1670
2344
 
@@ -1715,6 +2389,11 @@ class ModalSubmitInteraction extends Interaction {
1715
2389
  }
1716
2390
 
1717
2391
  class AutocompleteInteraction extends Interaction {
2392
+ options;
2393
+ constructor(client, data) {
2394
+ super(client, data);
2395
+ this.options = new CommandOptions(data.data?.options ?? []);
2396
+ }
1718
2397
  get commandName() {
1719
2398
  return this.data?.data?.name ?? "";
1720
2399
  }
@@ -1758,6 +2437,8 @@ function createInteraction(client, data) {
1758
2437
  }
1759
2438
 
1760
2439
  // packages/structures/src/index.ts
2440
+ var NON_SYSTEM_MESSAGE_TYPES = new Set([0, 19, 20, 23]);
2441
+
1761
2442
  class Message extends BaseStructure {
1762
2443
  content;
1763
2444
  author;
@@ -1803,6 +2484,12 @@ class Message extends BaseStructure {
1803
2484
  get embedsSuppressed() {
1804
2485
  return (this.flags & 4) !== 0;
1805
2486
  }
2487
+ get createdTimestamp() {
2488
+ return this.timestamp.getTime();
2489
+ }
2490
+ get createdAt() {
2491
+ return this.timestamp;
2492
+ }
1806
2493
  edit(options) {
1807
2494
  if (!this.#context)
1808
2495
  throw new Error("This message is not attached to a client.");
@@ -1857,6 +2544,51 @@ class Message extends BaseStructure {
1857
2544
  throw new Error("This message is not attached to a client.");
1858
2545
  return this.#context.unpinMessage(this.channelId, this.id);
1859
2546
  }
2547
+ get isEphemeral() {
2548
+ return (this.flags & 64) !== 0;
2549
+ }
2550
+ get isPinned() {
2551
+ return this.pinned;
2552
+ }
2553
+ get isSystemMessage() {
2554
+ return !NON_SYSTEM_MESSAGE_TYPES.has(this.type);
2555
+ }
2556
+ get isCrosspost() {
2557
+ return (this.flags & 2) !== 0;
2558
+ }
2559
+ get hasAttachments() {
2560
+ return this.attachments.length > 0;
2561
+ }
2562
+ get hasEmbeds() {
2563
+ return this.embeds.length > 0;
2564
+ }
2565
+ get hasComponents() {
2566
+ return this.components.length > 0;
2567
+ }
2568
+ suppressEmbeds() {
2569
+ return this.edit({ flags: this.flags | 4 });
2570
+ }
2571
+ toJSON() {
2572
+ return {
2573
+ id: this.id,
2574
+ content: this.content,
2575
+ channelId: this.channelId,
2576
+ guildId: this.guildId,
2577
+ flags: this.flags,
2578
+ pinned: this.pinned,
2579
+ mentionEveryone: this.mentionEveryone,
2580
+ type: this.type,
2581
+ timestamp: this.timestamp.toISOString(),
2582
+ author: { id: this.author.id, username: this.author.username },
2583
+ attachments: [...this.attachments],
2584
+ embeds: [...this.embeds],
2585
+ components: [...this.components],
2586
+ mentions: this.mentions.map((u) => u.id),
2587
+ mentionRoles: [...this.mentionRoles],
2588
+ reference: this.reference,
2589
+ referencedMessage: this.referencedMessage ? this.referencedMessage.id : this.referencedMessage
2590
+ };
2591
+ }
1860
2592
  }
1861
2593
 
1862
2594
  // packages/managers/src/message.ts
@@ -1919,7 +2651,7 @@ class ThreadManager {
1919
2651
  auto_archive_duration: options.autoArchiveDuration,
1920
2652
  rate_limit_per_user: options.rateLimitPerUser
1921
2653
  });
1922
- return new Channel(data, this.#context);
2654
+ return createChannel(data, this.#context);
1923
2655
  }
1924
2656
  }
1925
2657
 
@@ -1947,13 +2679,173 @@ class UserManager extends ResourceManager {
1947
2679
  });
1948
2680
  }
1949
2681
  }
2682
+ // packages/managers/src/guild-resources.ts
2683
+ function withQuery(path, params) {
2684
+ const query = new URLSearchParams;
2685
+ for (const [key, value] of Object.entries(params))
2686
+ if (value !== undefined)
2687
+ query.set(key, String(value));
2688
+ const suffix = query.toString();
2689
+ return suffix ? `${path}?${suffix}` : path;
2690
+ }
2691
+
2692
+ class GuildBanManager extends Manager {
2693
+ #rest;
2694
+ guildId;
2695
+ constructor(rest, guildId) {
2696
+ super();
2697
+ this.#rest = rest;
2698
+ this.guildId = guildId;
2699
+ }
2700
+ async fetch(userId) {
2701
+ const ban = await this.#rest.get(Routes.guildBan(this.guildId, userId));
2702
+ this.set(ban.user.id, ban);
2703
+ return ban;
2704
+ }
2705
+ async fetchAll(options = {}) {
2706
+ const limit = options.limit === undefined ? undefined : Math.min(1000, Math.max(1, options.limit));
2707
+ const bans = await this.#rest.get(withQuery(Routes.guildBans(this.guildId), {
2708
+ limit,
2709
+ before: options.before,
2710
+ after: options.after
2711
+ }));
2712
+ for (const ban of bans)
2713
+ this.set(ban.user.id, ban);
2714
+ return bans;
2715
+ }
2716
+ async create(userId, options = {}) {
2717
+ await this.#rest.put(Routes.guildBan(this.guildId, userId), { delete_message_seconds: options.deleteMessageSeconds }, { reason: options.reason });
2718
+ }
2719
+ async remove(userId, reason) {
2720
+ await this.#rest.delete(Routes.guildBan(this.guildId, userId), {
2721
+ reason
2722
+ });
2723
+ this.delete(userId);
2724
+ }
2725
+ }
2726
+
2727
+ class GuildScheduledEventManager extends Manager {
2728
+ #rest;
2729
+ guildId;
2730
+ constructor(rest, guildId) {
2731
+ super();
2732
+ this.#rest = rest;
2733
+ this.guildId = guildId;
2734
+ }
2735
+ #store(event) {
2736
+ this.set(event.id, event);
2737
+ return event;
2738
+ }
2739
+ async fetch(eventId, options = {}) {
2740
+ return this.#store(await this.#rest.get(withQuery(Routes.guildScheduledEvent(this.guildId, eventId), {
2741
+ with_user_count: options.withUserCount
2742
+ })));
2743
+ }
2744
+ async fetchAll(options = {}) {
2745
+ const events = await this.#rest.get(withQuery(Routes.guildScheduledEvents(this.guildId), {
2746
+ with_user_count: options.withUserCount
2747
+ }));
2748
+ return events.map((event) => this.#store(event));
2749
+ }
2750
+ async create(options, reason) {
2751
+ return this.#store(await this.#rest.post(Routes.guildScheduledEvents(this.guildId), options, { reason }));
2752
+ }
2753
+ async edit(eventId, options, reason) {
2754
+ return this.#store(await this.#rest.patch(Routes.guildScheduledEvent(this.guildId, eventId), options, { reason }));
2755
+ }
2756
+ async remove(eventId) {
2757
+ await this.#rest.delete(Routes.guildScheduledEvent(this.guildId, eventId));
2758
+ this.delete(eventId);
2759
+ }
2760
+ async fetchSubscribers(eventId, options = {}) {
2761
+ const limit = options.limit === undefined ? undefined : Math.min(100, Math.max(1, options.limit));
2762
+ return this.#rest.get(withQuery(Routes.guildScheduledEventUsers(this.guildId, eventId), {
2763
+ limit,
2764
+ with_member: options.withMember,
2765
+ before: options.before,
2766
+ after: options.after
2767
+ }));
2768
+ }
2769
+ }
2770
+
2771
+ class StageInstanceManager extends Manager {
2772
+ #rest;
2773
+ constructor(rest) {
2774
+ super();
2775
+ this.#rest = rest;
2776
+ }
2777
+ #store(stage) {
2778
+ this.set(stage.channel_id, stage);
2779
+ return stage;
2780
+ }
2781
+ async create(channelId, options, reason) {
2782
+ return this.#store(await this.#rest.post(Routes.stageInstances(), {
2783
+ channel_id: channelId,
2784
+ topic: options.topic,
2785
+ privacy_level: options.privacyLevel,
2786
+ send_start_notification: options.sendStartNotification,
2787
+ guild_scheduled_event_id: options.guildScheduledEventId
2788
+ }, { reason }));
2789
+ }
2790
+ async fetch(channelId) {
2791
+ return this.#store(await this.#rest.get(Routes.stageInstanceByChannel(channelId)));
2792
+ }
2793
+ async edit(channelId, options, reason) {
2794
+ return this.#store(await this.#rest.patch(Routes.stageInstanceByChannel(channelId), { topic: options.topic, privacy_level: options.privacyLevel }, { reason }));
2795
+ }
2796
+ async remove(channelId, reason) {
2797
+ await this.#rest.delete(Routes.stageInstanceByChannel(channelId), {
2798
+ reason
2799
+ });
2800
+ this.delete(channelId);
2801
+ }
2802
+ }
2803
+
2804
+ class PermissionOverwriteManager {
2805
+ #rest;
2806
+ channelId;
2807
+ constructor(rest, channelId) {
2808
+ this.#rest = rest;
2809
+ this.channelId = channelId;
2810
+ }
2811
+ async edit(overwriteId, options, reason) {
2812
+ await this.#rest.put(Routes.channelPermission(this.channelId, overwriteId), {
2813
+ type: options.type,
2814
+ allow: BigInt(options.allow ?? 0).toString(),
2815
+ deny: BigInt(options.deny ?? 0).toString()
2816
+ }, { reason });
2817
+ }
2818
+ async remove(overwriteId, reason) {
2819
+ await this.#rest.delete(Routes.channelPermission(this.channelId, overwriteId), { reason });
2820
+ }
2821
+ }
2822
+
1950
2823
  // packages/managers/src/guild.ts
2824
+ function withQuery2(path, params) {
2825
+ const suffix = params.toString();
2826
+ return suffix ? `${path}?${suffix}` : path;
2827
+ }
2828
+
1951
2829
  class GuildManager extends ResourceManager {
1952
2830
  #rest;
2831
+ #bans = new Map;
2832
+ #scheduledEvents = new Map;
1953
2833
  constructor(rest) {
1954
2834
  super(async (id) => new Guild(await rest.get(Routes.guild(id))), (guild) => guild.id);
1955
2835
  this.#rest = rest;
1956
2836
  }
2837
+ bans(guildId) {
2838
+ let manager = this.#bans.get(guildId);
2839
+ if (!manager)
2840
+ this.#bans.set(guildId, manager = new GuildBanManager(this.#rest, guildId));
2841
+ return manager;
2842
+ }
2843
+ scheduledEvents(guildId) {
2844
+ let manager = this.#scheduledEvents.get(guildId);
2845
+ if (!manager)
2846
+ this.#scheduledEvents.set(guildId, manager = new GuildScheduledEventManager(this.#rest, guildId));
2847
+ return manager;
2848
+ }
1957
2849
  async create(options) {
1958
2850
  const data = await this.#rest.post("/guilds", options);
1959
2851
  return this.upsert(new Guild(data));
@@ -1977,6 +2869,47 @@ class GuildManager extends ResourceManager {
1977
2869
  async fetchInvites(id) {
1978
2870
  return this.#rest.get(Routes.guildInvites(id));
1979
2871
  }
2872
+ async fetchAuditLog(guildId, options = {}) {
2873
+ const params = new URLSearchParams;
2874
+ if (options.userId)
2875
+ params.set("user_id", options.userId);
2876
+ if (options.actionType !== undefined)
2877
+ params.set("action_type", String(options.actionType));
2878
+ if (options.before)
2879
+ params.set("before", options.before);
2880
+ if (options.after)
2881
+ params.set("after", options.after);
2882
+ if (options.limit !== undefined)
2883
+ params.set("limit", String(Math.min(100, Math.max(1, options.limit))));
2884
+ return this.#rest.get(withQuery2(Routes.guildAuditLog(guildId), params));
2885
+ }
2886
+ async fetchMembers(guildId, options = {}) {
2887
+ const params = new URLSearchParams;
2888
+ if (options.limit !== undefined)
2889
+ params.set("limit", String(Math.min(1000, Math.max(1, options.limit))));
2890
+ if (options.after)
2891
+ params.set("after", options.after);
2892
+ return this.#rest.get(withQuery2(Routes.guildMembers(guildId), params));
2893
+ }
2894
+ async fetchAutoModerationRules(guildId) {
2895
+ const rules = await this.#rest.get(Routes.guildAutoModerationRules(guildId));
2896
+ return rules.map((r) => new AutoModerationRule(r));
2897
+ }
2898
+ async fetchAutoModerationRule(guildId, ruleId) {
2899
+ const rule = await this.#rest.get(Routes.guildAutoModerationRule(guildId, ruleId));
2900
+ return new AutoModerationRule(rule);
2901
+ }
2902
+ async createAutoModerationRule(guildId, options) {
2903
+ const rule = await this.#rest.post(Routes.guildAutoModerationRules(guildId), options);
2904
+ return new AutoModerationRule(rule);
2905
+ }
2906
+ async editAutoModerationRule(guildId, ruleId, options) {
2907
+ const rule = await this.#rest.patch(Routes.guildAutoModerationRule(guildId, ruleId), options);
2908
+ return new AutoModerationRule(rule);
2909
+ }
2910
+ async deleteAutoModerationRule(guildId, ruleId) {
2911
+ await this.#rest.delete(Routes.guildAutoModerationRule(guildId, ruleId));
2912
+ }
1980
2913
  }
1981
2914
  // packages/managers/src/application.ts
1982
2915
  class ApplicationCommandManager {
@@ -2049,6 +2982,9 @@ class ChannelManager extends Manager {
2049
2982
  }
2050
2983
  return manager;
2051
2984
  }
2985
+ permissionOverwrites(channelId) {
2986
+ return new PermissionOverwriteManager(this.#rest, channelId);
2987
+ }
2052
2988
  threads(channelId) {
2053
2989
  return new ThreadManager(this.#rest, this.#context, channelId);
2054
2990
  }
@@ -2060,7 +2996,7 @@ class ChannelManager extends Manager {
2060
2996
  }
2061
2997
  upsert(data) {
2062
2998
  const existing = this.get(data.id);
2063
- const channel = new Channel(data, this.#context);
2999
+ const channel = createChannel(data, this.#context);
2064
3000
  if (existing) {
2065
3001
  Object.assign(existing, channel);
2066
3002
  return existing;
@@ -2079,8 +3015,11 @@ class ChannelManager extends Manager {
2079
3015
  }
2080
3016
  async deleteChannel(channelId) {
2081
3017
  await this.#rest.delete(Routes.channel(channelId));
3018
+ this.delete(channelId);
3019
+ }
3020
+ delete(channelId) {
2082
3021
  this.#messageManagers.delete(channelId);
2083
- super.delete(channelId);
3022
+ return super.delete(channelId);
2084
3023
  }
2085
3024
  send(channelId, options) {
2086
3025
  return this.messages(channelId).send(options);
@@ -2120,66 +3059,638 @@ class ChannelManager extends Manager {
2120
3059
  deleteCachedMessage(channelId, messageId) {
2121
3060
  return this.messages(channelId).delete(messageId);
2122
3061
  }
2123
- async crosspostMessage(channelId, messageId) {
2124
- return this.messages(channelId).upsert(await this.#rest.post(Routes.crosspostMessage(channelId, messageId)));
3062
+ async crosspostMessage(channelId, messageId) {
3063
+ return this.messages(channelId).upsert(await this.#rest.post(Routes.crosspostMessage(channelId, messageId)));
3064
+ }
3065
+ async bulkDeleteMessages(channelId, messageIds) {
3066
+ const ids = [...messageIds];
3067
+ if (ids.length < 2 || ids.length > 100)
3068
+ throw new RangeError("bulkDelete requires between 2 and 100 message IDs.");
3069
+ await this.#rest.post(Routes.channelBulkDelete(channelId), {
3070
+ messages: ids
3071
+ });
3072
+ for (const id of ids)
3073
+ this.messages(channelId).delete(id);
3074
+ }
3075
+ async addReaction(channelId, messageId, emoji) {
3076
+ await this.#rest.put(`${Routes.messageReactions(channelId, messageId, emoji)}/@me`);
3077
+ }
3078
+ async fetchReactions(channelId, messageId, emoji, options = {}) {
3079
+ const params = new URLSearchParams;
3080
+ if (options.limit !== undefined)
3081
+ params.set("limit", String(options.limit));
3082
+ if (options.after)
3083
+ params.set("after", options.after);
3084
+ const suffix = params.toString();
3085
+ const data = await this.#rest.get(`${Routes.messageReactions(channelId, messageId, emoji)}${suffix ? `?${suffix}` : ""}`);
3086
+ return data.map((user) => new User(user));
3087
+ }
3088
+ async removeOwnReaction(channelId, messageId, emoji) {
3089
+ await this.#rest.delete(`${Routes.messageReactions(channelId, messageId, emoji)}/@me`);
3090
+ }
3091
+ async removeReaction(channelId, messageId, emoji, userId) {
3092
+ await this.#rest.delete(`${Routes.messageReactions(channelId, messageId, emoji)}/${userId}`);
3093
+ }
3094
+ async removeAllReactions(channelId, messageId) {
3095
+ await this.#rest.delete(Routes.messageReactionsAll(channelId, messageId));
3096
+ }
3097
+ async fetchPinnedMessages(channelId) {
3098
+ const data = await this.#rest.get(Routes.channelPins(channelId));
3099
+ return data.map((item) => this.messages(channelId).upsert(item));
3100
+ }
3101
+ async pinMessage(channelId, messageId) {
3102
+ await this.#rest.put(Routes.channelPin(channelId, messageId));
3103
+ }
3104
+ async unpinMessage(channelId, messageId) {
3105
+ await this.#rest.delete(Routes.channelPin(channelId, messageId));
3106
+ }
3107
+ createThreadFromMessage(channelId, messageId, options) {
3108
+ return this.threads(channelId).createFromMessage(messageId, options);
3109
+ }
3110
+ async fetchPage(channelId, options = {}) {
3111
+ const limit = Math.min(100, Math.max(1, options.limit ?? 50));
3112
+ const messages = await this.fetchMessages(channelId, {
3113
+ ...options,
3114
+ limit
3115
+ });
3116
+ return {
3117
+ messages,
3118
+ before: messages[0]?.id,
3119
+ after: messages[messages.length - 1]?.id,
3120
+ hasMore: messages.length === limit
3121
+ };
3122
+ }
3123
+ async bulkDelete(channelId, messageIds) {
3124
+ return this.bulkDeleteMessages(channelId, messageIds);
3125
+ }
3126
+ clear() {
3127
+ this.#messageManagers.clear();
3128
+ super.clear();
3129
+ }
3130
+ }
3131
+ function isMessageQuery(value) {
3132
+ return typeof value === "object" && value !== null && !(Symbol.iterator in value);
3133
+ }
3134
+
3135
+ // packages/ws/src/session.ts
3136
+ class GatewaySession {
3137
+ #state = { status: "none" };
3138
+ #sequence = null;
3139
+ #generation = 0;
3140
+ #lastInvalidation;
3141
+ get lastInvalidation() {
3142
+ return this.#lastInvalidation;
3143
+ }
3144
+ get generation() {
3145
+ return this.#generation;
3146
+ }
3147
+ get sessionId() {
3148
+ return this.#state.status === "established" ? this.#state.sessionId : undefined;
3149
+ }
3150
+ get resumeURL() {
3151
+ return this.#state.status === "established" ? this.#state.resumeURL : undefined;
3152
+ }
3153
+ get sequence() {
3154
+ return this.#sequence;
3155
+ }
3156
+ get canResume() {
3157
+ return this.#state.status === "established" && this.#sequence !== null;
3158
+ }
3159
+ beginConnection() {
3160
+ return ++this.#generation;
3161
+ }
3162
+ owns(token) {
3163
+ return token === this.#generation;
3164
+ }
3165
+ recordSequence(sequence, token) {
3166
+ if (!this.owns(token))
3167
+ return false;
3168
+ if (!Number.isInteger(sequence))
3169
+ return false;
3170
+ this.#sequence = sequence;
3171
+ return true;
3172
+ }
3173
+ activate(payload, token) {
3174
+ if (!this.owns(token))
3175
+ return false;
3176
+ const ready = payload;
3177
+ if (typeof ready?.session_id !== "string" || typeof ready?.resume_gateway_url !== "string")
3178
+ return false;
3179
+ this.#state = {
3180
+ status: "established",
3181
+ sessionId: ready.session_id,
3182
+ resumeURL: ready.resume_gateway_url
3183
+ };
3184
+ return true;
3185
+ }
3186
+ invalidate(token, reason = "explicit") {
3187
+ if (!this.owns(token))
3188
+ return false;
3189
+ this.#lastInvalidation = reason;
3190
+ this.#state = { status: "none" };
3191
+ this.#sequence = null;
3192
+ return true;
3193
+ }
3194
+ resumeInfo() {
3195
+ if (this.#state.status !== "established")
3196
+ return;
3197
+ if (this.#sequence === null)
3198
+ return;
3199
+ const { sessionId, resumeURL } = this.#state;
3200
+ return { sessionId, sequence: this.#sequence, resumeURL };
3201
+ }
3202
+ handshake() {
3203
+ const resume = this.resumeInfo();
3204
+ return resume ? { type: "resume", ...resume } : { type: "identify" };
3205
+ }
3206
+ connectURL(fallback) {
3207
+ return this.resumeInfo()?.resumeURL ?? fallback;
3208
+ }
3209
+ }
3210
+
3211
+ // packages/ws/src/heartbeat.ts
3212
+ var MAX_MONITOR_INTERVAL = 1000;
3213
+
3214
+ class GatewayHeartbeat {
3215
+ #options;
3216
+ #now;
3217
+ #interval = 0;
3218
+ #acknowledged = true;
3219
+ #sentAt = 0;
3220
+ #latency = -1;
3221
+ #lastMessageAt = 0;
3222
+ #zombieReported = false;
3223
+ #timer;
3224
+ #initialTimer;
3225
+ #ackTimer;
3226
+ #monitorTimer;
3227
+ constructor(options) {
3228
+ if (options.zombieTimeout <= options.ackTimeout)
3229
+ throw new RangeError("Gateway zombieTimeout must be greater than heartbeatAckTimeout");
3230
+ this.#options = options;
3231
+ this.#now = options.now ?? (() => Date.now());
3232
+ }
3233
+ get latency() {
3234
+ return this.#latency;
3235
+ }
3236
+ get interval() {
3237
+ return this.#interval;
3238
+ }
3239
+ get acknowledged() {
3240
+ return this.#acknowledged;
3241
+ }
3242
+ get silentFor() {
3243
+ return this.#lastMessageAt === 0 ? 0 : this.#now() - this.#lastMessageAt;
3244
+ }
3245
+ get stalenessDeadline() {
3246
+ return Math.max(this.#options.zombieTimeout, this.#interval + this.#options.ackTimeout);
3247
+ }
3248
+ isHealthy() {
3249
+ if (!this.#acknowledged)
3250
+ return false;
3251
+ if (this.#lastMessageAt === 0)
3252
+ return true;
3253
+ return this.silentFor < this.stalenessDeadline;
3254
+ }
3255
+ watch() {
3256
+ this.#lastMessageAt = this.#now();
3257
+ this.#zombieReported = false;
3258
+ this.#startMonitor();
3259
+ }
3260
+ receivedMessage() {
3261
+ this.#lastMessageAt = this.#now();
3262
+ this.#zombieReported = false;
3263
+ }
3264
+ start(interval) {
3265
+ this.#clearBeatTimers();
3266
+ this.#interval = interval;
3267
+ this.#acknowledged = true;
3268
+ this.#initialTimer = setTimeout(() => this.sendHeartbeat(), Math.random() * interval);
3269
+ this.#timer = setInterval(() => this.sendHeartbeat(), interval);
3270
+ }
3271
+ stop() {
3272
+ this.#clearBeatTimers();
3273
+ if (this.#monitorTimer)
3274
+ clearInterval(this.#monitorTimer);
3275
+ this.#monitorTimer = undefined;
3276
+ }
3277
+ sendHeartbeat() {
3278
+ this.#acknowledged = false;
3279
+ this.#sentAt = this.#now();
3280
+ if (!this.#options.send(this.#options.sequence())) {
3281
+ this.#options.onError(new Error("Unable to send Gateway heartbeat because the WebSocket is not open."));
3282
+ return;
3283
+ }
3284
+ this.#clearAckTimer();
3285
+ this.#ackTimer = setTimeout(() => {
3286
+ if (this.#acknowledged)
3287
+ return;
3288
+ this.#options.onTimeout({
3289
+ type: "ack",
3290
+ elapsedMs: this.#now() - this.#sentAt
3291
+ });
3292
+ }, this.#options.ackTimeout);
3293
+ }
3294
+ acknowledge() {
3295
+ this.#acknowledged = true;
3296
+ this.#latency = this.#now() - this.#sentAt;
3297
+ this.#clearAckTimer();
3298
+ }
3299
+ #startMonitor() {
3300
+ if (this.#monitorTimer)
3301
+ clearInterval(this.#monitorTimer);
3302
+ const interval = Math.max(1, Math.min(MAX_MONITOR_INTERVAL, this.#options.ackTimeout, this.#options.zombieTimeout / 2));
3303
+ this.#monitorTimer = setInterval(() => this.#checkStaleness(), interval);
3304
+ }
3305
+ #checkStaleness() {
3306
+ if (!this.#options.isConnected() || this.#lastMessageAt === 0)
3307
+ return;
3308
+ const silentFor = this.silentFor;
3309
+ const deadline = this.stalenessDeadline;
3310
+ if (silentFor < deadline || this.#zombieReported)
3311
+ return;
3312
+ this.#zombieReported = true;
3313
+ this.#options.onTimeout({ type: "zombie", silentFor, deadline });
3314
+ }
3315
+ #clearAckTimer() {
3316
+ if (this.#ackTimer)
3317
+ clearTimeout(this.#ackTimer);
3318
+ this.#ackTimer = undefined;
3319
+ }
3320
+ #clearBeatTimers() {
3321
+ if (this.#timer)
3322
+ clearInterval(this.#timer);
3323
+ if (this.#initialTimer)
3324
+ clearTimeout(this.#initialTimer);
3325
+ this.#clearAckTimer();
3326
+ this.#timer = undefined;
3327
+ this.#initialTimer = undefined;
2125
3328
  }
2126
- async bulkDeleteMessages(channelId, messageIds) {
2127
- const ids = [...messageIds];
2128
- await this.#rest.post(Routes.channelBulkDelete(channelId), {
2129
- messages: ids
3329
+ }
3330
+
3331
+ // packages/ws/src/close-codes.ts
3332
+ var GatewayCloseCodes = {
3333
+ UnknownError: 4000,
3334
+ UnknownOpcode: 4001,
3335
+ DecodeError: 4002,
3336
+ NotAuthenticated: 4003,
3337
+ AuthenticationFailed: 4004,
3338
+ AlreadyAuthenticated: 4005,
3339
+ InvalidSeq: 4007,
3340
+ RateLimited: 4008,
3341
+ SessionTimedOut: 4009,
3342
+ InvalidShard: 4010,
3343
+ ShardingRequired: 4011,
3344
+ InvalidAPIVersion: 4012,
3345
+ InvalidIntents: 4013,
3346
+ DisallowedIntents: 4014
3347
+ };
3348
+
3349
+ // packages/ws/src/reconnect.ts
3350
+ var FATAL_CLOSE_CODES = [
3351
+ GatewayCloseCodes.AuthenticationFailed,
3352
+ GatewayCloseCodes.InvalidShard,
3353
+ GatewayCloseCodes.ShardingRequired,
3354
+ GatewayCloseCodes.InvalidAPIVersion,
3355
+ GatewayCloseCodes.InvalidIntents,
3356
+ GatewayCloseCodes.DisallowedIntents
3357
+ ];
3358
+ var IDENTIFY_CLOSE_CODES = [
3359
+ GatewayCloseCodes.InvalidSeq,
3360
+ GatewayCloseCodes.SessionTimedOut
3361
+ ];
3362
+ function classifyCloseCode(code, canResume) {
3363
+ if (FATAL_CLOSE_CODES.includes(code))
3364
+ return "stop";
3365
+ if (IDENTIFY_CLOSE_CODES.includes(code))
3366
+ return "identify";
3367
+ return canResume ? "resume" : "identify";
3368
+ }
3369
+
3370
+ class GatewayReconnect {
3371
+ #options;
3372
+ #random;
3373
+ #attempt = 0;
3374
+ #timer;
3375
+ #promise;
3376
+ #resolve;
3377
+ #reject;
3378
+ constructor(options) {
3379
+ this.#options = options;
3380
+ this.#random = options.random ?? Math.random;
3381
+ }
3382
+ get enabled() {
3383
+ return this.#options.enabled;
3384
+ }
3385
+ get attempts() {
3386
+ return this.#attempt;
3387
+ }
3388
+ get pending() {
3389
+ return this.#timer !== undefined;
3390
+ }
3391
+ get exhausted() {
3392
+ return this.#attempt >= this.#options.maxAttempts;
3393
+ }
3394
+ get connecting() {
3395
+ return this.#promise !== undefined;
3396
+ }
3397
+ get attemptPromise() {
3398
+ return this.#promise;
3399
+ }
3400
+ classifyClose(code, canResume) {
3401
+ return classifyCloseCode(code, canResume);
3402
+ }
3403
+ canReconnect() {
3404
+ return this.#options.enabled && !this.exhausted;
3405
+ }
3406
+ schedule(run) {
3407
+ if (!this.#options.enabled)
3408
+ return { scheduled: false, reason: "disabled" };
3409
+ if (this.#timer)
3410
+ return { scheduled: false, reason: "pending" };
3411
+ if (this.exhausted)
3412
+ return { scheduled: false, reason: "exhausted" };
3413
+ const attempt = this.#attempt;
3414
+ const delay = Math.min(this.#options.maxDelay, this.#options.baseDelay * 2 ** this.#attempt++);
3415
+ const jitter = this.#random() * Math.max(1, delay * 0.25);
3416
+ const delayMs = delay + jitter;
3417
+ this.#timer = setTimeout(() => {
3418
+ this.#timer = undefined;
3419
+ run();
3420
+ }, delayMs);
3421
+ return { scheduled: true, delayMs, attempt };
3422
+ }
3423
+ cancel() {
3424
+ if (this.#timer)
3425
+ clearTimeout(this.#timer);
3426
+ this.#timer = undefined;
3427
+ }
3428
+ reset() {
3429
+ this.#attempt = 0;
3430
+ }
3431
+ attempt(start) {
3432
+ if (this.#promise)
3433
+ return this.#promise;
3434
+ this.#promise = new Promise((resolve, reject) => {
3435
+ this.#resolve = resolve;
3436
+ this.#reject = reject;
3437
+ start();
2130
3438
  });
2131
- for (const id of ids)
2132
- this.messages(channelId).delete(id);
2133
- }
2134
- async addReaction(channelId, messageId, emoji) {
2135
- await this.#rest.put(Routes.messageReactions(channelId, messageId, emoji));
3439
+ return this.#promise;
3440
+ }
3441
+ settle(error) {
3442
+ const resolve = this.#resolve;
3443
+ const reject = this.#reject;
3444
+ this.#resolve = undefined;
3445
+ this.#reject = undefined;
3446
+ this.#promise = undefined;
3447
+ if (error)
3448
+ reject?.(error);
3449
+ else
3450
+ resolve?.();
2136
3451
  }
2137
- async fetchReactions(channelId, messageId, emoji, options = {}) {
2138
- const params = new URLSearchParams;
2139
- if (options.limit !== undefined)
2140
- params.set("limit", String(options.limit));
2141
- if (options.after)
2142
- params.set("after", options.after);
2143
- const suffix = params.toString();
2144
- const data = await this.#rest.get(`${Routes.messageReactions(channelId, messageId, emoji)}${suffix ? `?${suffix}` : ""}`);
2145
- return data.map((user) => new User(user));
3452
+ }
3453
+
3454
+ // packages/ws/src/decoder.ts
3455
+ import {
3456
+ createInflate,
3457
+ constants as zlibConstants
3458
+ } from "zlib";
3459
+ async function toBytes(data) {
3460
+ if (data instanceof Uint8Array)
3461
+ return data;
3462
+ if (data instanceof ArrayBuffer)
3463
+ return new Uint8Array(data);
3464
+ if (typeof Blob !== "undefined" && data instanceof Blob)
3465
+ return new Uint8Array(await data.arrayBuffer());
3466
+ throw new TypeError("Gateway returned an unsupported compressed frame type.");
3467
+ }
3468
+
3469
+ class PlainTextDecoder {
3470
+ push(data) {
3471
+ return [String(data)];
2146
3472
  }
2147
- async removeOwnReaction(channelId, messageId, emoji) {
2148
- await this.#rest.delete(`${Routes.messageReactions(channelId, messageId, emoji)}/@me`);
3473
+ reset() {}
3474
+ }
3475
+
3476
+ class ZlibStreamDecoder {
3477
+ #create;
3478
+ #inflate;
3479
+ #chunks = [];
3480
+ #failure;
3481
+ #pendingReject;
3482
+ constructor(create = () => createInflate()) {
3483
+ this.#create = create;
3484
+ }
3485
+ push(data) {
3486
+ if (typeof data === "string")
3487
+ return [data];
3488
+ return this.#inflateFrame(data);
3489
+ }
3490
+ async#inflateFrame(data) {
3491
+ const chunk = await toBytes(data);
3492
+ const inflate = this.#inflate ??= this.#open();
3493
+ if (this.#failure)
3494
+ throw this.#failure;
3495
+ const complete = chunk.length >= 4 && chunk[chunk.length - 4] === 0 && chunk[chunk.length - 3] === 0 && chunk[chunk.length - 2] === 255 && chunk[chunk.length - 1] === 255;
3496
+ await this.#awaiting((settle) => inflate.write(chunk, (error) => error ? settle(error) : settle()));
3497
+ if (!complete)
3498
+ return [];
3499
+ await this.#awaiting((settle) => inflate.flush(zlibConstants.Z_SYNC_FLUSH, () => settle()));
3500
+ const parts = this.#chunks;
3501
+ this.#chunks = [];
3502
+ if (parts.length === 0)
3503
+ return [];
3504
+ const total = parts.reduce((size, part) => size + part.length, 0);
3505
+ const merged = new Uint8Array(total);
3506
+ let offset = 0;
3507
+ for (const part of parts) {
3508
+ merged.set(part, offset);
3509
+ offset += part.length;
3510
+ }
3511
+ return [new TextDecoder().decode(merged)];
3512
+ }
3513
+ reset() {
3514
+ const inflate = this.#inflate;
3515
+ this.#inflate = undefined;
3516
+ this.#chunks = [];
3517
+ this.#failure = undefined;
3518
+ this.#pendingReject = undefined;
3519
+ if (!inflate)
3520
+ return;
3521
+ inflate.removeAllListeners();
3522
+ try {
3523
+ inflate.close();
3524
+ } catch {}
3525
+ }
3526
+ #awaiting(run) {
3527
+ if (this.#failure)
3528
+ return Promise.reject(this.#failure);
3529
+ return new Promise((resolve, reject) => {
3530
+ this.#pendingReject = reject;
3531
+ run((error) => {
3532
+ this.#pendingReject = undefined;
3533
+ if (error)
3534
+ reject(error);
3535
+ else if (this.#failure)
3536
+ reject(this.#failure);
3537
+ else
3538
+ resolve();
3539
+ });
3540
+ });
2149
3541
  }
2150
- async removeReaction(channelId, messageId, emoji, userId) {
2151
- await this.#rest.delete(`${Routes.messageReactions(channelId, messageId, emoji)}/${userId}`);
3542
+ #open() {
3543
+ const inflate = this.#create();
3544
+ inflate.on("data", (chunk) => this.#chunks.push(chunk));
3545
+ inflate.on("error", (error) => {
3546
+ this.#failure = error;
3547
+ const reject = this.#pendingReject;
3548
+ this.#pendingReject = undefined;
3549
+ reject?.(error);
3550
+ });
3551
+ return inflate;
2152
3552
  }
2153
- async removeAllReactions(channelId, messageId) {
2154
- await this.#rest.delete(Routes.messageReactionsAll(channelId, messageId));
3553
+ }
3554
+ function createDecoder(compress) {
3555
+ return compress ? new ZlibStreamDecoder : new PlainTextDecoder;
3556
+ }
3557
+
3558
+ // packages/ws/src/transport.ts
3559
+ var OPEN = 1;
3560
+
3561
+ class WebSocketTransport {
3562
+ #handlers;
3563
+ #decoder;
3564
+ #createSocket;
3565
+ #compress;
3566
+ #socket;
3567
+ #generation = 0;
3568
+ #queue = Promise.resolve();
3569
+ constructor(options) {
3570
+ this.#handlers = options.handlers;
3571
+ this.#compress = options.compress ?? false;
3572
+ this.#decoder = options.decoder ?? createDecoder(options.compress);
3573
+ this.#createSocket = options.createSocket ?? ((url) => new WebSocket(url));
3574
+ }
3575
+ get connected() {
3576
+ return this.#socket?.readyState === OPEN;
3577
+ }
3578
+ get live() {
3579
+ return this.#socket !== undefined && this.#socket.readyState <= OPEN;
3580
+ }
3581
+ get generation() {
3582
+ return this.#generation;
3583
+ }
3584
+ connect(url) {
3585
+ this.#discard(1000, "Superseded by a new connection");
3586
+ const generation = ++this.#generation;
3587
+ this.#decoder.reset();
3588
+ this.#queue = Promise.resolve();
3589
+ let socket;
3590
+ try {
3591
+ socket = this.#createSocket(url);
3592
+ } catch (error) {
3593
+ return { ok: false, error: this.#asError(error) };
3594
+ }
3595
+ if (this.#compress) {
3596
+ try {
3597
+ socket.binaryType = "arraybuffer";
3598
+ } catch {}
3599
+ }
3600
+ this.#socket = socket;
3601
+ socket.addEventListener("open", () => {
3602
+ if (!this.#owns(generation))
3603
+ return;
3604
+ this.#handlers.onOpen();
3605
+ });
3606
+ socket.addEventListener("message", (event) => {
3607
+ if (!this.#owns(generation))
3608
+ return;
3609
+ let decoded;
3610
+ try {
3611
+ decoded = this.#decoder.push(event.data);
3612
+ } catch (error) {
3613
+ this.#handlers.onError(this.#asError(error));
3614
+ return;
3615
+ }
3616
+ if (!(decoded instanceof Promise)) {
3617
+ for (const frame of decoded)
3618
+ this.#handlers.onFrame(frame);
3619
+ return;
3620
+ }
3621
+ this.#queue = this.#queue.then(async () => {
3622
+ const frames = await decoded;
3623
+ if (!this.#owns(generation))
3624
+ return;
3625
+ for (const frame of frames)
3626
+ this.#handlers.onFrame(frame);
3627
+ }).catch((error) => {
3628
+ if (!this.#owns(generation))
3629
+ return;
3630
+ this.#handlers.onError(this.#asError(error));
3631
+ });
3632
+ });
3633
+ socket.addEventListener("close", (event) => {
3634
+ if (!this.#owns(generation))
3635
+ return;
3636
+ this.#socket = undefined;
3637
+ this.#generation++;
3638
+ this.#decoder.reset();
3639
+ this.#handlers.onClose(event.code, event.reason ?? "");
3640
+ });
3641
+ socket.addEventListener("error", () => {
3642
+ if (!this.#owns(generation))
3643
+ return;
3644
+ this.#handlers.onError(new Error("Gateway WebSocket error"));
3645
+ });
3646
+ return { ok: true };
2155
3647
  }
2156
- async fetchPinnedMessages(channelId) {
2157
- const data = await this.#rest.get(Routes.channelPins(channelId));
2158
- return data.map((item) => this.messages(channelId).upsert(item));
3648
+ send(data) {
3649
+ if (!this.connected)
3650
+ return false;
3651
+ try {
3652
+ this.#socket.send(data);
3653
+ return true;
3654
+ } catch (error) {
3655
+ this.#handlers.onError(this.#asError(error));
3656
+ return false;
3657
+ }
2159
3658
  }
2160
- async pinMessage(channelId, messageId) {
2161
- await this.#rest.put(Routes.channelPin(channelId, messageId));
3659
+ close(code = 1000, reason = "") {
3660
+ const socket = this.#socket;
3661
+ if (!socket)
3662
+ return;
3663
+ try {
3664
+ socket.close(code, reason);
3665
+ } catch (error) {
3666
+ this.#handlers.onError(this.#asError(error));
3667
+ }
2162
3668
  }
2163
- async unpinMessage(channelId, messageId) {
2164
- await this.#rest.delete(Routes.channelPin(channelId, messageId));
3669
+ destroy(code = 1000, reason = "") {
3670
+ this.#discard(code, reason);
3671
+ this.#decoder.reset();
2165
3672
  }
2166
- createThreadFromMessage(channelId, messageId, options) {
2167
- return this.threads(channelId).createFromMessage(messageId, options);
3673
+ #owns(generation) {
3674
+ return generation === this.#generation;
2168
3675
  }
2169
- delete(channelId) {
2170
- this.#messageManagers.delete(channelId);
2171
- return super.delete(channelId);
3676
+ #discard(code, reason) {
3677
+ const socket = this.#socket;
3678
+ if (!socket)
3679
+ return;
3680
+ this.#generation++;
3681
+ this.#socket = undefined;
3682
+ try {
3683
+ socket.close(code, reason);
3684
+ } catch (error) {
3685
+ this.#handlers.onError(this.#asError(error));
3686
+ }
2172
3687
  }
2173
- clear() {
2174
- this.#messageManagers.clear();
2175
- super.clear();
3688
+ #asError(error) {
3689
+ return error instanceof Error ? error : new Error(String(error));
2176
3690
  }
2177
3691
  }
2178
- function isMessageQuery(value) {
2179
- return typeof value === "object" && value !== null && !(Symbol.iterator in value);
2180
- }
2181
3692
 
2182
- // packages/ws/src/index.ts
3693
+ // packages/ws/src/opcodes.ts
2183
3694
  var GatewayOpcodes = {
2184
3695
  Dispatch: 0,
2185
3696
  Heartbeat: 1,
@@ -2193,6 +3704,131 @@ var GatewayOpcodes = {
2193
3704
  Hello: 10,
2194
3705
  HeartbeatAck: 11
2195
3706
  };
3707
+
3708
+ // packages/ws/src/protocol.ts
3709
+ function classifyFrame(raw) {
3710
+ let payload;
3711
+ try {
3712
+ payload = JSON.parse(raw);
3713
+ } catch (error) {
3714
+ return {
3715
+ sequence: null,
3716
+ action: {
3717
+ type: "invalid",
3718
+ violation: "invalid-json",
3719
+ message: "Gateway returned invalid JSON",
3720
+ cause: error
3721
+ }
3722
+ };
3723
+ }
3724
+ return classifyPayload(payload);
3725
+ }
3726
+ function classifyPayload(payload) {
3727
+ if (payload === null || typeof payload !== "object" || typeof payload.op !== "number")
3728
+ return {
3729
+ sequence: null,
3730
+ action: {
3731
+ type: "invalid",
3732
+ violation: "invalid-payload",
3733
+ message: "Gateway returned an invalid payload"
3734
+ }
3735
+ };
3736
+ const frame = payload;
3737
+ const sequence = typeof frame.s === "number" ? frame.s : null;
3738
+ switch (frame.op) {
3739
+ case GatewayOpcodes.Dispatch:
3740
+ return {
3741
+ sequence,
3742
+ action: {
3743
+ type: "dispatch",
3744
+ event: typeof frame.t === "string" ? frame.t : null,
3745
+ data: frame.d
3746
+ }
3747
+ };
3748
+ case GatewayOpcodes.Hello: {
3749
+ const interval = frame.d?.heartbeat_interval;
3750
+ if (typeof interval !== "number" || !Number.isFinite(interval) || interval <= 0)
3751
+ return {
3752
+ sequence,
3753
+ action: {
3754
+ type: "invalid",
3755
+ violation: "invalid-hello",
3756
+ message: "Gateway HELLO payload contains an invalid heartbeat interval"
3757
+ }
3758
+ };
3759
+ return {
3760
+ sequence,
3761
+ action: { type: "hello", heartbeatInterval: interval }
3762
+ };
3763
+ }
3764
+ case GatewayOpcodes.Heartbeat:
3765
+ return { sequence, action: { type: "heartbeat" } };
3766
+ case GatewayOpcodes.HeartbeatAck:
3767
+ return {
3768
+ sequence,
3769
+ action: { type: "heartbeat-ack", data: frame.d }
3770
+ };
3771
+ case GatewayOpcodes.Reconnect:
3772
+ return { sequence, action: { type: "reconnect" } };
3773
+ case GatewayOpcodes.InvalidSession:
3774
+ return {
3775
+ sequence,
3776
+ action: {
3777
+ type: "invalid-session",
3778
+ resumable: frame.d === true
3779
+ }
3780
+ };
3781
+ default:
3782
+ return { sequence, action: { type: "unknown", opcode: frame.op } };
3783
+ }
3784
+ }
3785
+ function identifyPayload(options) {
3786
+ const props = options.properties ?? {};
3787
+ const os = props.os ?? "Android";
3788
+ const browser = props.browser ?? "Discord Android";
3789
+ const device = props.device ?? "Discord Android";
3790
+ return {
3791
+ op: GatewayOpcodes.Identify,
3792
+ d: {
3793
+ token: options.token,
3794
+ intents: options.intents,
3795
+ properties: {
3796
+ os,
3797
+ browser,
3798
+ device,
3799
+ $os: os,
3800
+ $browser: browser,
3801
+ $device: device,
3802
+ ...props
3803
+ },
3804
+ presence: {
3805
+ since: options.presence?.since ?? null,
3806
+ activities: options.presence?.activities ?? [],
3807
+ status: options.presence?.status ?? "online",
3808
+ afk: Boolean(options.presence?.afk)
3809
+ },
3810
+ shard: [options.shardId, options.shardCount]
3811
+ },
3812
+ s: null,
3813
+ t: null
3814
+ };
3815
+ }
3816
+ function resumePayload(token, resume) {
3817
+ return {
3818
+ op: GatewayOpcodes.Resume,
3819
+ d: {
3820
+ token,
3821
+ session_id: resume.sessionId,
3822
+ seq: resume.sequence
3823
+ },
3824
+ s: null,
3825
+ t: null
3826
+ };
3827
+ }
3828
+ function heartbeatPayload(sequence) {
3829
+ return { op: GatewayOpcodes.Heartbeat, d: sequence, s: null, t: null };
3830
+ }
3831
+ // packages/ws/src/state.ts
2196
3832
  class GatewayError extends Error {
2197
3833
  code;
2198
3834
  constructor(message, code, options) {
@@ -2201,31 +3837,43 @@ class GatewayError extends Error {
2201
3837
  this.code = code;
2202
3838
  }
2203
3839
  }
3840
+ // packages/ws/src/send-budget.ts
3841
+ class SendBudget {
3842
+ #timestamps = [];
3843
+ #limit;
3844
+ #windowMs;
3845
+ constructor(limit = 115, windowMs = 60000) {
3846
+ this.#limit = limit;
3847
+ this.#windowMs = windowMs;
3848
+ }
3849
+ get used() {
3850
+ return this.#timestamps.length;
3851
+ }
3852
+ allows(privileged, now = Date.now()) {
3853
+ while (this.#timestamps.length && now - this.#timestamps[0] >= this.#windowMs)
3854
+ this.#timestamps.shift();
3855
+ return privileged || this.#timestamps.length < this.#limit;
3856
+ }
3857
+ record(now = Date.now()) {
3858
+ this.#timestamps.push(now);
3859
+ }
3860
+ }
3861
+
3862
+ // packages/ws/src/index.ts
3863
+ var DEFAULT_GATEWAY_URL = "wss://gateway.discord.gg/?v=10&encoding=json";
2204
3864
 
2205
3865
  class Gateway {
2206
3866
  state = "CONNECT" /* Connect */;
2207
3867
  #options;
2208
- #ws;
2209
- #sequence = null;
2210
- #sessionId;
2211
- #resumeURL;
2212
- #heartbeatTimer;
2213
- #initialHeartbeat;
2214
- #heartbeatAckTimer;
2215
- #heartbeatACK = true;
2216
- #heartbeatInterval = 0;
2217
- #heartbeatSentAt = 0;
2218
- #lastMessageAt = 0;
2219
- #zombieTimer;
2220
- #zombieReported = false;
3868
+ #transport;
3869
+ #session = new GatewaySession;
3870
+ #token = 0;
3871
+ #heartbeat;
2221
3872
  #closed = false;
2222
- #attempt = 0;
2223
- #reconnectTimer;
2224
- #connectPromise;
2225
- #resolveConnect;
2226
- #rejectConnect;
3873
+ #reconnect;
2227
3874
  #listeners = new Map;
2228
- #sendTimestamps = [];
3875
+ #sendBudget = new SendBudget;
3876
+ ping = -1;
2229
3877
  constructor(options) {
2230
3878
  if (!options.token?.trim())
2231
3879
  throw new TypeError("A Gateway token is required.");
@@ -2246,39 +3894,78 @@ class Gateway {
2246
3894
  browser: "Discord Android",
2247
3895
  device: "Discord Android"
2248
3896
  },
2249
- presence: { status: "online", activities: [], afk: false, since: null },
2250
- ...options
3897
+ presence: {
3898
+ status: "online",
3899
+ activities: [],
3900
+ afk: false,
3901
+ since: null
3902
+ },
3903
+ ...options,
3904
+ intents: resolvedIntents
2251
3905
  };
2252
3906
  if (this.#options.shardId < 0 || this.#options.shardId >= this.#options.shardCount)
2253
3907
  throw new RangeError("Gateway shardId must be within the configured shard count.");
2254
3908
  if (this.#options.zombieTimeout <= this.#options.heartbeatAckTimeout)
2255
3909
  throw new RangeError("Gateway zombieTimeout must be greater than heartbeatAckTimeout");
3910
+ this.#transport = new WebSocketTransport({
3911
+ compress: this.#options.compress,
3912
+ createSocket: options.createSocket,
3913
+ handlers: {
3914
+ onOpen: () => this.#onOpen(),
3915
+ onFrame: (raw) => this.#message(raw, this.#token),
3916
+ onClose: (code) => this.#close(code, this.#token),
3917
+ onError: (error) => this.#emitError(error instanceof GatewayError ? error : new GatewayError(error.message, undefined, {
3918
+ cause: error
3919
+ }))
3920
+ }
3921
+ });
3922
+ this.#reconnect = new GatewayReconnect({
3923
+ enabled: this.#options.reconnect,
3924
+ maxAttempts: this.#options.maxReconnectAttempts,
3925
+ baseDelay: this.#options.reconnectBaseDelay,
3926
+ maxDelay: this.#options.reconnectMaxDelay
3927
+ });
3928
+ this.#heartbeat = new GatewayHeartbeat({
3929
+ ackTimeout: this.#options.heartbeatAckTimeout,
3930
+ zombieTimeout: this.#options.zombieTimeout,
3931
+ send: (sequence) => this.#dispatch(heartbeatPayload(sequence), true),
3932
+ sequence: () => this.#session.sequence,
3933
+ isConnected: () => !this.#closed && this.#transport.connected,
3934
+ onTimeout: (timeout) => this.#onHeartbeatTimeout(timeout),
3935
+ onError: (error) => this.#emitError(error)
3936
+ });
3937
+ }
3938
+ #onHeartbeatTimeout(timeout) {
3939
+ if (timeout.type === "zombie") {
3940
+ this.#emit("zombie", {
3941
+ silentFor: timeout.silentFor,
3942
+ timeout: timeout.deadline
3943
+ });
3944
+ this.#emitError(new GatewayError(`Gateway connection appears stale after ${timeout.silentFor}ms without traffic.`));
3945
+ } else {
3946
+ this.#emitError(new GatewayError(`Gateway heartbeat acknowledgement timed out after ${timeout.elapsedMs}ms.`));
3947
+ }
3948
+ this.#transport.close(1001, timeout.type === "zombie" ? "Zombie Gateway connection" : "Heartbeat timeout");
2256
3949
  }
2257
- connect(url = "wss://gateway.discord.gg/?v=10&encoding=json") {
3950
+ connect(url = DEFAULT_GATEWAY_URL) {
2258
3951
  if (this.state === "CLOSED" /* Closed */)
2259
3952
  throw new GatewayError("Gateway has been permanently closed.");
2260
- if (this.#connectPromise)
2261
- return this.#connectPromise;
3953
+ const inFlight = this.#reconnect.attemptPromise;
3954
+ if (inFlight)
3955
+ return inFlight;
3956
+ if (this.#transport.live)
3957
+ return Promise.resolve();
3958
+ this.#reconnect.cancel();
2262
3959
  this.#closed = false;
2263
3960
  this.#setState("CONNECT" /* Connect */);
2264
- this.#connectPromise = new Promise((resolve, reject) => {
2265
- this.#resolveConnect = resolve;
2266
- this.#rejectConnect = reject;
2267
- this.#open(url);
2268
- });
2269
- return this.#connectPromise;
3961
+ const connectURL = this.#options.compress ? url.includes("compress=") ? url : `${url}&compress=zlib-stream` : url;
3962
+ return this.#reconnect.attempt(() => this.#open(connectURL));
2270
3963
  }
2271
3964
  close() {
2272
3965
  this.#closed = true;
2273
3966
  this.#clearTimers();
2274
3967
  this.#settleConnect(new GatewayError("Gateway connection closed before socket open."));
2275
- const ws = this.#ws;
2276
- this.#ws = undefined;
2277
- try {
2278
- ws?.close(1000, "Client closed connection");
2279
- } catch (error) {
2280
- this.#emitError(error);
2281
- }
3968
+ this.#transport.destroy(1000, "Client closed connection");
2282
3969
  this.#setState("CLOSED" /* Closed */);
2283
3970
  }
2284
3971
  emit(event, data) {
@@ -2300,23 +3987,20 @@ class Gateway {
2300
3987
  send(payload) {
2301
3988
  if (!payload || !Number.isInteger(payload.op))
2302
3989
  throw new TypeError("Gateway payload must contain an integer opcode.");
2303
- if (this.#ws?.readyState !== WebSocket.OPEN)
3990
+ return this.#dispatch(payload, false);
3991
+ }
3992
+ #dispatch(payload, privileged) {
3993
+ if (!this.#transport.connected)
2304
3994
  return false;
2305
3995
  const now = Date.now();
2306
- while (this.#sendTimestamps.length && now - this.#sendTimestamps[0] >= 60000)
2307
- this.#sendTimestamps.shift();
2308
- if (this.#sendTimestamps.length >= 115) {
3996
+ if (!this.#sendBudget.allows(privileged, now)) {
2309
3997
  this.#emitError(new GatewayError("Gateway send rate budget exhausted."));
2310
3998
  return false;
2311
3999
  }
2312
- try {
2313
- this.#ws.send(JSON.stringify(payload));
2314
- this.#sendTimestamps.push(now);
2315
- return true;
2316
- } catch (error) {
2317
- this.#emitError(error);
4000
+ if (!this.#transport.send(JSON.stringify(payload)))
2318
4001
  return false;
2319
- }
4002
+ this.#sendBudget.record(now);
4003
+ return true;
2320
4004
  }
2321
4005
  setPresence(data) {
2322
4006
  this.#options.presence = { ...this.#options.presence, ...data };
@@ -2352,289 +4036,142 @@ class Gateway {
2352
4036
  #open(url) {
2353
4037
  if (this.#closed)
2354
4038
  return;
2355
- let ws;
2356
- try {
2357
- ws = new WebSocket(url);
2358
- } catch (error) {
2359
- const failure = this.#normalizeError(error);
2360
- this.#emitError(failure);
2361
- if (this.#options.reconnect) {
2362
- this.#setState("RECONNECT" /* Reconnect */);
2363
- this.#scheduleReconnect();
2364
- } else
2365
- this.#settleConnect(failure);
4039
+ this.#token = this.#session.beginConnection();
4040
+ const result = this.#transport.connect(url);
4041
+ if (result.ok)
2366
4042
  return;
4043
+ const failure = this.#normalizeError(result.error);
4044
+ this.#emitError(failure);
4045
+ if (this.#options.reconnect) {
4046
+ this.#setState("RECONNECT" /* Reconnect */);
4047
+ this.#scheduleReconnect();
4048
+ } else {
4049
+ this.#settleConnect(failure);
2367
4050
  }
2368
- this.#ws = ws;
2369
- ws.addEventListener("open", () => {
2370
- this.#lastMessageAt = Date.now();
2371
- this.#zombieReported = false;
2372
- this.#startZombieDetection();
2373
- this.#emit("open", undefined);
2374
- this.#settleConnect();
2375
- });
2376
- ws.addEventListener("message", (event) => {
2377
- this.#lastMessageAt = Date.now();
2378
- this.#zombieReported = false;
2379
- this.#message(ws, String(event.data));
2380
- });
2381
- ws.addEventListener("close", (event) => this.#close(ws, event.code));
2382
- ws.addEventListener("error", () => this.#emitError(new GatewayError("Gateway WebSocket error")));
2383
- }
2384
- #startZombieDetection() {
2385
- if (this.#zombieTimer)
2386
- clearInterval(this.#zombieTimer);
2387
- const interval = Math.max(1, Math.min(250, this.#options.heartbeatAckTimeout, this.#options.zombieTimeout / 2));
2388
- this.#zombieTimer = setInterval(() => {
2389
- if (this.#closed || this.#ws?.readyState !== WebSocket.OPEN || this.#lastMessageAt === 0)
2390
- return;
2391
- const silentFor = Date.now() - this.#lastMessageAt;
2392
- const deadline = Math.max(this.#options.zombieTimeout, this.#heartbeatInterval + this.#options.heartbeatAckTimeout);
2393
- if (silentFor < deadline || this.#zombieReported)
2394
- return;
2395
- this.#zombieReported = true;
2396
- const error = new GatewayError(`Gateway connection appears stale after ${silentFor}ms without traffic.`);
2397
- this.#emit("zombie", { silentFor, timeout: deadline });
2398
- this.#emitError(error);
2399
- try {
2400
- this.#ws.close(1001, "Zombie Gateway connection");
2401
- } catch (closeError) {
2402
- this.#emitError(closeError);
2403
- }
2404
- }, interval);
2405
4051
  }
2406
- #message(ws, raw) {
2407
- let payload;
2408
- try {
2409
- payload = JSON.parse(raw);
2410
- } catch (error) {
2411
- this.#emitError(new GatewayError("Gateway returned invalid JSON", undefined, {
2412
- cause: error
2413
- }));
2414
- ws.close(1002, "Invalid JSON");
2415
- return;
2416
- }
2417
- if (!payload || typeof payload.op !== "number") {
2418
- this.#emitError(new GatewayError("Gateway returned an invalid payload"));
2419
- ws.close(1002, "Invalid payload");
4052
+ #onOpen() {
4053
+ this.#heartbeat.watch();
4054
+ this.#emit("open", undefined);
4055
+ this.#settleConnect();
4056
+ }
4057
+ #message(raw, token) {
4058
+ if (!this.#session.owns(token))
2420
4059
  return;
2421
- }
2422
- if (typeof payload.s === "number")
2423
- this.#sequence = payload.s;
2424
- switch (payload.op) {
2425
- case GatewayOpcodes.Dispatch:
4060
+ const { sequence, action } = classifyFrame(raw);
4061
+ if (sequence !== null)
4062
+ this.#session.recordSequence(sequence, token);
4063
+ this.#apply(action, token);
4064
+ }
4065
+ #apply(action, token) {
4066
+ switch (action.type) {
4067
+ case "dispatch":
2426
4068
  this.#setState("DISPATCH" /* Dispatch */);
2427
- this.#handleDispatch(payload.t, payload.d);
2428
- break;
2429
- case GatewayOpcodes.Hello:
2430
- this.#handleHello(payload.d);
2431
- break;
2432
- case GatewayOpcodes.Heartbeat:
4069
+ this.#handleDispatch(action.event, action.data, token);
4070
+ return;
4071
+ case "hello":
4072
+ this.#handleHello(action.heartbeatInterval);
4073
+ return;
4074
+ case "heartbeat":
2433
4075
  this.#setState("HEARTBEAT" /* Heartbeat */);
2434
- this.#sendHeartbeat();
2435
- break;
2436
- case GatewayOpcodes.HeartbeatAck:
2437
- this.#heartbeatACK = true;
2438
- this.#clearHeartbeatAckTimer();
2439
- this.#emit("heartbeatAck", payload.d);
2440
- break;
2441
- case GatewayOpcodes.Reconnect:
2442
- ws.close(1001, "Server requested reconnect");
2443
- break;
2444
- case GatewayOpcodes.InvalidSession:
2445
- this.#sessionId = undefined;
2446
- this.#sequence = null;
2447
- this.#emit("invalidSession", payload.d);
2448
- ws.close(1000, "Invalid session");
2449
- break;
4076
+ this.#heartbeat.sendHeartbeat();
4077
+ return;
4078
+ case "heartbeat-ack":
4079
+ this.#heartbeat.acknowledge();
4080
+ this.ping = this.#heartbeat.latency;
4081
+ this.#emit("heartbeatAck", action.data);
4082
+ return;
4083
+ case "reconnect":
4084
+ this.#transport.close(1001, "Server requested reconnect");
4085
+ return;
4086
+ case "invalid-session": {
4087
+ if (!action.resumable)
4088
+ this.#session.invalidate(token, "invalid-session");
4089
+ this.#emit("invalidSession", action.resumable);
4090
+ this.#transport.close(action.resumable ? GatewayCloseCodes.UnknownError : 1000, action.resumable ? "Invalid session (resumable)" : "Invalid session");
4091
+ return;
4092
+ }
4093
+ case "invalid":
4094
+ this.#emitError(new GatewayError(action.message, undefined, action.cause === undefined ? undefined : { cause: action.cause }));
4095
+ this.#transport.close(1002, action.violation === "invalid-json" ? "Invalid JSON" : action.violation === "invalid-hello" ? "Invalid heartbeat interval" : "Invalid payload");
4096
+ return;
4097
+ case "unknown":
4098
+ return;
2450
4099
  }
2451
4100
  }
2452
- #handleDispatch(event, data) {
4101
+ #handleDispatch(event, data, token) {
2453
4102
  if (event === "READY") {
2454
- const ready = data;
2455
- if (typeof ready?.session_id !== "string" || typeof ready?.resume_gateway_url !== "string") {
2456
- this.#emitError(new GatewayError("Gateway READY payload is missing session information"));
4103
+ if (!this.#session.activate(data, token)) {
4104
+ if (this.#session.owns(token))
4105
+ this.#emitError(new GatewayError("Gateway READY payload is missing session information"));
2457
4106
  return;
2458
4107
  }
2459
- this.#sessionId = ready.session_id;
2460
- this.#resumeURL = ready.resume_gateway_url;
2461
- this.#attempt = 0;
4108
+ this.#reconnect.reset();
2462
4109
  this.#setState("READY" /* Ready */);
2463
4110
  this.#emit("ready", data);
4111
+ } else if (event === "RESUMED") {
4112
+ this.#reconnect.reset();
4113
+ this.#setState("READY" /* Ready */);
4114
+ this.#emit("resumed", data);
2464
4115
  }
2465
4116
  this.#emit("RAW", { event: event ?? "UNKNOWN", data });
2466
4117
  if (this.#closed)
2467
4118
  return;
2468
4119
  this.#emit(event ?? "dispatch", data);
2469
4120
  }
2470
- #handleHello(data) {
2471
- const interval = data?.heartbeat_interval;
2472
- if (typeof interval !== "number" || !Number.isFinite(interval) || interval <= 0) {
2473
- this.#emitError(new GatewayError("Gateway HELLO payload contains an invalid heartbeat interval"));
2474
- this.#ws?.close(1002, "Invalid heartbeat interval");
2475
- return;
2476
- }
2477
- this.#heartbeatInterval = interval;
2478
- this.#setState(this.#sessionId && this.#sequence !== null && this.#resumeURL ? "RESUME" /* Resume */ : "HELLO" /* Hello */);
2479
- this.#startHeartbeat(interval);
4121
+ #handleHello(interval) {
4122
+ this.#setState(this.#session.canResume ? "RESUME" /* Resume */ : "HELLO" /* Hello */);
4123
+ this.#heartbeat.start(interval);
2480
4124
  this.#identifyOrResume();
2481
4125
  }
2482
4126
  #identifyOrResume() {
2483
- if (this.#sessionId && this.#sequence !== null && this.#resumeURL) {
4127
+ const intent = this.#session.handshake();
4128
+ if (intent.type === "resume") {
2484
4129
  this.#setState("RESUME" /* Resume */);
2485
- this.send({
2486
- op: GatewayOpcodes.Resume,
2487
- d: {
2488
- token: this.#options.token,
2489
- session_id: this.#sessionId,
2490
- seq: this.#sequence
2491
- },
2492
- s: null,
2493
- t: null
2494
- });
4130
+ this.#dispatch(resumePayload(this.#options.token, intent), true);
2495
4131
  return;
2496
4132
  }
2497
4133
  this.#setState("IDENTIFY" /* Identify */);
2498
- const props = this.#options.properties ?? {
2499
- os: "Android",
2500
- browser: "Discord Android",
2501
- device: "Discord Android"
2502
- };
2503
- const os = props.os ?? "Android";
2504
- const browser = props.browser ?? "Discord Android";
2505
- const device = props.device ?? "Discord Android";
2506
- this.send({
2507
- op: GatewayOpcodes.Identify,
2508
- d: {
2509
- token: this.#options.token,
2510
- intents: this.#options.intents,
2511
- properties: {
2512
- os,
2513
- browser,
2514
- device,
2515
- $os: os,
2516
- $browser: browser,
2517
- $device: device,
2518
- ...props
2519
- },
2520
- presence: {
2521
- since: this.#options.presence?.since ?? null,
2522
- activities: this.#options.presence?.activities ?? [],
2523
- status: this.#options.presence?.status ?? "online",
2524
- afk: Boolean(this.#options.presence?.afk)
2525
- },
2526
- shard: [this.#options.shardId, this.#options.shardCount]
2527
- },
2528
- s: null,
2529
- t: null
2530
- });
2531
- }
2532
- #startHeartbeat(interval) {
2533
- this.#clearHeartbeatTimers();
2534
- this.#heartbeatACK = true;
2535
- this.#initialHeartbeat = setTimeout(() => this.#sendHeartbeat(), Math.random() * interval);
2536
- this.#heartbeatTimer = setInterval(() => this.#sendHeartbeat(), interval);
2537
- }
2538
- #sendHeartbeat() {
2539
- this.#heartbeatACK = false;
2540
- this.#heartbeatSentAt = Date.now();
2541
- if (!this.send({
2542
- op: GatewayOpcodes.Heartbeat,
2543
- d: this.#sequence,
2544
- s: null,
2545
- t: null
2546
- })) {
2547
- this.#emitError(new GatewayError("Unable to send Gateway heartbeat because the WebSocket is not open."));
2548
- return;
2549
- }
2550
- this.#clearHeartbeatAckTimer();
2551
- this.#heartbeatAckTimer = setTimeout(() => {
2552
- if (!this.#heartbeatACK) {
2553
- const elapsed = Date.now() - this.#heartbeatSentAt;
2554
- this.#emitError(new GatewayError(`Gateway heartbeat acknowledgement timed out after ${elapsed}ms.`));
2555
- try {
2556
- this.#ws?.close(1001, "Heartbeat timeout");
2557
- } catch (error) {
2558
- this.#emitError(error);
2559
- }
2560
- }
2561
- }, this.#options.heartbeatAckTimeout);
2562
- }
2563
- #close(ws, code) {
2564
- if (this.#ws !== ws)
2565
- return;
2566
- this.#ws = undefined;
4134
+ this.#dispatch(identifyPayload({
4135
+ token: this.#options.token,
4136
+ intents: this.#options.intents,
4137
+ shardId: this.#options.shardId,
4138
+ shardCount: this.#options.shardCount,
4139
+ properties: this.#options.properties,
4140
+ presence: this.#options.presence
4141
+ }), true);
4142
+ }
4143
+ #close(code, token) {
2567
4144
  this.#clearTimers();
2568
- const action = this.#closeAction(code);
4145
+ const action = this.#reconnect.classifyClose(code, this.#session.canResume);
2569
4146
  this.#emit("close", { code, action });
2570
- if (this.#closed || !this.#options.reconnect || action === "stop") {
4147
+ if (this.#closed || !this.#reconnect.enabled || action === "stop") {
4148
+ if (action === "stop")
4149
+ this.#closed = true;
2571
4150
  this.#setState(this.#closed ? "CLOSED" /* Closed */ : "CONNECT" /* Connect */);
2572
- if (this.#connectPromise)
2573
- this.#settleConnect(new GatewayError(`Gateway closed before READY (code ${code}).`, code));
4151
+ if (this.#reconnect.connecting)
4152
+ this.#reconnect.settle(new GatewayError(`Gateway closed before READY (code ${code}).`, code));
2574
4153
  return;
2575
4154
  }
2576
- if (action === "identify") {
2577
- this.#sequence = null;
2578
- this.#sessionId = undefined;
2579
- }
4155
+ if (action === "identify")
4156
+ this.#session.invalidate(token, "session-timeout");
2580
4157
  this.#setState("RECONNECT" /* Reconnect */);
2581
4158
  this.#scheduleReconnect();
2582
4159
  }
2583
- #closeAction(code) {
2584
- if ([4004, 4010, 4011, 4012, 4013, 4014].includes(code))
2585
- return "stop";
2586
- if ([4007, 4009].includes(code))
2587
- return "identify";
2588
- return this.#sessionId && this.#sequence !== null ? "resume" : "identify";
2589
- }
2590
4160
  #scheduleReconnect() {
2591
- if (this.#closed || !this.#options.reconnect || this.#reconnectTimer)
4161
+ if (this.#closed)
2592
4162
  return;
2593
- if (this.#attempt >= this.#options.maxReconnectAttempts) {
2594
- this.#settleConnect(new GatewayError("Gateway reconnect attempts exhausted."));
2595
- this.#setState("CONNECT" /* Connect */);
4163
+ const result = this.#reconnect.schedule(() => this.#open(this.#session.connectURL(DEFAULT_GATEWAY_URL)));
4164
+ if (result.scheduled || result.reason !== "exhausted")
2596
4165
  return;
2597
- }
2598
- const delay = Math.min(this.#options.reconnectMaxDelay, this.#options.reconnectBaseDelay * 2 ** this.#attempt++);
2599
- const jitter = Math.random() * Math.min(250, Math.max(1, delay * 0.25));
2600
- this.#reconnectTimer = setTimeout(() => {
2601
- this.#reconnectTimer = undefined;
2602
- this.#open(this.#resumeURL ?? "wss://gateway.discord.gg/?v=10&encoding=json");
2603
- }, delay + jitter);
4166
+ this.#reconnect.settle(new GatewayError("Gateway reconnect attempts exhausted."));
4167
+ this.#setState("CONNECT" /* Connect */);
2604
4168
  }
2605
4169
  #settleConnect(error) {
2606
- const resolve = this.#resolveConnect;
2607
- const reject = this.#rejectConnect;
2608
- this.#resolveConnect = undefined;
2609
- this.#rejectConnect = undefined;
2610
- this.#connectPromise = undefined;
2611
- if (error)
2612
- reject?.(error);
2613
- else
2614
- resolve?.();
2615
- }
2616
- #clearHeartbeatAckTimer() {
2617
- if (this.#heartbeatAckTimer)
2618
- clearTimeout(this.#heartbeatAckTimer);
2619
- this.#heartbeatAckTimer = undefined;
2620
- }
2621
- #clearHeartbeatTimers() {
2622
- if (this.#heartbeatTimer)
2623
- clearInterval(this.#heartbeatTimer);
2624
- if (this.#initialHeartbeat)
2625
- clearTimeout(this.#initialHeartbeat);
2626
- this.#clearHeartbeatAckTimer();
2627
- this.#heartbeatTimer = undefined;
2628
- this.#initialHeartbeat = undefined;
4170
+ this.#reconnect.settle(error);
2629
4171
  }
2630
4172
  #clearTimers() {
2631
- if (this.#reconnectTimer)
2632
- clearTimeout(this.#reconnectTimer);
2633
- if (this.#zombieTimer)
2634
- clearInterval(this.#zombieTimer);
2635
- this.#reconnectTimer = undefined;
2636
- this.#zombieTimer = undefined;
2637
- this.#clearHeartbeatTimers();
4173
+ this.#reconnect.cancel();
4174
+ this.#heartbeat.stop();
2638
4175
  }
2639
4176
  #normalizeError(error) {
2640
4177
  return error instanceof GatewayError ? error : new GatewayError("Gateway connection failed.", undefined, {
@@ -2669,7 +4206,7 @@ class Gateway {
2669
4206
  }
2670
4207
 
2671
4208
  // packages/core/src/index.ts
2672
- class EventEmitter {
4209
+ class EventEmitter2 {
2673
4210
  #listeners = new Map;
2674
4211
  on(event, listener) {
2675
4212
  if (typeof listener !== "function")
@@ -2691,6 +4228,9 @@ class EventEmitter {
2691
4228
  this.#listeners.get(event)?.delete(listener);
2692
4229
  return this;
2693
4230
  }
4231
+ removeListener(event, listener) {
4232
+ return this.off(event, listener);
4233
+ }
2694
4234
  removeAllListeners(event) {
2695
4235
  if (event === undefined)
2696
4236
  this.#listeners.clear();
@@ -2727,12 +4267,13 @@ class EventEmitter {
2727
4267
  }
2728
4268
  }
2729
4269
 
2730
- class Client extends EventEmitter {
4270
+ class Client extends EventEmitter2 {
2731
4271
  options;
2732
4272
  rest;
2733
4273
  users;
2734
4274
  guilds;
2735
4275
  channels;
4276
+ stageInstances;
2736
4277
  application;
2737
4278
  get ws() {
2738
4279
  return this.#gateway;
@@ -2743,14 +4284,26 @@ class Client extends EventEmitter {
2743
4284
  user;
2744
4285
  readyAt;
2745
4286
  state = "idle";
4287
+ get token() {
4288
+ return this.state === "destroyed" ? null : this.options.token ?? null;
4289
+ }
2746
4290
  get uptime() {
2747
4291
  return this.readyAt ? Date.now() - this.readyAt.getTime() : null;
2748
4292
  }
4293
+ get ping() {
4294
+ return this.#gateway.ping;
4295
+ }
2749
4296
  isReady() {
2750
4297
  return this.state === "ready";
2751
4298
  }
2752
4299
  setPresence(data) {
2753
- return this.#gateway.setPresence?.(data) ?? false;
4300
+ return this.#gateway.setPresence(data);
4301
+ }
4302
+ setVoiceState(data) {
4303
+ return this.#gateway.setVoiceState(data);
4304
+ }
4305
+ requestGuildMembers(data) {
4306
+ return this.#gateway.requestGuildMembers(data);
2754
4307
  }
2755
4308
  #gateway;
2756
4309
  #resourceContext;
@@ -2763,14 +4316,15 @@ class Client extends EventEmitter {
2763
4316
  this.users = new UserManager(this.rest);
2764
4317
  this.guilds = new GuildManager(this.rest);
2765
4318
  this.channels = new ChannelManager(this.rest);
4319
+ this.stageInstances = new StageInstanceManager(this.rest);
2766
4320
  const placeholderAppCommands = new ApplicationCommandManager(this.rest, "0");
2767
4321
  this.application = { commands: placeholderAppCommands };
2768
4322
  this.#resourceContext = {
2769
- sendMessage: (channelId, options2) => this.channels.send(channelId, options2),
2770
- editMessage: (channelId, messageId, options2) => this.channels.editMessage(channelId, messageId, options2),
4323
+ sendMessage: (channelId, options) => this.channels.send(channelId, options),
4324
+ editMessage: (channelId, messageId, options) => this.channels.editMessage(channelId, messageId, options),
2771
4325
  deleteMessage: (channelId, messageId) => this.channels.deleteMessage(channelId, messageId),
2772
4326
  crosspostMessage: (channelId, messageId) => this.channels.crosspostMessage(channelId, messageId),
2773
- editChannel: (channelId, options2) => this.channels.edit(channelId, options2),
4327
+ editChannel: (channelId, options) => this.channels.edit(channelId, options),
2774
4328
  deleteChannel: (channelId) => this.channels.deleteChannel(channelId),
2775
4329
  addReaction: (channelId, messageId, emoji) => this.channels.addReaction(channelId, messageId, emoji),
2776
4330
  removeOwnReaction: (channelId, messageId, emoji) => this.channels.removeOwnReaction(channelId, messageId, emoji),
@@ -2794,6 +4348,13 @@ class Client extends EventEmitter {
2794
4348
  this.application.commands = new ApplicationCommandManager(this.rest, appId);
2795
4349
  this.emit("ready" /* Ready */, this.user);
2796
4350
  });
4351
+ this.#gateway.on("RESUMED", () => {
4352
+ this.state = "ready";
4353
+ this.emit("resumed" /* Resumed */);
4354
+ });
4355
+ this.#gateway.on("invalidSession", (isRecoverable) => {
4356
+ this.emit("invalidSession" /* InvalidSession */, isRecoverable);
4357
+ });
2797
4358
  this.#gateway.on("open", () => this.emit("open" /* Open */));
2798
4359
  this.#gateway.on("close", (data) => {
2799
4360
  if (this.state !== "destroyed")
@@ -2826,6 +4387,7 @@ class Client extends EventEmitter {
2826
4387
  this.emit("guildUnavailable" /* GuildUnavailable */, payload);
2827
4388
  return;
2828
4389
  }
4390
+ const wasCached = this.guilds.has(payload.id);
2829
4391
  const guild = new Guild(payload);
2830
4392
  this.guilds.set(guild.id, guild);
2831
4393
  for (const member of payload.members ?? [])
@@ -2834,8 +4396,8 @@ class Client extends EventEmitter {
2834
4396
  ...payload.channels ?? [],
2835
4397
  ...payload.threads ?? []
2836
4398
  ])
2837
- this.channels.set(channelData.id, new Channel(channelData, this.#resourceContext));
2838
- this.emit("guildCreate" /* GuildCreate */, payload);
4399
+ this.channels.set(channelData.id, createChannel(channelData, this.#resourceContext));
4400
+ this.emit(wasCached ? "guildAvailable" /* GuildAvailable */ : "guildCreate" /* GuildCreate */, payload);
2839
4401
  });
2840
4402
  this.#gateway.on("GUILD_UPDATE", (data) => {
2841
4403
  const guild = new Guild(data);
@@ -2881,12 +4443,12 @@ class Client extends EventEmitter {
2881
4443
  this.#gateway.on("AUTO_MODERATION_RULE_DELETE", (data) => this.emit("autoModerationRuleDelete" /* AutoModerationRuleDelete */, data));
2882
4444
  this.#gateway.on("AUTO_MODERATION_ACTION_EXECUTION", (data) => this.emit("autoModerationActionExecution" /* AutoModerationActionExecution */, data));
2883
4445
  this.#gateway.on("CHANNEL_CREATE", (data) => {
2884
- const channel = new Channel(data, this.#resourceContext);
4446
+ const channel = createChannel(data, this.#resourceContext);
2885
4447
  this.channels.update(channel);
2886
4448
  this.emit("channelCreate" /* ChannelCreate */, channel);
2887
4449
  });
2888
4450
  this.#gateway.on("CHANNEL_UPDATE", (data) => {
2889
- const channel = new Channel(data, this.#resourceContext);
4451
+ const channel = createChannel(data, this.#resourceContext);
2890
4452
  this.channels.update(channel);
2891
4453
  this.emit("channelUpdate" /* ChannelUpdate */, channel);
2892
4454
  });
@@ -2897,12 +4459,12 @@ class Client extends EventEmitter {
2897
4459
  });
2898
4460
  this.#gateway.on("CHANNEL_PINS_UPDATE", (data) => this.emit("channelPinsUpdate" /* ChannelPinsUpdate */, data));
2899
4461
  this.#gateway.on("THREAD_CREATE", (data) => {
2900
- const channel = new Channel(data, this.#resourceContext);
4462
+ const channel = createChannel(data, this.#resourceContext);
2901
4463
  this.channels.update(channel);
2902
4464
  this.emit("threadCreate" /* ThreadCreate */, channel);
2903
4465
  });
2904
4466
  this.#gateway.on("THREAD_UPDATE", (data) => {
2905
- const channel = new Channel(data, this.#resourceContext);
4467
+ const channel = createChannel(data, this.#resourceContext);
2906
4468
  this.channels.update(channel);
2907
4469
  this.emit("threadUpdate" /* ThreadUpdate */, channel);
2908
4470
  });
@@ -2964,13 +4526,57 @@ class Client extends EventEmitter {
2964
4526
  return Promise.reject(new Error("Client is unauthenticated."));
2965
4527
  return this.rest.post(`/webhooks/${this.user.id}/${token}`, data);
2966
4528
  }
4529
+ fetchWebhook(id, token) {
4530
+ return this.rest.get(token ? `/webhooks/${id}/${token}` : `/webhooks/${id}`);
4531
+ }
4532
+ fetchGuildPreview(guildId) {
4533
+ return this.rest.get(`/guilds/${guildId}/preview`);
4534
+ }
4535
+ fetchVoiceRegions() {
4536
+ return this.rest.get("/voice/regions");
4537
+ }
4538
+ fetchInvite(code, options) {
4539
+ const query = new URLSearchParams;
4540
+ if (options?.withCounts)
4541
+ query.set("with_counts", "true");
4542
+ if (options?.withExpiration)
4543
+ query.set("with_expiration", "true");
4544
+ if (options?.guildScheduledEventId)
4545
+ query.set("guild_scheduled_event_id", options.guildScheduledEventId);
4546
+ const qs = query.toString();
4547
+ return this.rest.get(`/invites/${encodeURIComponent(code)}${qs ? `?${qs}` : ""}`);
4548
+ }
4549
+ fetchSticker(id) {
4550
+ return this.rest.get(`/stickers/${id}`);
4551
+ }
4552
+ fetchPremiumStickerPacks() {
4553
+ return this.rest.get(`/sticker-packs`);
4554
+ }
4555
+ fetchGuildTemplate(code) {
4556
+ return this.rest.get(`/guilds/templates/${encodeURIComponent(code)}`);
4557
+ }
4558
+ generateInvite(options) {
4559
+ if (!this.user)
4560
+ throw new Error("Client must be ready to generate invite.");
4561
+ const params = new URLSearchParams({
4562
+ client_id: this.user.id,
4563
+ scope: (options?.scopes ?? ["bot"]).join(" ")
4564
+ });
4565
+ if (options?.permissions) {
4566
+ params.set("permissions", String(options.permissions));
4567
+ }
4568
+ return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
4569
+ }
2967
4570
  }
2968
4571
  export {
2969
4572
  Client,
2970
4573
  ClientEvent,
4574
+ Collector,
4575
+ ClientEvent as Events,
2971
4576
  GatewayIntentBits,
2972
4577
  IntentBits,
2973
4578
  Intents,
4579
+ IntentsBitField,
2974
4580
  Permission,
2975
4581
  PermissionFlagsBits,
2976
4582
  PermissionOverwriteType,
@@ -2980,4 +4586,4 @@ export {
2980
4586
  resolveGatewayIntents
2981
4587
  };
2982
4588
 
2983
- //# debugId=813519968046205864756E2164756E21
4589
+ //# debugId=B9E28396E97D857564756E2164756E21